diff --git a/.gitattributes b/.gitattributes
index cc576ac7960..3c21bde619a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -33,3 +33,6 @@
*.ico binary
*.mo binary
*.pdf binary
+*.woff binary
+*.ttf binary
+*.eot binary
diff --git a/.htaccess b/.htaccess
index f23dbaf6686..2ac5e0e7a89 100644
--- a/.htaccess
+++ b/.htaccess
@@ -1,5 +1,5 @@
Paginator->counter(array( - 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') )); ?>"; ?>
diff --git a/lib/Cake/Console/Templates/default/views/view.ctp b/lib/Cake/Console/Templates/default/views/view.ctp index fb8acfc30cb..3acfad8be7c 100644 --- a/lib/Cake/Console/Templates/default/views/view.ctp +++ b/lib/Cake/Console/Templates/default/views/view.ctp @@ -43,7 +43,7 @@ foreach ($fields as $field) {' . $line . "
\n"; -endforeach; -?> \ No newline at end of file +endforeach; \ No newline at end of file diff --git a/lib/Cake/Console/Templates/skel/View/Errors/error400.ctp b/lib/Cake/Console/Templates/skel/View/Errors/error400.ctp index 8e4ff3d0a98..6c84d88faf4 100644 --- a/lib/Cake/Console/Templates/skel/View/Errors/error400.ctp +++ b/lib/Cake/Console/Templates/skel/View/Errors/error400.ctp @@ -17,4 +17,3 @@ if (Configure::read('debug') > 0): echo $this->element('exception_stack_trace'); endif; -?> diff --git a/lib/Cake/Console/Templates/skel/View/Errors/error500.ctp b/lib/Cake/Console/Templates/skel/View/Errors/error500.ctp index c1d5ffb55c1..339e140dabc 100644 --- a/lib/Cake/Console/Templates/skel/View/Errors/error500.ctp +++ b/lib/Cake/Console/Templates/skel/View/Errors/error500.ctp @@ -14,4 +14,3 @@ if (Configure::read('debug') > 0): echo $this->element('exception_stack_trace'); endif; -?> diff --git a/lib/Cake/Console/Templates/skel/View/Pages/home.ctp b/lib/Cake/Console/Templates/skel/View/Pages/home.ctp index 0a028702e39..ad84a25539e 100644 --- a/lib/Cake/Console/Templates/skel/View/Pages/home.ctp +++ b/lib/Cake/Console/Templates/skel/View/Pages/home.ctp @@ -62,7 +62,7 @@ endif; $settings = Cache::settings(); if (!empty($settings)): echo ''; - echo __d('cake_dev', 'The %s is being used for core caching. To change the config edit %s', ''. $settings['engine'] . 'Engine', 'APP/Config/core.php'); + echo __d('cake_dev', 'The %s is being used for core caching. To change the config edit %s', '' . $settings['engine'] . 'Engine', 'APP/Config/core.php'); echo ''; else: echo ''; @@ -141,7 +141,7 @@ if (isset($filePresent)): echo ''; echo __d('cake_dev', 'DebugKit is not installed. It will help you inspect and debug different aspects of your application.'); echo '
';
+ $expected = '
';
$result = $this->CakeEmail->send();
$this->assertContains($expected, $result['message']);
}
@@ -1996,7 +2024,7 @@ public function testEmailFormat() {
$this->assertEquals('html', $result);
$this->setExpectedException('SocketException');
- $result = $this->CakeEmail->emailFormat('invalid');
+ $this->CakeEmail->emailFormat('invalid');
}
/**
@@ -2256,10 +2284,10 @@ public function testCharsetsCompatible() {
protected function _getEmailByOldStyleCharset($charset, $headerCharset) {
$email = new CakeEmail(array('transport' => 'Debug'));
- if (! empty($charset)) {
+ if (!empty($charset)) {
$email->charset = $charset;
}
- if (! empty($headerCharset)) {
+ if (!empty($headerCharset)) {
$email->headerCharset = $headerCharset;
}
@@ -2280,10 +2308,10 @@ protected function _getEmailByOldStyleCharset($charset, $headerCharset) {
protected function _getEmailByNewStyleCharset($charset, $headerCharset) {
$email = new CakeEmail(array('transport' => 'Debug'));
- if (! empty($charset)) {
+ if (!empty($charset)) {
$email->charset($charset);
}
- if (! empty($headerCharset)) {
+ if (!empty($headerCharset)) {
$email->headerCharset($headerCharset);
}
@@ -2353,7 +2381,6 @@ public function testWrapWithTagsAcrossLines() {
style="font-weight: bold">The tag is across multiple lines
HTML;
- $length = strlen($str);
$message = $str . str_repeat('x', CakeEmail::LINE_LENGTH_MUST + 1);
$this->CakeEmail->reset();
@@ -2435,6 +2462,25 @@ public function testZeroOnlyLinesNotBeingEmptied() {
$this->assertEquals($expected, $result['message']);
}
+/**
+ * Test that really long lines don't cause errors.
+ *
+ * @return void
+ */
+ public function testReallyLongLine() {
+ $this->CakeEmail->reset();
+ $this->CakeEmail->config(array('empty'));
+ $this->CakeEmail->transport('Debug');
+ $this->CakeEmail->from('cake@cakephp.org');
+ $this->CakeEmail->to('cake@cakephp.org');
+ $this->CakeEmail->subject('Wordwrap Test');
+ $this->CakeEmail->emailFormat('html');
+ $this->CakeEmail->template('long_line', null);
+ $result = $this->CakeEmail->send();
+ $this->assertContains('', $result['message'], 'First bits are included');
+ $this->assertContains('x', $result['message'], 'Last byte are included');
+ }
+
/**
* CakeEmailTest::assertLineLengths()
*
diff --git a/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php b/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php
index ca5653b49c1..9c6061c3645 100644
--- a/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php
+++ b/lib/Cake/Test/Case/Network/Email/DebugTransportTest.php
@@ -22,7 +22,6 @@
/**
* Test case
- *
*/
class DebugTransportTest extends CakeTestCase {
diff --git a/lib/Cake/Test/Case/Network/Email/MailTransportTest.php b/lib/Cake/Test/Case/Network/Email/MailTransportTest.php
index bccc95439db..dde5eaa9010 100644
--- a/lib/Cake/Test/Case/Network/Email/MailTransportTest.php
+++ b/lib/Cake/Test/Case/Network/Email/MailTransportTest.php
@@ -22,7 +22,6 @@
/**
* Test case
- *
*/
class MailTransportTest extends CakeTestCase {
diff --git a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php
index 7c4076439e6..478de6b98ac 100644
--- a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php
+++ b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php
@@ -22,14 +22,13 @@
/**
* Help to test SmtpTransport
- *
*/
class SmtpTestTransport extends SmtpTransport {
/**
* Helper to change the socket
*
- * @param object $socket
+ * @param CakeSocket $socket A socket.
* @return void
*/
public function setSocket(CakeSocket $socket) {
@@ -39,7 +38,7 @@ public function setSocket(CakeSocket $socket) {
/**
* Helper to change the CakeEmail
*
- * @param object $cakeEmail
+ * @param object $cakeEmail An email object.
* @return void
*/
public function setCakeEmail($cakeEmail) {
@@ -57,8 +56,8 @@ protected function _generateSocket() {
/**
* Magic function to call protected methods
*
- * @param string $method
- * @param string $args
+ * @param string $method The method to call.
+ * @param string $args The arguments.
* @return mixed
*/
public function __call($method, $args) {
@@ -70,7 +69,6 @@ public function __call($method, $args) {
/**
* Test case
- *
*/
class SmtpTransportTest extends CakeTestCase {
diff --git a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php
index 6456a0c21fc..318e43401fa 100644
--- a/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php
+++ b/lib/Cake/Test/Case/Network/Http/HttpSocketTest.php
@@ -29,8 +29,8 @@ class TestAuthentication {
/**
* authentication method
*
- * @param HttpSocket $http
- * @param array $authInfo
+ * @param HttpSocket $http A HTTP socket.
+ * @param array &$authInfo Some auth info.
* @return void
*/
public static function authentication(HttpSocket $http, &$authInfo) {
@@ -40,8 +40,8 @@ public static function authentication(HttpSocket $http, &$authInfo) {
/**
* proxyAuthentication method
*
- * @param HttpSocket $http
- * @param array $proxyInfo
+ * @param HttpSocket $http A HTTP socket.
+ * @param array &$proxyInfo Some proxy info.
* @return void
*/
public static function proxyAuthentication(HttpSocket $http, &$proxyInfo) {
@@ -52,7 +52,6 @@ public static function proxyAuthentication(HttpSocket $http, &$proxyInfo) {
/**
* CustomResponse
- *
*/
class CustomResponse {
@@ -66,6 +65,7 @@ class CustomResponse {
/**
* Constructor
*
+ * @param string $message A message.
*/
public function __construct($message) {
$this->first10 = substr($message, 0, 10);
@@ -75,7 +75,6 @@ public function __construct($message) {
/**
* TestHttpSocket
- *
*/
class TestHttpSocket extends HttpSocket {
@@ -135,7 +134,6 @@ public function parseQuery($query) {
* Convenience method for testing protected method
*
* @param array $request Needs to contain a 'uri' key. Should also contain a 'method' key, otherwise defaults to GET.
- * @param string $versionToken The version token to use, defaults to HTTP/1.1
* @return string Request line
*/
public function buildRequestLine($request = array()) {
@@ -307,6 +305,10 @@ public function testConfigUri() {
* @return void
*/
public function testRequest() {
+ $this->Socket->expects($this->any())
+ ->method('read')
+ ->will($this->returnValue(false));
+
$this->Socket->reset();
$response = $this->Socket->request(true);
@@ -317,11 +319,18 @@ public function testRequest() {
'verify_peer' => true,
'allow_self_signed' => false,
'verify_depth' => 5,
+ 'SNI_enabled' => true,
'CN_match' => 'www.cakephp.org',
'cafile' => CAKE . 'Config' . DS . 'cacert.pem'
)
);
+ if (version_compare(PHP_VERSION, '5.6.0', '>=')) {
+ $context['ssl']['peer_name'] = 'www.cakephp.org';
+ } else {
+ $context['ssl']['SNI_server_name'] = 'www.cakephp.org';
+ }
+
$tests = array(
array(
'request' => 'http://www.cakephp.org/?foo=bar',
@@ -572,7 +581,7 @@ public function testRequest() {
$this->Socket->reset();
$request = array('method' => 'POST', 'uri' => 'http://www.cakephp.org/posts/add', 'body' => array('name' => 'HttpSocket-is-released', 'date' => 'today'));
- $response = $this->Socket->request($request);
+ $this->Socket->request($request);
$this->assertEquals("name=HttpSocket-is-released&date=today", $this->Socket->request['body']);
}
@@ -582,6 +591,10 @@ public function testRequest() {
* @return void
*/
public function testGetWithSchemeAndPort() {
+ $this->Socket->expects($this->any())
+ ->method('read')
+ ->will($this->returnValue(false));
+
$this->Socket->reset();
$request = array(
'uri' => array(
@@ -602,6 +615,10 @@ public function testGetWithSchemeAndPort() {
* @return void
*/
public function testRequestWithStringQuery() {
+ $this->Socket->expects($this->any())
+ ->method('read')
+ ->will($this->returnValue(false));
+
$this->Socket->reset();
$request = array(
'uri' => array(
@@ -635,14 +652,18 @@ public function testRequestNotAllowedUri() {
*/
public function testRequest2() {
$this->Socket->reset();
+
$request = array('uri' => 'htpp://www.cakephp.org/');
$number = mt_rand(0, 9999999);
$this->Socket->expects($this->any())->method('connect')->will($this->returnValue(true));
$serverResponse = "HTTP/1.x 200 OK\r\nDate: Mon, 16 Apr 2007 04:14:16 GMT\r\nServer: CakeHttp Server\r\nContent-Type: text/html\r\n\r\nText here
', array('clean' => 'html', 'before' => ':', 'after' => '') ); @@ -268,13 +283,14 @@ public function testCleanInsert() { /** * Tests that non-insertable variables (i.e. arrays) are skipped when used as values in - * String::insert(). + * CakeText::insert(). * * @return void + * @covers ::insert */ public function testAutoIgnoreBadInsertData() { $data = array('foo' => 'alpha', 'bar' => 'beta', 'fale' => array()); - $result = String::insert('(:foo > :bar || :fale!)', $data, array('clean' => 'text')); + $result = CakeText::insert('(:foo > :bar || :fale!)', $data, array('clean' => 'text')); $this->assertEquals('(alpha > beta || !)', $result); } @@ -282,37 +298,49 @@ public function testAutoIgnoreBadInsertData() { * testTokenize method * * @return void + * @covers ::tokenize */ public function testTokenize() { - $result = String::tokenize('A,(short,boring test)'); + $result = CakeText::tokenize('A,(short,boring test)'); $expected = array('A', '(short,boring test)'); $this->assertEquals($expected, $result); - $result = String::tokenize('A,(short,more interesting( test)'); + $result = CakeText::tokenize('A,(short,more interesting( test)'); $expected = array('A', '(short,more interesting( test)'); $this->assertEquals($expected, $result); - $result = String::tokenize('A,(short,very interesting( test))'); + $result = CakeText::tokenize('A,(short,very interesting( test))'); $expected = array('A', '(short,very interesting( test))'); $this->assertEquals($expected, $result); - $result = String::tokenize('"single tag"', ' ', '"', '"'); + $result = CakeText::tokenize('"single tag"', ' ', '"', '"'); $expected = array('"single tag"'); $this->assertEquals($expected, $result); - $result = String::tokenize('tagA "single tag" tagB', ' ', '"', '"'); + $result = CakeText::tokenize('tagA "single tag" tagB', ' ', '"', '"'); $expected = array('tagA', '"single tag"', 'tagB'); $this->assertEquals($expected, $result); - $result = String::tokenize(''); + // Ideographic width space. + $result = CakeText::tokenize("tagA\xe3\x80\x80\"single\xe3\x80\x80tag\"\xe3\x80\x80tagB", "\xe3\x80\x80", '"', '"'); + $expected = array('tagA', '"single tag"', 'tagB'); + $this->assertEquals($expected, $result); + + $result = CakeText::tokenize(''); $expected = array(); $this->assertEquals($expected, $result); } +/** + * testReplaceWithQuestionMarkInString method + * + * @return void + * @covers ::insert + */ public function testReplaceWithQuestionMarkInString() { $string = ':a, :b and :c?'; $expected = '2 and 3?'; - $result = String::insert($string, array('b' => 2, 'c' => 3), array('clean' => true)); + $result = CakeText::insert($string, array('b' => 2, 'c' => 3), array('clean' => true)); $this->assertEquals($expected, $result); } @@ -321,9 +349,11 @@ public function testReplaceWithQuestionMarkInString() { * * @dataProvider wordWrapProvider * @return void + * @covers ::wordWrap + * @covers ::_wordWrap */ public function testWordWrap($text, $width, $break = "\n", $cut = false) { - $result = String::wordWrap($text, $width, $break, $cut); + $result = CakeText::wordWrap($text, $width, $break, $cut); $expected = wordwrap($text, $width, $break, $cut); $this->assertTextEquals($expected, $result, 'Text not wrapped same as built-in function.'); } @@ -354,10 +384,12 @@ public function wordWrapProvider() { * test that wordWrap() properly handle unicode strings. * * @return void + * @covers ::wordWrap + * @covers ::_wordWrap */ public function testWordWrapUnicodeAware() { $text = 'Но вим омниюм факёльиси элыктрам, мюнырэ лэгыры векж ыт. Выльёт квюандо нюмквуам ты кюм. Зыд эю рыбюм.'; - $result = String::wordWrap($text, 33, "\n", true); + $result = CakeText::wordWrap($text, 33, "\n", true); $expected = <<'; - $close = php_sapi_name() === 'cli' ? "\n" : ''; + $open = PHP_SAPI === 'cli' ? "\n" : '
'; + $close = PHP_SAPI === 'cli' ? "\n" : ''; $expected = <<
'; - $close = php_sapi_name() === 'cli' ? "\n" : ''; + $open = PHP_SAPI === 'cli' ? "\n" : '
'; + $close = PHP_SAPI === 'cli' ? "\n" : ''; $expected = <<
Broadcast message testing
\nBroadcast message testing
\n| Paginator->sort('id'); ?> | +Paginator->sort('article_id'); ?> | +Paginator->sort('user_id'); ?> | +Paginator->sort('comment'); ?> | +Paginator->sort('published'); ?> | +Paginator->sort('created'); ?> | +Paginator->sort('updated'); ?> | ++ |
|---|---|---|---|---|---|---|---|
| + | + Html->link($viewTaskComment['Article']['title'], array('controller' => 'view_task_articles', 'action' => 'view', $viewTaskComment['Article']['id'])); ?> + | ++ | + | + | + | + | + Html->link(__('View'), array('action' => 'view', $viewTaskComment['ViewTaskComment']['id'])); ?> + Html->link(__('Edit'), array('action' => 'edit', $viewTaskComment['ViewTaskComment']['id'])); ?> + Form->postLink(__('Delete'), array('action' => 'delete', $viewTaskComment['ViewTaskComment']['id']), array('confirm' => __('Are you sure you want to delete # %s?', $viewTaskComment['ViewTaskComment']['id']))); ?> + | +
+ Paginator->counter(array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + )); + ?>
+' . $line . '
'; -endforeach; -?> \ No newline at end of file +endforeach; \ No newline at end of file diff --git a/lib/Cake/Test/test_app/View/Emails/html/long_line.ctp b/lib/Cake/Test/test_app/View/Emails/html/long_line.ctp new file mode 100644 index 00000000000..4bf8bbdab53 --- /dev/null +++ b/lib/Cake/Test/test_app/View/Emails/html/long_line.ctp @@ -0,0 +1,14 @@ +34567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'12345678901234567890123456789012345678901234567890123456789012345678901234567890' . +'1234567890123456789012345678901234567890123456x'; diff --git a/lib/Cake/Test/test_app/View/Layouts/ajax2.ctp b/lib/Cake/Test/test_app/View/Layouts/ajax2.ctp index 43331599001..25c20f425ca 100644 --- a/lib/Cake/Test/test_app/View/Layouts/ajax2.ctp +++ b/lib/Cake/Test/test_app/View/Layouts/ajax2.ctp @@ -1,2 +1,3 @@ Ajax! -fetch('content'); ?> \ No newline at end of file +fetch('content'); \ No newline at end of file diff --git a/lib/Cake/Test/test_app/View/Layouts/rss/default.ctp b/lib/Cake/Test/test_app/View/Layouts/rss/default.ctp index df2f0ab19b8..f0af9c0d842 100644 --- a/lib/Cake/Test/test_app/View/Layouts/rss/default.ctp +++ b/lib/Cake/Test/test_app/View/Layouts/rss/default.ctp @@ -12,6 +12,4 @@ echo $this->Rss->document( $this->Rss->channel( array(), $channel, $this->fetch('content') ) -); - -?> \ No newline at end of file +); \ No newline at end of file diff --git a/lib/Cake/Test/test_app/View/Pages/extract.ctp b/lib/Cake/Test/test_app/View/Pages/extract.ctp index 3331f2fd4da..63e8c73a573 100644 --- a/lib/Cake/Test/test_app/View/Pages/extract.ctp +++ b/lib/Cake/Test/test_app/View/Pages/extract.ctp @@ -28,6 +28,8 @@ __('Hot features!' . ' Join us #cakephp on IRC. We\'d love to help you get started'); // Category +echo __c('You have a new message (category: LC_NUMERIC).', 4); +// LC_TIME is skipped. echo __c('You have a new message (category: LC_TIME).', 5); // Context diff --git a/lib/Cake/Test/test_app/View/Posts/helper_overwrite.ctp b/lib/Cake/Test/test_app/View/Posts/helper_overwrite.ctp index 100c5f51bb1..610d33ad907 100644 --- a/lib/Cake/Test/test_app/View/Posts/helper_overwrite.ctp +++ b/lib/Cake/Test/test_app/View/Posts/helper_overwrite.ctp @@ -1,4 +1,3 @@ Html->link('Test link', '#'); -?> \ No newline at end of file +echo $this->Html->link('Test link', '#'); \ No newline at end of file diff --git a/lib/Cake/Test/test_app/View/Posts/parent_view.ctp b/lib/Cake/Test/test_app/View/Posts/parent_view.ctp index e338b784f28..ee468954414 100644 --- a/lib/Cake/Test/test_app/View/Posts/parent_view.ctp +++ b/lib/Cake/Test/test_app/View/Posts/parent_view.ctp @@ -1,2 +1,2 @@ Parent View. -fetch('content') ?> +fetch('content'); ?> diff --git a/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp b/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp index c0b2ac283ab..19a88e33919 100644 --- a/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp +++ b/lib/Cake/Test/test_app/View/Posts/test_nocache_tags.ctp @@ -47,7 +47,7 @@ Html->image('test.jpg') ?> \ No newline at end of file +Html->image('test.jpg'); ?> \ No newline at end of file diff --git a/lib/Cake/Test/test_app/View/Themed/TestTheme/Layouts/default.ctp b/lib/Cake/Test/test_app/View/Themed/TestTheme/Layouts/default.ctp index 9f2aa062d63..d4e3dd5868f 100644 --- a/lib/Cake/Test/test_app/View/Themed/TestTheme/Layouts/default.ctp +++ b/lib/Cake/Test/test_app/View/Themed/TestTheme/Layouts/default.ctp @@ -1,2 +1,2 @@ default test_theme layout -fetch('content') ?> +fetch('content'); ?> diff --git a/lib/Cake/TestSuite/CakeTestCase.php b/lib/Cake/TestSuite/CakeTestCase.php index e457a033b51..c375717fe9a 100644 --- a/lib/Cake/TestSuite/CakeTestCase.php +++ b/lib/Cake/TestSuite/CakeTestCase.php @@ -548,7 +548,7 @@ protected function _assertAttributes($assertions, $string) { * @return void */ protected static function assertEqual($result, $expected, $message = '') { - return self::assertEquals($expected, $result, $message); + return static::assertEquals($expected, $result, $message); } /** @@ -561,7 +561,7 @@ protected static function assertEqual($result, $expected, $message = '') { * @return void */ protected static function assertNotEqual($result, $expected, $message = '') { - return self::assertNotEquals($expected, $result, $message); + return static::assertNotEquals($expected, $result, $message); } /** @@ -574,7 +574,7 @@ protected static function assertNotEqual($result, $expected, $message = '') { * @return void */ protected static function assertPattern($pattern, $string, $message = '') { - return self::assertRegExp($pattern, $string, $message); + return static::assertRegExp($pattern, $string, $message); } /** @@ -587,7 +587,7 @@ protected static function assertPattern($pattern, $string, $message = '') { * @return void */ protected static function assertIdentical($actual, $expected, $message = '') { - return self::assertSame($expected, $actual, $message); + return static::assertSame($expected, $actual, $message); } /** @@ -600,7 +600,7 @@ protected static function assertIdentical($actual, $expected, $message = '') { * @return void */ protected static function assertNotIdentical($actual, $expected, $message = '') { - return self::assertNotSame($expected, $actual, $message); + return static::assertNotSame($expected, $actual, $message); } /** @@ -613,7 +613,7 @@ protected static function assertNotIdentical($actual, $expected, $message = '') * @return void */ protected static function assertNoPattern($pattern, $string, $message = '') { - return self::assertNotRegExp($pattern, $string, $message); + return static::assertNotRegExp($pattern, $string, $message); } /** @@ -643,9 +643,9 @@ protected function expectError($expected = false, $message = '') { /** * Compatibility wrapper function for setExpectedException * - * @param mixed $expected the name of the Exception + * @param mixed $name The name of the expected Exception. * @param string $message the text to display if the assertion is not correct - * @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0 + * @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0. * @return void */ protected function expectException($name = 'Exception', $message = '') { @@ -662,7 +662,7 @@ protected function expectException($name = 'Exception', $message = '') { * @return void */ protected static function assertReference(&$first, &$second, $message = '') { - return self::assertSame($first, $second, $message); + return static::assertSame($first, $second, $message); } /** @@ -675,7 +675,7 @@ protected static function assertReference(&$first, &$second, $message = '') { * @return void */ protected static function assertIsA($object, $type, $message = '') { - return self::assertInstanceOf($type, $object, $message); + return static::assertInstanceOf($type, $object, $message); } /** @@ -690,7 +690,7 @@ protected static function assertIsA($object, $type, $message = '') { protected static function assertWithinMargin($result, $expected, $margin, $message = '') { $upper = $result + $margin; $lower = $result - $margin; - return self::assertTrue((($expected <= $upper) && ($expected >= $lower)), $message); + return static::assertTrue((($expected <= $upper) && ($expected >= $lower)), $message); } /** @@ -732,12 +732,12 @@ public function getMockForModel($model, $methods = array(), $config = array()) { $mock = $this->getMock($name, $methods, array($config)); $availableDs = array_keys(ConnectionManager::enumConnectionObjects()); - if ($mock->useDbConfig === 'default') { - $mock->useDbConfig = null; - $mock->setDataSource('test'); - } + if ($mock->useDbConfig !== 'test' && in_array('test_' . $mock->useDbConfig, $availableDs)) { $mock->setDataSource('test_' . $mock->useDbConfig); + } else { + $mock->useDbConfig = 'test'; + $mock->setDataSource('test'); } ClassRegistry::removeObject($name); diff --git a/lib/Cake/TestSuite/CakeTestLoader.php b/lib/Cake/TestSuite/CakeTestLoader.php index d6f3d8df41e..3fe681bde87 100644 --- a/lib/Cake/TestSuite/CakeTestLoader.php +++ b/lib/Cake/TestSuite/CakeTestLoader.php @@ -42,9 +42,9 @@ public function load($filePath, $params = '') { /** * Convert path fragments used by CakePHP's test runner to absolute paths that can be fed to PHPUnit. * - * @param string $filePath The file path to load - * @param string $params Additional parameters - * @return void + * @param string $filePath The file path to load. + * @param string $params Additional parameters. + * @return string Converted path fragments. */ protected function _resolveTestFile($filePath, $params) { $basePath = $this->_basePath($params) . DS . $filePath; @@ -85,8 +85,8 @@ protected static function _basePath($params) { * @return array */ public static function generateTestList($params) { - $directory = self::_basePath($params); - $fileList = self::_getRecursiveFileList($directory); + $directory = static::_basePath($params); + $fileList = static::_getRecursiveFileList($directory); $testCases = array(); foreach ($fileList as $testCaseFile) { diff --git a/lib/Cake/TestSuite/CakeTestRunner.php b/lib/Cake/TestSuite/CakeTestRunner.php index 3aafdc198cc..d512724cc04 100644 --- a/lib/Cake/TestSuite/CakeTestRunner.php +++ b/lib/Cake/TestSuite/CakeTestRunner.php @@ -48,7 +48,7 @@ public function __construct($loader, $params) { */ public function doRun(PHPUnit_Framework_Test $suite, array $arguments = array()) { if (isset($arguments['printer'])) { - self::$versionStringPrinted = true; + static::$versionStringPrinted = true; } $fixture = $this->_getFixtureManager($arguments); diff --git a/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php b/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php index 8b8b1e3cad2..85c6e2278e3 100644 --- a/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php +++ b/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php @@ -37,7 +37,7 @@ class CakeTestSuiteDispatcher { 'codeCoverage' => false, 'case' => null, 'core' => false, - 'app' => true, + 'app' => false, 'plugin' => null, 'output' => 'html', 'show' => 'groups', @@ -254,7 +254,7 @@ protected function _runTestCase() { restore_error_handler(); try { - self::time(); + static::time(); $command = new CakeTestSuiteCommand('CakeTestLoader', $commandArgs); $command->run($options); } catch (MissingConnectionException $exception) { @@ -287,7 +287,7 @@ public static function time($reset = false) { * @return string formatted date */ public static function date($format) { - return date($format, self::time()); + return date($format, static::time()); } } diff --git a/lib/Cake/TestSuite/ControllerTestCase.php b/lib/Cake/TestSuite/ControllerTestCase.php index 8ed11c7da66..03d78011a3b 100644 --- a/lib/Cake/TestSuite/ControllerTestCase.php +++ b/lib/Cake/TestSuite/ControllerTestCase.php @@ -210,7 +210,7 @@ public function __call($name, $arguments) { * - `result` Get the return value of the controller action. Useful * for testing requestAction methods. * - * @param string $url The URL to test + * @param string|array $url The URL to test. * @param array $options See options * @return mixed The specified return type. * @triggers ControllerTestCase $Dispatch, array('request' => $request) @@ -224,6 +224,10 @@ protected function _testAction($url, $options = array()) { 'return' => 'result' ); + if (is_array($url)) { + $url = Router::url($url); + } + $restore = array('get' => $_GET, 'post' => $_POST); $_SERVER['REQUEST_METHOD'] = strtoupper($options['method']); @@ -254,7 +258,7 @@ protected function _testAction($url, $options = array()) { $Dispatch->parseParams(new CakeEvent('ControllerTestCase', $Dispatch, array('request' => $request))); if (!isset($request->params['controller']) && Router::currentRoute()) { $this->headers = Router::currentRoute()->response->header(); - return; + return null; } if ($this->_dirtyController) { $this->controller = null; @@ -271,7 +275,7 @@ protected function _testAction($url, $options = array()) { $params['requested'] = 1; } $Dispatch->testController = $this->controller; - $Dispatch->response = $this->getMock('CakeResponse', array('send')); + $Dispatch->response = $this->getMock('CakeResponse', array('send', '_clearBuffer')); $this->result = $Dispatch->dispatch($request, $Dispatch->response, $params); $this->controller = $Dispatch->testController; $this->vars = $this->controller->viewVars; diff --git a/lib/Cake/TestSuite/templates/header.php b/lib/Cake/TestSuite/templates/header.php index 616bc179f73..60afa75595b 100644 --- a/lib/Cake/TestSuite/templates/header.php +++ b/lib/Cake/TestSuite/templates/header.php @@ -106,7 +106,7 @@ div.code-coverage-results span.line-num strong { color:#666; } div.code-coverage-results div.start { border:1px solid #aaa; - border-width:1px 1px 0px 1px; + border-width:1px 1px 0 1px; margin-top:30px; padding-top:5px; } diff --git a/lib/Cake/TestSuite/templates/phpunit.php b/lib/Cake/TestSuite/templates/phpunit.php index f25d8d971b0..3f39a59d9ed 100644 --- a/lib/Cake/TestSuite/templates/phpunit.php +++ b/lib/Cake/TestSuite/templates/phpunit.php @@ -20,12 +20,7 @@You must install PHPUnit to use the CakePHP(tm) Test Suite.
-PHPUnit can be installed with pear, using the pear installer.
-To install with the PEAR installer run the following commands:
-pear config-set auto_discover 1pear install pear.phpunit.de/PHPUnitPHPUnit can be installed with Composer, or downloaded as a phar archive.
Once PHPUnit is installed make sure its located on PHP's include_path by checking your php.ini
For full instructions on how to install PHPUnit, see the PHPUnit installation guide.
diff --git a/lib/Cake/Utility/CakeNumber.php b/lib/Cake/Utility/CakeNumber.php index 61a3daf5942..970f734e9d6 100644 --- a/lib/Cake/Utility/CakeNumber.php +++ b/lib/Cake/Utility/CakeNumber.php @@ -116,13 +116,13 @@ public static function toReadableSize($size) { case $size < 1024: return __dn('cake', '%d Byte', '%d Bytes', $size, $size); case round($size / 1024) < 1024: - return __d('cake', '%s KB', self::precision($size / 1024, 0)); + return __d('cake', '%s KB', static::precision($size / 1024, 0)); case round($size / 1024 / 1024, 2) < 1024: - return __d('cake', '%s MB', self::precision($size / 1024 / 1024, 2)); + return __d('cake', '%s MB', static::precision($size / 1024 / 1024, 2)); case round($size / 1024 / 1024 / 1024, 2) < 1024: - return __d('cake', '%s GB', self::precision($size / 1024 / 1024 / 1024, 2)); + return __d('cake', '%s GB', static::precision($size / 1024 / 1024 / 1024, 2)); default: - return __d('cake', '%s TB', self::precision($size / 1024 / 1024 / 1024 / 1024, 2)); + return __d('cake', '%s TB', static::precision($size / 1024 / 1024 / 1024 / 1024, 2)); } } @@ -181,7 +181,7 @@ public static function toPercentage($value, $precision = 2, $options = array()) if ($options['multiply']) { $value *= 100; } - return self::precision($value, $precision) . '%'; + return static::precision($value, $precision) . '%'; } /** @@ -221,8 +221,8 @@ public static function format($value, $options = false) { extract($options); } - $value = self::_numberFormat($value, $places, '.', ''); - $out = $before . self::_numberFormat($value, $places, $decimals, $thousands) . $after; + $value = static::_numberFormat($value, $places, '.', ''); + $out = $before . static::_numberFormat($value, $places, $decimals, $thousands) . $after; if ($escape) { return h($out); @@ -249,10 +249,10 @@ public static function format($value, $options = false) { */ public static function formatDelta($value, $options = array()) { $places = isset($options['places']) ? $options['places'] : 0; - $value = self::_numberFormat($value, $places, '.', ''); + $value = static::_numberFormat($value, $places, '.', ''); $sign = $value > 0 ? '+' : ''; $options['before'] = isset($options['before']) ? $options['before'] . $sign : $sign; - return self::format($value, $options); + return static::format($value, $options); } /** @@ -265,10 +265,10 @@ public static function formatDelta($value, $options = array()) { * @return string */ protected static function _numberFormat($value, $places = 0, $decimals = '.', $thousands = ',') { - if (!isset(self::$_numberFormatSupport)) { - self::$_numberFormatSupport = version_compare(PHP_VERSION, '5.4.0', '>='); + if (!isset(static::$_numberFormatSupport)) { + static::$_numberFormatSupport = version_compare(PHP_VERSION, '5.4.0', '>='); } - if (self::$_numberFormatSupport) { + if (static::$_numberFormatSupport) { return number_format($value, $places, $decimals, $thousands); } $value = number_format($value, $places, '.', ''); @@ -323,13 +323,13 @@ protected static function _numberFormat($value, $places = 0, $decimals = '.', $t * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::currency */ public static function currency($value, $currency = null, $options = array()) { - $defaults = self::$_currencyDefaults; + $defaults = static::$_currencyDefaults; if ($currency === null) { - $currency = self::defaultCurrency(); + $currency = static::defaultCurrency(); } - if (isset(self::$_currencies[$currency])) { - $defaults = self::$_currencies[$currency]; + if (isset(static::$_currencies[$currency])) { + $defaults = static::$_currencies[$currency]; } elseif (is_string($currency)) { $options['before'] = $currency; } @@ -364,7 +364,7 @@ public static function currency($value, $currency = null, $options = array()) { $options[$position] = $options[$symbolKey . 'Symbol']; $abs = abs($value); - $result = self::format($abs, $options); + $result = static::format($abs, $options); if ($value < 0) { if ($options['negative'] === '()') { @@ -396,7 +396,7 @@ public static function currency($value, $currency = null, $options = array()) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::addFormat */ public static function addFormat($formatName, $options) { - self::$_currencies[$formatName] = $options + self::$_currencyDefaults; + static::$_currencies[$formatName] = $options + static::$_currencyDefaults; } /** @@ -408,9 +408,9 @@ public static function addFormat($formatName, $options) { */ public static function defaultCurrency($currency = null) { if ($currency) { - self::$_defaultCurrency = $currency; + static::$_defaultCurrency = $currency; } - return self::$_defaultCurrency; + return static::$_defaultCurrency; } } diff --git a/lib/Cake/Utility/CakeText.php b/lib/Cake/Utility/CakeText.php new file mode 100644 index 00000000000..ff60cffa7a9 --- /dev/null +++ b/lib/Cake/Utility/CakeText.php @@ -0,0 +1,710 @@ + 65535) { + $pid = mt_rand(0, 0xfff) | 0x4000; + } + + list($timeMid, $timeLow) = explode(' ', microtime()); + return sprintf( + "%08x-%04x-%04x-%02x%02x-%04x%08x", (int)$timeLow, (int)substr($timeMid, 2) & 0xffff, + mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3f) | 0x80, mt_rand(0, 0xff), $pid, $node + ); + } + +/** + * Tokenizes a string using $separator, ignoring any instance of $separator that appears between + * $leftBound and $rightBound. + * + * @param string $data The data to tokenize. + * @param string $separator The token to split the data on. + * @param string $leftBound The left boundary to ignore separators in. + * @param string $rightBound The right boundary to ignore separators in. + * @return mixed Array of tokens in $data or original input if empty. + */ + public static function tokenize($data, $separator = ',', $leftBound = '(', $rightBound = ')') { + if (empty($data)) { + return array(); + } + + $depth = 0; + $offset = 0; + $buffer = ''; + $results = array(); + $length = mb_strlen($data); + $open = false; + + while ($offset <= $length) { + $tmpOffset = -1; + $offsets = array( + mb_strpos($data, $separator, $offset), + mb_strpos($data, $leftBound, $offset), + mb_strpos($data, $rightBound, $offset) + ); + for ($i = 0; $i < 3; $i++) { + if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset == -1)) { + $tmpOffset = $offsets[$i]; + } + } + if ($tmpOffset !== -1) { + $buffer .= mb_substr($data, $offset, ($tmpOffset - $offset)); + $char = mb_substr($data, $tmpOffset, 1); + if (!$depth && $char === $separator) { + $results[] = $buffer; + $buffer = ''; + } else { + $buffer .= $char; + } + if ($leftBound !== $rightBound) { + if ($char === $leftBound) { + $depth++; + } + if ($char === $rightBound) { + $depth--; + } + } else { + if ($char === $leftBound) { + if (!$open) { + $depth++; + $open = true; + } else { + $depth--; + } + } + } + $offset = ++$tmpOffset; + } else { + $results[] = $buffer . mb_substr($data, $offset); + $offset = $length + 1; + } + } + if (empty($results) && !empty($buffer)) { + $results[] = $buffer; + } + + if (!empty($results)) { + return array_map('trim', $results); + } + + return array(); + } + +/** + * Replaces variable placeholders inside a $str with any given $data. Each key in the $data array + * corresponds to a variable placeholder name in $str. + * Example: `CakeText::insert(':name is :age years old.', array('name' => 'Bob', '65'));` + * Returns: Bob is 65 years old. + * + * Available $options are: + * + * - before: The character or string in front of the name of the variable placeholder (Defaults to `:`) + * - after: The character or string after the name of the variable placeholder (Defaults to null) + * - escape: The character or string used to escape the before character / string (Defaults to `\`) + * - format: A regex to use for matching variable placeholders. Default is: `/(? val array where each key stands for a placeholder variable name + * to be replaced with val + * @param array $options An array of options, see description above + * @return string + */ + public static function insert($str, $data, $options = array()) { + $defaults = array( + 'before' => ':', 'after' => null, 'escape' => '\\', 'format' => null, 'clean' => false + ); + $options += $defaults; + $format = $options['format']; + $data = (array)$data; + if (empty($data)) { + return ($options['clean']) ? CakeText::cleanInsert($str, $options) : $str; + } + + if (!isset($format)) { + $format = sprintf( + '/(? $hashVal) { + $key = sprintf($format, preg_quote($key, '/')); + $str = preg_replace($key, $hashVal, $str); + } + $dataReplacements = array_combine($hashKeys, array_values($data)); + foreach ($dataReplacements as $tmpHash => $tmpValue) { + $tmpValue = (is_array($tmpValue)) ? '' : $tmpValue; + $str = str_replace($tmpHash, $tmpValue, $str); + } + + if (!isset($options['format']) && isset($options['before'])) { + $str = str_replace($options['escape'] . $options['before'], $options['before'], $str); + } + return ($options['clean']) ? CakeText::cleanInsert($str, $options) : $str; + } + +/** + * Cleans up a CakeText::insert() formatted string with given $options depending on the 'clean' key in + * $options. The default method used is text but html is also available. The goal of this function + * is to replace all whitespace and unneeded markup around placeholders that did not get replaced + * by CakeText::insert(). + * + * @param string $str CakeText to clean. + * @param array $options Options list. + * @return string + * @see CakeText::insert() + */ + public static function cleanInsert($str, $options) { + $clean = $options['clean']; + if (!$clean) { + return $str; + } + if ($clean === true) { + $clean = array('method' => 'text'); + } + if (!is_array($clean)) { + $clean = array('method' => $options['clean']); + } + switch ($clean['method']) { + case 'html': + $clean = array_merge(array( + 'word' => '[\w,.]+', + 'andText' => true, + 'replacement' => '', + ), $clean); + $kleenex = sprintf( + '/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i', + preg_quote($options['before'], '/'), + $clean['word'], + preg_quote($options['after'], '/') + ); + $str = preg_replace($kleenex, $clean['replacement'], $str); + if ($clean['andText']) { + $options['clean'] = array('method' => 'text'); + $str = CakeText::cleanInsert($str, $options); + } + break; + case 'text': + $clean = array_merge(array( + 'word' => '[\w,.]+', + 'gap' => '[\s]*(?:(?:and|or)[\s]*)?', + 'replacement' => '', + ), $clean); + + $kleenex = sprintf( + '/(%s%s%s%s|%s%s%s%s)/', + preg_quote($options['before'], '/'), + $clean['word'], + preg_quote($options['after'], '/'), + $clean['gap'], + $clean['gap'], + preg_quote($options['before'], '/'), + $clean['word'], + preg_quote($options['after'], '/') + ); + $str = preg_replace($kleenex, $clean['replacement'], $str); + break; + } + return $str; + } + +/** + * Wraps text to a specific width, can optionally wrap at word breaks. + * + * ### Options + * + * - `width` The width to wrap to. Defaults to 72. + * - `wordWrap` Only wrap on words breaks (spaces) Defaults to true. + * - `indent` CakeText to indent with. Defaults to null. + * - `indentAt` 0 based index to start indenting at. Defaults to 0. + * + * @param string $text The text to format. + * @param array|int $options Array of options to use, or an integer to wrap the text to. + * @return string Formatted text. + */ + public static function wrap($text, $options = array()) { + if (is_numeric($options)) { + $options = array('width' => $options); + } + $options += array('width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0); + if ($options['wordWrap']) { + $wrapped = static::wordWrap($text, $options['width'], "\n"); + } else { + $wrapped = trim(chunk_split($text, $options['width'] - 1, "\n")); + } + if (!empty($options['indent'])) { + $chunks = explode("\n", $wrapped); + for ($i = $options['indentAt'], $len = count($chunks); $i < $len; $i++) { + $chunks[$i] = $options['indent'] . $chunks[$i]; + } + $wrapped = implode("\n", $chunks); + } + return $wrapped; + } + +/** + * Unicode aware version of wordwrap. + * + * @param string $text The text to format. + * @param int $width The width to wrap to. Defaults to 72. + * @param string $break The line is broken using the optional break parameter. Defaults to '\n'. + * @param bool $cut If the cut is set to true, the string is always wrapped at the specified width. + * @return string Formatted text. + */ + public static function wordWrap($text, $width = 72, $break = "\n", $cut = false) { + $paragraphs = explode($break, $text); + foreach ($paragraphs as &$paragraph) { + $paragraph = static::_wordWrap($paragraph, $width, $break, $cut); + } + return implode($break, $paragraphs); + } + +/** + * Helper method for wordWrap(). + * + * @param string $text The text to format. + * @param int $width The width to wrap to. Defaults to 72. + * @param string $break The line is broken using the optional break parameter. Defaults to '\n'. + * @param bool $cut If the cut is set to true, the string is always wrapped at the specified width. + * @return string Formatted text. + */ + protected static function _wordWrap($text, $width = 72, $break = "\n", $cut = false) { + if ($cut) { + $parts = array(); + while (mb_strlen($text) > 0) { + $part = mb_substr($text, 0, $width); + $parts[] = trim($part); + $text = trim(mb_substr($text, mb_strlen($part))); + } + return implode($break, $parts); + } + + $parts = array(); + while (mb_strlen($text) > 0) { + if ($width >= mb_strlen($text)) { + $parts[] = trim($text); + break; + } + + $part = mb_substr($text, 0, $width); + $nextChar = mb_substr($text, $width, 1); + if ($nextChar !== ' ') { + $breakAt = mb_strrpos($part, ' '); + if ($breakAt === false) { + $breakAt = mb_strpos($text, ' ', $width); + } + if ($breakAt === false) { + $parts[] = trim($text); + break; + } + $part = mb_substr($text, 0, $breakAt); + } + + $part = trim($part); + $parts[] = $part; + $text = trim(mb_substr($text, mb_strlen($part))); + } + + return implode($break, $parts); + } + +/** + * Highlights a given phrase in a text. You can specify any expression in highlighter that + * may include the \1 expression to include the $phrase found. + * + * ### Options: + * + * - `format` The piece of html with that the phrase will be highlighted + * - `html` If true, will ignore any HTML tags, ensuring that only the correct text is highlighted + * - `regex` a custom regex rule that is used to match words, default is '|$tag|iu' + * + * @param string $text Text to search the phrase in. + * @param string|array $phrase The phrase or phrases that will be searched. + * @param array $options An array of html attributes and options. + * @return string The highlighted text + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::highlight + */ + public static function highlight($text, $phrase, $options = array()) { + if (empty($phrase)) { + return $text; + } + + $defaults = array( + 'format' => '\1', + 'html' => false, + 'regex' => "|%s|iu" + ); + $options += $defaults; + extract($options); + + if (is_array($phrase)) { + $replace = array(); + $with = array(); + + foreach ($phrase as $key => $segment) { + $segment = '(' . preg_quote($segment, '|') . ')'; + if ($html) { + $segment = "(?![^<]+>)$segment(?![^<]+>)"; + } + + $with[] = (is_array($format)) ? $format[$key] : $format; + $replace[] = sprintf($options['regex'], $segment); + } + + return preg_replace($replace, $with, $text); + } + + $phrase = '(' . preg_quote($phrase, '|') . ')'; + if ($html) { + $phrase = "(?![^<]+>)$phrase(?![^<]+>)"; + } + + return preg_replace(sprintf($options['regex'], $phrase), $format, $text); + } + +/** + * Strips given text of all links (]+>|im', '', preg_replace('|<\/a>|im', '', $text)); + } + +/** + * Truncates text starting from the end. + * + * Cuts a string to the length of $length and replaces the first characters + * with the ellipsis if the text is longer than length. + * + * ### Options: + * + * - `ellipsis` Will be used as Beginning and prepended to the trimmed string + * - `exact` If false, $text will not be cut mid-word + * + * @param string $text CakeText to truncate. + * @param int $length Length of returned string, including ellipsis. + * @param array $options An array of options. + * @return string Trimmed string. + */ + public static function tail($text, $length = 100, $options = array()) { + $defaults = array( + 'ellipsis' => '...', 'exact' => true + ); + $options += $defaults; + extract($options); + + if (!function_exists('mb_strlen')) { + class_exists('Multibyte'); + } + + if (mb_strlen($text) <= $length) { + return $text; + } + + $truncate = mb_substr($text, mb_strlen($text) - $length + mb_strlen($ellipsis)); + if (!$exact) { + $spacepos = mb_strpos($truncate, ' '); + $truncate = $spacepos === false ? '' : trim(mb_substr($truncate, $spacepos)); + } + + return $ellipsis . $truncate; + } + +/** + * Truncates text. + * + * Cuts a string to the length of $length and replaces the last characters + * with the ellipsis if the text is longer than length. + * + * ### Options: + * + * - `ellipsis` Will be used as Ending and appended to the trimmed string (`ending` is deprecated) + * - `exact` If false, $text will not be cut mid-word + * - `html` If true, HTML tags would be handled correctly + * + * @param string $text CakeText to truncate. + * @param int $length Length of returned string, including ellipsis. + * @param array $options An array of html attributes and options. + * @return string Trimmed string. + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::truncate + */ + public static function truncate($text, $length = 100, $options = array()) { + $defaults = array( + 'ellipsis' => '...', 'exact' => true, 'html' => false + ); + if (isset($options['ending'])) { + $defaults['ellipsis'] = $options['ending']; + } elseif (!empty($options['html']) && Configure::read('App.encoding') === 'UTF-8') { + $defaults['ellipsis'] = "\xe2\x80\xa6"; + } + $options += $defaults; + extract($options); + + if (!function_exists('mb_strlen')) { + class_exists('Multibyte'); + } + + if ($html) { + if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) { + return $text; + } + $totalLength = mb_strlen(strip_tags($ellipsis)); + $openTags = array(); + $truncate = ''; + + preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER); + foreach ($tags as $tag) { + if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2])) { + if (preg_match('/<[\w]+[^>]*>/s', $tag[0])) { + array_unshift($openTags, $tag[2]); + } elseif (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag)) { + $pos = array_search($closeTag[1], $openTags); + if ($pos !== false) { + array_splice($openTags, $pos, 1); + } + } + } + $truncate .= $tag[1]; + + $contentLength = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|[0-9]{1,7};|[0-9a-f]{1,6};/i', ' ', $tag[3])); + if ($contentLength + $totalLength > $length) { + $left = $length - $totalLength; + $entitiesLength = 0; + if (preg_match_all('/&[0-9a-z]{2,8};|[0-9]{1,7};|[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE)) { + foreach ($entities[0] as $entity) { + if ($entity[1] + 1 - $entitiesLength <= $left) { + $left--; + $entitiesLength += mb_strlen($entity[0]); + } else { + break; + } + } + } + + $truncate .= mb_substr($tag[3], 0, $left + $entitiesLength); + break; + } else { + $truncate .= $tag[3]; + $totalLength += $contentLength; + } + if ($totalLength >= $length) { + break; + } + } + } else { + if (mb_strlen($text) <= $length) { + return $text; + } + $truncate = mb_substr($text, 0, $length - mb_strlen($ellipsis)); + } + if (!$exact) { + $spacepos = mb_strrpos($truncate, ' '); + if ($html) { + $truncateCheck = mb_substr($truncate, 0, $spacepos); + $lastOpenTag = mb_strrpos($truncateCheck, '<'); + $lastCloseTag = mb_strrpos($truncateCheck, '>'); + if ($lastOpenTag > $lastCloseTag) { + preg_match_all('/<[\w]+[^>]*>/s', $truncate, $lastTagMatches); + $lastTag = array_pop($lastTagMatches[0]); + $spacepos = mb_strrpos($truncate, $lastTag) + mb_strlen($lastTag); + } + $bits = mb_substr($truncate, $spacepos); + preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER); + if (!empty($droppedTags)) { + if (!empty($openTags)) { + foreach ($droppedTags as $closingTag) { + if (!in_array($closingTag[1], $openTags)) { + array_unshift($openTags, $closingTag[1]); + } + } + } else { + foreach ($droppedTags as $closingTag) { + $openTags[] = $closingTag[1]; + } + } + } + } + $truncate = mb_substr($truncate, 0, $spacepos); + } + $truncate .= $ellipsis; + + if ($html) { + foreach ($openTags as $tag) { + $truncate .= '' . $tag . '>'; + } + } + + return $truncate; + } + +/** + * Extracts an excerpt from the text surrounding the phrase with a number of characters on each side + * determined by radius. + * + * @param string $text CakeText to search the phrase in + * @param string $phrase Phrase that will be searched for + * @param int $radius The amount of characters that will be returned on each side of the founded phrase + * @param string $ellipsis Ending that will be appended + * @return string Modified string + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::excerpt + */ + public static function excerpt($text, $phrase, $radius = 100, $ellipsis = '...') { + if (empty($text) || empty($phrase)) { + return static::truncate($text, $radius * 2, array('ellipsis' => $ellipsis)); + } + + $append = $prepend = $ellipsis; + + $phraseLen = mb_strlen($phrase); + $textLen = mb_strlen($text); + + $pos = mb_strpos(mb_strtolower($text), mb_strtolower($phrase)); + if ($pos === false) { + return mb_substr($text, 0, $radius) . $ellipsis; + } + + $startPos = $pos - $radius; + if ($startPos <= 0) { + $startPos = 0; + $prepend = ''; + } + + $endPos = $pos + $phraseLen + $radius; + if ($endPos >= $textLen) { + $endPos = $textLen; + $append = ''; + } + + $excerpt = mb_substr($text, $startPos, $endPos - $startPos); + $excerpt = $prepend . $excerpt . $append; + + return $excerpt; + } + +/** + * Creates a comma separated list where the last two items are joined with 'and', forming natural language. + * + * @param array $list The list to be joined. + * @param string $and The word used to join the last and second last items together with. Defaults to 'and'. + * @param string $separator The separator used to join all the other items together. Defaults to ', '. + * @return string The glued together string. + * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::toList + */ + public static function toList($list, $and = null, $separator = ', ') { + if ($and === null) { + $and = __d('cake', 'and'); + } + if (count($list) > 1) { + return implode($separator, array_slice($list, null, -1)) . ' ' . $and . ' ' . array_pop($list); + } + + return array_pop($list); + } +} diff --git a/lib/Cake/Utility/CakeTime.php b/lib/Cake/Utility/CakeTime.php index 38c22196a33..0987dce3c16 100644 --- a/lib/Cake/Utility/CakeTime.php +++ b/lib/Cake/Utility/CakeTime.php @@ -100,7 +100,7 @@ class CakeTime { public function __set($name, $value) { switch ($name) { case 'niceFormat': - self::${$name} = $value; + static::${$name} = $value; break; } } @@ -115,7 +115,7 @@ public function __set($name, $value) { public function __get($name) { switch ($name) { case 'niceFormat': - return self::${$name}; + return static::${$name}; default: return null; } @@ -123,25 +123,25 @@ public function __get($name) { /** * Converts a string representing the format for the function strftime and returns a - * windows safe and i18n aware format. + * Windows safe and i18n aware format. * * @param string $format Format with specifiers for strftime function. * Accepts the special specifier %S which mimics the modifier S for date() * @param string $time UNIX timestamp - * @return string windows safe and date() function compatible format for strftime + * @return string Windows safe and date() function compatible format for strftime * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::convertSpecifiers */ public static function convertSpecifiers($format, $time = null) { if (!$time) { $time = time(); } - self::$_time = $time; + static::$_time = $time; return preg_replace_callback('/\%(\w+)/', array('CakeTime', '_translateSpecifier'), $format); } /** * Auxiliary function to translate a matched specifier element from a regular expression into - * a windows safe and i18n aware specifier + * a Windows safe and i18n aware specifier * * @param array $specifier match from regular expression * @return string converted element @@ -151,47 +151,47 @@ protected static function _translateSpecifier($specifier) { case 'a': $abday = __dc('cake', 'abday', 5); if (is_array($abday)) { - return $abday[date('w', self::$_time)]; + return $abday[date('w', static::$_time)]; } break; case 'A': $day = __dc('cake', 'day', 5); if (is_array($day)) { - return $day[date('w', self::$_time)]; + return $day[date('w', static::$_time)]; } break; case 'c': $format = __dc('cake', 'd_t_fmt', 5); if ($format !== 'd_t_fmt') { - return self::convertSpecifiers($format, self::$_time); + return static::convertSpecifiers($format, static::$_time); } break; case 'C': - return sprintf("%02d", date('Y', self::$_time) / 100); + return sprintf("%02d", date('Y', static::$_time) / 100); case 'D': return '%m/%d/%y'; case 'e': if (DS === '/') { return '%e'; } - $day = date('j', self::$_time); + $day = date('j', static::$_time); if ($day < 10) { $day = ' ' . $day; } return $day; case 'eS' : - return date('jS', self::$_time); + return date('jS', static::$_time); case 'b': case 'h': $months = __dc('cake', 'abmon', 5); if (is_array($months)) { - return $months[date('n', self::$_time) - 1]; + return $months[date('n', static::$_time) - 1]; } return '%b'; case 'B': $months = __dc('cake', 'mon', 5); if (is_array($months)) { - return $months[date('n', self::$_time) - 1]; + return $months[date('n', static::$_time) - 1]; } break; case 'n': @@ -199,7 +199,7 @@ protected static function _translateSpecifier($specifier) { case 'p': case 'P': $default = array('am' => 0, 'pm' => 1); - $meridiem = $default[date('a', self::$_time)]; + $meridiem = $default[date('a', static::$_time)]; $format = __dc('cake', 'am_pm', 5); if (is_array($format)) { $meridiem = $format[$meridiem]; @@ -209,27 +209,27 @@ protected static function _translateSpecifier($specifier) { case 'r': $complete = __dc('cake', 't_fmt_ampm', 5); if ($complete !== 't_fmt_ampm') { - return str_replace('%p', self::_translateSpecifier(array('%p', 'p')), $complete); + return str_replace('%p', static::_translateSpecifier(array('%p', 'p')), $complete); } break; case 'R': - return date('H:i', self::$_time); + return date('H:i', static::$_time); case 't': return "\t"; case 'T': return '%H:%M:%S'; case 'u': - return ($weekDay = date('w', self::$_time)) ? $weekDay : 7; + return ($weekDay = date('w', static::$_time)) ? $weekDay : 7; case 'x': $format = __dc('cake', 'd_fmt', 5); if ($format !== 'd_fmt') { - return self::convertSpecifiers($format, self::$_time); + return static::convertSpecifiers($format, static::$_time); } break; case 'X': $format = __dc('cake', 't_fmt', 5); if ($format !== 't_fmt') { - return self::convertSpecifiers($format, self::$_time); + return static::convertSpecifiers($format, static::$_time); } break; } @@ -254,7 +254,7 @@ public static function convert($serverTime, $timezone) { if (is_numeric($timezone)) { $userOffset = $timezone * (60 * 60); } else { - $timezone = self::timezone($timezone); + $timezone = static::timezone($timezone); $userOffset = $timezone->getOffset(new DateTime('@' . $gmtTime)); } $userTime = $gmtTime + $userOffset; @@ -343,7 +343,7 @@ public static function fromString($dateString, $timezone = null) { } if ($timezone !== null) { - return self::convert($date, $timezone); + return static::convert($date, $timezone); } return $date; } @@ -364,12 +364,12 @@ public static function nice($dateString = null, $timezone = null, $format = null if (!$dateString) { $dateString = time(); } - $date = self::fromString($dateString, $timezone); + $date = static::fromString($dateString, $timezone); if (!$format) { - $format = self::$niceFormat; + $format = static::$niceFormat; } - return self::_strftime(self::convertSpecifiers($format, $date), $date); + return static::_strftime(static::convertSpecifiers($format, $date), $date); } /** @@ -391,19 +391,19 @@ public static function niceShort($dateString = null, $timezone = null) { if (!$dateString) { $dateString = time(); } - $date = self::fromString($dateString, $timezone); + $date = static::fromString($dateString, $timezone); - if (self::isToday($dateString, $timezone)) { - return __d('cake', 'Today, %s', self::_strftime("%H:%M", $date)); + if (static::isToday($dateString, $timezone)) { + return __d('cake', 'Today, %s', static::_strftime("%H:%M", $date)); } - if (self::wasYesterday($dateString, $timezone)) { - return __d('cake', 'Yesterday, %s', self::_strftime("%H:%M", $date)); + if (static::wasYesterday($dateString, $timezone)) { + return __d('cake', 'Yesterday, %s', static::_strftime("%H:%M", $date)); } - if (self::isTomorrow($dateString, $timezone)) { - return __d('cake', 'Tomorrow, %s', self::_strftime("%H:%M", $date)); + if (static::isTomorrow($dateString, $timezone)) { + return __d('cake', 'Tomorrow, %s', static::_strftime("%H:%M", $date)); } - $d = self::_strftime("%w", $date); + $d = static::_strftime("%w", $date); $day = array( __d('cake', 'Sunday'), __d('cake', 'Monday'), @@ -413,18 +413,18 @@ public static function niceShort($dateString = null, $timezone = null) { __d('cake', 'Friday'), __d('cake', 'Saturday') ); - if (self::wasWithinLast('7 days', $dateString, $timezone)) { - return sprintf('%s %s', $day[$d], self::_strftime(self::$niceShortFormat, $date)); + if (static::wasWithinLast('7 days', $dateString, $timezone)) { + return sprintf('%s %s', $day[$d], static::_strftime(static::$niceShortFormat, $date)); } - if (self::isWithinNext('7 days', $dateString, $timezone)) { - return __d('cake', 'On %s %s', $day[$d], self::_strftime(self::$niceShortFormat, $date)); + if (static::isWithinNext('7 days', $dateString, $timezone)) { + return __d('cake', 'On %s %s', $day[$d], static::_strftime(static::$niceShortFormat, $date)); } $y = ''; - if (!self::isThisYear($date)) { + if (!static::isThisYear($date)) { $y = ' %Y'; } - return self::_strftime(self::convertSpecifiers("%b %eS{$y}, %H:%M", $date), $date); + return static::_strftime(static::convertSpecifiers("%b %eS{$y}, %H:%M", $date), $date); } /** @@ -438,8 +438,8 @@ public static function niceShort($dateString = null, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::daysAsSql */ public static function daysAsSql($begin, $end, $fieldName, $timezone = null) { - $begin = self::fromString($begin, $timezone); - $end = self::fromString($end, $timezone); + $begin = static::fromString($begin, $timezone); + $end = static::fromString($end, $timezone); $begin = date('Y-m-d', $begin) . ' 00:00:00'; $end = date('Y-m-d', $end) . ' 23:59:59'; @@ -457,7 +457,7 @@ public static function daysAsSql($begin, $end, $fieldName, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::dayAsSql */ public static function dayAsSql($dateString, $fieldName, $timezone = null) { - return self::daysAsSql($dateString, $dateString, $fieldName, $timezone); + return static::daysAsSql($dateString, $dateString, $fieldName, $timezone); } /** @@ -469,8 +469,8 @@ public static function dayAsSql($dateString, $fieldName, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::isToday */ public static function isToday($dateString, $timezone = null) { - $timestamp = self::fromString($dateString, $timezone); - $now = self::fromString('now', $timezone); + $timestamp = static::fromString($dateString, $timezone); + $now = static::fromString('now', $timezone); return date('Y-m-d', $timestamp) === date('Y-m-d', $now); } @@ -483,7 +483,7 @@ public static function isToday($dateString, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::isFuture */ public static function isFuture($dateString, $timezone = null) { - $timestamp = self::fromString($dateString, $timezone); + $timestamp = static::fromString($dateString, $timezone); return $timestamp > time(); } @@ -496,7 +496,7 @@ public static function isFuture($dateString, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::isPast */ public static function isPast($dateString, $timezone = null) { - $timestamp = self::fromString($dateString, $timezone); + $timestamp = static::fromString($dateString, $timezone); return $timestamp < time(); } @@ -509,8 +509,8 @@ public static function isPast($dateString, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::isThisWeek */ public static function isThisWeek($dateString, $timezone = null) { - $timestamp = self::fromString($dateString, $timezone); - $now = self::fromString('now', $timezone); + $timestamp = static::fromString($dateString, $timezone); + $now = static::fromString('now', $timezone); return date('W o', $timestamp) === date('W o', $now); } @@ -523,8 +523,8 @@ public static function isThisWeek($dateString, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::isThisMonth */ public static function isThisMonth($dateString, $timezone = null) { - $timestamp = self::fromString($dateString, $timezone); - $now = self::fromString('now', $timezone); + $timestamp = static::fromString($dateString, $timezone); + $now = static::fromString('now', $timezone); return date('m Y', $timestamp) === date('m Y', $now); } @@ -537,8 +537,8 @@ public static function isThisMonth($dateString, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::isThisYear */ public static function isThisYear($dateString, $timezone = null) { - $timestamp = self::fromString($dateString, $timezone); - $now = self::fromString('now', $timezone); + $timestamp = static::fromString($dateString, $timezone); + $now = static::fromString('now', $timezone); return date('Y', $timestamp) === date('Y', $now); } @@ -551,8 +551,8 @@ public static function isThisYear($dateString, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::wasYesterday */ public static function wasYesterday($dateString, $timezone = null) { - $timestamp = self::fromString($dateString, $timezone); - $yesterday = self::fromString('yesterday', $timezone); + $timestamp = static::fromString($dateString, $timezone); + $yesterday = static::fromString('yesterday', $timezone); return date('Y-m-d', $timestamp) === date('Y-m-d', $yesterday); } @@ -565,8 +565,8 @@ public static function wasYesterday($dateString, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::isTomorrow */ public static function isTomorrow($dateString, $timezone = null) { - $timestamp = self::fromString($dateString, $timezone); - $tomorrow = self::fromString('tomorrow', $timezone); + $timestamp = static::fromString($dateString, $timezone); + $tomorrow = static::fromString('tomorrow', $timezone); return date('Y-m-d', $timestamp) === date('Y-m-d', $tomorrow); } @@ -575,12 +575,12 @@ public static function isTomorrow($dateString, $timezone = null) { * * @param int|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object * @param bool $range if true returns a range in Y-m-d format - * @return mixed 1, 2, 3, or 4 quarter of year or array if $range true + * @return int|array 1, 2, 3, or 4 quarter of year or array if $range true * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::toQuarter */ public static function toQuarter($dateString, $range = false) { - $time = self::fromString($dateString); - $date = ceil(date('m', $time) / 3); + $time = static::fromString($dateString); + $date = (int)ceil(date('m', $time) / 3); if ($range === false) { return $date; } @@ -607,7 +607,7 @@ public static function toQuarter($dateString, $range = false) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::toUnix */ public static function toUnix($dateString, $timezone = null) { - return self::fromString($dateString, $timezone); + return static::fromString($dateString, $timezone); } /** @@ -658,7 +658,7 @@ public static function toServer($dateString, $timezone = null, $format = 'Y-m-d * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::toAtom */ public static function toAtom($dateString, $timezone = null) { - return date('Y-m-d\TH:i:s\Z', self::fromString($dateString, $timezone)); + return date('Y-m-d\TH:i:s\Z', static::fromString($dateString, $timezone)); } /** @@ -670,7 +670,7 @@ public static function toAtom($dateString, $timezone = null) { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::toRSS */ public static function toRSS($dateString, $timezone = null) { - $date = self::fromString($dateString, $timezone); + $date = static::fromString($dateString, $timezone); if ($timezone === null) { return date("r", $date); @@ -737,9 +737,9 @@ public static function toRSS($dateString, $timezone = null) { */ public static function timeAgoInWords($dateTime, $options = array()) { $timezone = null; - $accuracies = self::$wordAccuracy; - $format = self::$wordFormat; - $relativeEnd = self::$wordEnd; + $accuracies = static::$wordAccuracy; + $format = static::$wordFormat; + $relativeEnd = static::$wordEnd; $relativeStringPast = __d('cake', '%s ago'); $relativeStringFuture = __d('cake', 'in %s'); $absoluteString = __d('cake', 'on %s'); @@ -784,8 +784,8 @@ public static function timeAgoInWords($dateTime, $options = array()) { unset($options['end'], $options['format']); } - $now = self::fromString(time(), $timezone); - $inSeconds = self::fromString($dateTime, $timezone); + $now = static::fromString(time(), $timezone); + $inSeconds = static::fromString($dateTime, $timezone); $isFuture = ($inSeconds > $now); if ($isFuture) { @@ -801,12 +801,12 @@ public static function timeAgoInWords($dateTime, $options = array()) { return __d('cake', 'just now', 'just now'); } - $isAbsoluteDate = $diff > abs($now - self::fromString($relativeEnd)); + $isAbsoluteDate = $diff > abs($now - static::fromString($relativeEnd)); if ($isAbsoluteDate) { if (strpos($format, '%') === false) { $date = date($format, $inSeconds); } else { - $date = self::_strftime($format, $inSeconds); + $date = static::_strftime($format, $inSeconds); } return sprintf($absoluteString, $date); } @@ -958,9 +958,9 @@ public static function wasWithinLast($timeInterval, $dateString, $timezone = nul $timeInterval = $tmp . ' ' . __d('cake', 'days'); } - $date = self::fromString($dateString, $timezone); - $interval = self::fromString('-' . $timeInterval); - $now = self::fromString('now', $timezone); + $date = static::fromString($dateString, $timezone); + $interval = static::fromString('-' . $timeInterval); + $now = static::fromString('now', $timezone); return $date >= $interval && $date <= $now; } @@ -980,9 +980,9 @@ public static function isWithinNext($timeInterval, $dateString, $timezone = null $timeInterval = $tmp . ' ' . __d('cake', 'days'); } - $date = self::fromString($dateString, $timezone); - $interval = self::fromString('+' . $timeInterval); - $now = self::fromString('now', $timezone); + $date = static::fromString($dateString, $timezone); + $interval = static::fromString('+' . $timeInterval); + $now = static::fromString('now', $timezone); return $date <= $interval && $date >= $now; } @@ -997,7 +997,7 @@ public static function isWithinNext($timeInterval, $dateString, $timezone = null public static function gmt($dateString = null) { $time = time(); if ($dateString) { - $time = self::fromString($dateString); + $time = static::fromString($dateString); } return gmmktime( (int)date('G', $time), @@ -1035,10 +1035,10 @@ public static function gmt($dateString = null) { */ public static function format($date, $format = null, $default = false, $timezone = null) { //Backwards compatible params re-order test - $time = self::fromString($format, $timezone); + $time = static::fromString($format, $timezone); if ($time === false) { - return self::i18nFormat($date, $format, $default, $timezone); + return static::i18nFormat($date, $format, $default, $timezone); } return date($date, $time); } @@ -1055,7 +1055,7 @@ public static function format($date, $format = null, $default = false, $timezone * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#TimeHelper::i18nFormat */ public static function i18nFormat($date, $format = null, $default = false, $timezone = null) { - $date = self::fromString($date, $timezone); + $date = static::fromString($date, $timezone); if ($date === false && $default !== false) { return $default; } @@ -1065,7 +1065,7 @@ public static function i18nFormat($date, $format = null, $default = false, $time if (empty($format)) { $format = '%x'; } - return self::_strftime(self::convertSpecifiers($format, $date), $date); + return static::_strftime(static::convertSpecifiers($format, $date), $date); } /** diff --git a/lib/Cake/Utility/ClassRegistry.php b/lib/Cake/Utility/ClassRegistry.php index 144a0318fea..1208b4401f1 100644 --- a/lib/Cake/Utility/ClassRegistry.php +++ b/lib/Cake/Utility/ClassRegistry.php @@ -91,7 +91,7 @@ public static function getInstance() { * stored in the registry and returned. * @param bool $strict if set to true it will return false if the class was not found instead * of trying to create an AppModel - * @return object instance of ClassName. + * @return $class instance of ClassName. * @throws CakeException when you try to construct an interface or abstract class. */ public static function init($class, $strict = false) { diff --git a/lib/Cake/Utility/Debugger.php b/lib/Cake/Utility/Debugger.php index d35ea1ffc95..ec29a4d6eb9 100644 --- a/lib/Cake/Utility/Debugger.php +++ b/lib/Cake/Utility/Debugger.php @@ -19,7 +19,7 @@ */ App::uses('CakeLog', 'Log'); -App::uses('String', 'Utility'); +App::uses('CakeText', 'Utility'); /** * Provide custom logging and error handling. @@ -91,7 +91,6 @@ class Debugger { /** * Constructor. - * */ public function __construct() { $docRef = ini_get('docref_root'); @@ -174,7 +173,7 @@ public static function getInstance($class = null) { * @link http://book.cakephp.org/2.0/en/development/debugging.html#Debugger::dump */ public static function dump($var, $depth = 3) { - pr(self::exportVar($var, $depth)); + pr(static::exportVar($var, $depth)); } /** @@ -188,8 +187,8 @@ public static function dump($var, $depth = 3) { * @link http://book.cakephp.org/2.0/en/development/debugging.html#Debugger::log */ public static function log($var, $level = LOG_DEBUG, $depth = 3) { - $source = self::trace(array('start' => 1)) . "\n"; - CakeLog::write($level, "\n" . $source . self::exportVar($var, $depth)); + $source = static::trace(array('start' => 1)) . "\n"; + CakeLog::write($level, "\n" . $source . static::exportVar($var, $depth)); } /** @@ -200,7 +199,7 @@ public static function log($var, $level = LOG_DEBUG, $depth = 3) { * @param string $file File on which error occurred * @param int $line Line that triggered the error * @param array $context Context - * @return bool true if error was handled + * @return bool|null True if error was handled, otherwise null. * @deprecated 3.0.0 Will be removed in 3.0. This function is superseded by Debugger::outputError(). */ public static function showError($code, $description, $file = null, $line = null, $context = null) { @@ -217,7 +216,7 @@ public static function showError($code, $description, $file = null, $line = null if (!in_array($info, $self->errors)) { $self->errors[] = $info; } else { - return; + return null; } switch ($code) { @@ -247,7 +246,7 @@ public static function showError($code, $description, $file = null, $line = null $level = LOG_NOTICE; break; default: - return; + return null; } $data = compact( @@ -334,10 +333,10 @@ public static function trace($options = array()) { } else { $tpl = $self->_templates['base']['traceLine']; } - $trace['path'] = self::trimPath($trace['file']); + $trace['path'] = static::trimPath($trace['file']); $trace['reference'] = $reference; unset($trace['object'], $trace['args']); - $back[] = String::insert($tpl, $trace, array('before' => '{:', 'after' => '}')); + $back[] = CakeText::insert($tpl, $trace, array('before' => '{:', 'after' => '}')); } } @@ -408,7 +407,7 @@ public static function excerpt($file, $line, $context = 2) { if (!isset($data[$i])) { continue; } - $string = str_replace(array("\r\n", "\n"), "", self::_highlight($data[$i])); + $string = str_replace(array("\r\n", "\n"), "", static::_highlight($data[$i])); if ($i == $line) { $lines[] = '' . $string . ''; } else { @@ -468,7 +467,7 @@ protected static function _highlight($str) { * @link http://book.cakephp.org/2.0/en/development/debugging.html#Debugger::exportVar */ public static function exportVar($var, $depth = 3) { - return self::_export($var, $depth, 0); + return static::_export($var, $depth, 0); } /** @@ -480,7 +479,7 @@ public static function exportVar($var, $depth = 3) { * @return string The dumped variable. */ protected static function _export($var, $depth, $indent) { - switch (self::getType($var)) { + switch (static::getType($var)) { case 'boolean': return ($var) ? 'true' : 'false'; case 'integer': @@ -493,7 +492,7 @@ protected static function _export($var, $depth, $indent) { } return "'" . $var . "'"; case 'array': - return self::_array($var, $depth - 1, $indent + 1); + return static::_array($var, $depth - 1, $indent + 1); case 'resource': return strtolower(gettype($var)); case 'null': @@ -501,7 +500,7 @@ protected static function _export($var, $depth, $indent) { case 'unknown': return 'unknown'; default: - return self::_object($var, $depth - 1, $indent + 1); + return static::_object($var, $depth - 1, $indent + 1); } } @@ -550,9 +549,9 @@ protected static function _array(array $var, $depth, $indent) { if ($key === 'GLOBALS' && is_array($val) && isset($val['GLOBALS'])) { $val = '[recursion]'; } elseif ($val !== $var) { - $val = self::_export($val, $depth, $indent); + $val = static::_export($val, $depth, $indent); } - $vars[] = $break . self::exportVar($key) . + $vars[] = $break . static::exportVar($key) . ' => ' . $val; } @@ -583,7 +582,7 @@ protected static function _object($var, $depth, $indent) { $break = "\n" . str_repeat("\t", $indent); $objectVars = get_object_vars($var); foreach ($objectVars as $key => $value) { - $value = self::_export($value, $depth - 1, $indent); + $value = static::_export($value, $depth - 1, $indent); $props[] = "$key => " . $value; } @@ -600,7 +599,7 @@ protected static function _object($var, $depth, $indent) { $reflectionProperty->setAccessible(true); $property = $reflectionProperty->getValue($var); - $value = self::_export($property, $depth - 1, $indent); + $value = static::_export($property, $depth - 1, $indent); $key = $reflectionProperty->name; $props[] = sprintf('[%s] %s => %s', $visibility, $key, $value); } @@ -637,7 +636,7 @@ public static function outputAs($format = null) { * * `Debugger::addFormat('custom', $data);` * - * Where $data is an array of strings that use String::insert() variable + * Where $data is an array of strings that use CakeText::insert() variable * replacement. The template vars should be in a `{:id}` style. * An error formatter can have the following keys: * @@ -775,7 +774,7 @@ public function outputError($data) { if (isset($tpl['links'])) { foreach ($tpl['links'] as $key => $val) { - $links[$key] = String::insert($val, $data, $insertOpts); + $links[$key] = CakeText::insert($val, $data, $insertOpts); } } @@ -791,14 +790,14 @@ public function outputError($data) { if (is_array($value)) { $value = implode("\n", $value); } - $info .= String::insert($tpl[$key], array($key => $value) + $data, $insertOpts); + $info .= CakeText::insert($tpl[$key], array($key => $value) + $data, $insertOpts); } $links = implode(' ', $links); if (isset($tpl['callback']) && is_callable($tpl['callback'])) { return call_user_func($tpl['callback'], $data, compact('links', 'info')); } - echo String::insert($tpl['error'], compact('links', 'info') + $data, $insertOpts); + echo CakeText::insert($tpl['error'], compact('links', 'info') + $data, $insertOpts); } /** diff --git a/lib/Cake/Utility/Folder.php b/lib/Cake/Utility/Folder.php index 088723cb2e8..20058f17449 100644 --- a/lib/Cake/Utility/Folder.php +++ b/lib/Cake/Utility/Folder.php @@ -64,7 +64,7 @@ class Folder { public $sort = false; /** - * Mode to be used on create. Does nothing on windows platforms. + * Mode to be used on create. Does nothing on Windows platforms. * * @var int * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::$mode @@ -263,7 +263,7 @@ protected function _findRecursive($pattern, $sort = false) { * Returns true if given $path is a Windows path. * * @param string $path Path to check - * @return bool true if windows path, false otherwise + * @return bool true if Windows path, false otherwise * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::isWindowsPath */ public static function isWindowsPath($path) { @@ -285,7 +285,7 @@ public static function isAbsolute($path) { return $path[0] === '/' || preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) === '\\\\' || - self::isRegisteredStreamWrapper($path); + static::isRegisteredStreamWrapper($path); } /** @@ -525,8 +525,8 @@ public function create($pathname, $mode = false) { return true; } - if (!self::isAbsolute($pathname)) { - $pathname = self::addPathElement($this->pwd(), $pathname); + if (!static::isAbsolute($pathname)) { + $pathname = static::addPathElement($this->pwd(), $pathname); } if (!$mode) { diff --git a/lib/Cake/Utility/Hash.php b/lib/Cake/Utility/Hash.php index 70485195b30..451e8f13e95 100644 --- a/lib/Cake/Utility/Hash.php +++ b/lib/Cake/Utility/Hash.php @@ -14,7 +14,7 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -App::uses('String', 'Utility'); +App::uses('CakeText', 'Utility'); /** * Library of array functions for manipulating and extracting data @@ -43,7 +43,7 @@ class Hash { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::get */ public static function get(array $data, $path, $default = null) { - if (empty($data)) { + if (empty($data) || $path === '' || $path === null) { return $default; } if (is_string($path) || is_numeric($path)) { @@ -55,7 +55,6 @@ public static function get(array $data, $path, $default = null) { $path )); } - $parts = $path; } @@ -77,6 +76,7 @@ public static function get(array $data, $path, $default = null) { * * - `{n}` Matches any numeric key, or integer. * - `{s}` Matches any string key. + * - `{*}` Matches any value. * - `Foo` Matches any key with the exact same value. * * There are a number of attribute operators: @@ -106,13 +106,13 @@ public static function extract(array $data, $path) { // Simple paths. if (!preg_match('/[{\[]/', $path)) { - return (array)self::get($data, $path); + return (array)static::get($data, $path); } if (strpos($path, '[') === false) { $tokens = explode('.', $path); } else { - $tokens = String::tokenize($path, '.', '[', ']'); + $tokens = CakeText::tokenize($path, '.', '[', ']'); } $_key = '__set_item__'; @@ -122,11 +122,11 @@ public static function extract(array $data, $path) { foreach ($tokens as $token) { $next = array(); - list($token, $conditions) = self::_splitConditions($token); + list($token, $conditions) = static::_splitConditions($token); foreach ($context[$_key] as $item) { foreach ((array)$item as $k => $v) { - if (self::_matchToken($k, $token)) { + if (static::_matchToken($k, $token)) { $next[] = $v; } } @@ -136,7 +136,7 @@ public static function extract(array $data, $path) { if ($conditions) { $filter = array(); foreach ($next as $item) { - if (is_array($item) && self::_matches($item, $conditions)) { + if (is_array($item) && static::_matches($item, $conditions)) { $filter[] = $item; } } @@ -172,16 +172,16 @@ protected static function _splitConditions($token) { * @return bool */ protected static function _matchToken($key, $token) { - if ($token === '{n}') { - return is_numeric($key); - } - if ($token === '{s}') { - return is_string($key); - } - if (is_numeric($token)) { - return ($key == $token); + switch ($token) { + case '{n}': + return is_numeric($key); + case '{s}': + return is_string($key); + case '{*}': + return true; + default: + return is_numeric($token) ? ($key == $token) : $key === $token; } - return ($key === $token); } /** @@ -250,7 +250,7 @@ protected static function _matches(array $data, $selector) { * * @param array $data The data to insert into. * @param string $path The path to insert at. - * @param array $values The values to insert. + * @param mixed $values The values to insert. * @return array The data with $values inserted. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::insert */ @@ -258,26 +258,26 @@ public static function insert(array $data, $path, $values = null) { if (strpos($path, '[') === false) { $tokens = explode('.', $path); } else { - $tokens = String::tokenize($path, '.', '[', ']'); + $tokens = CakeText::tokenize($path, '.', '[', ']'); } if (strpos($path, '{') === false && strpos($path, '[') === false) { - return self::_simpleOp('insert', $data, $tokens, $values); + return static::_simpleOp('insert', $data, $tokens, $values); } $token = array_shift($tokens); $nextPath = implode('.', $tokens); - list($token, $conditions) = self::_splitConditions($token); + list($token, $conditions) = static::_splitConditions($token); foreach ($data as $k => $v) { - if (self::_matchToken($k, $token)) { - if ($conditions && self::_matches($v, $conditions)) { + if (static::_matchToken($k, $token)) { + if ($conditions && static::_matches($v, $conditions)) { $data[$k] = array_merge($v, $values); continue; } if (!$conditions) { - $data[$k] = self::insert($v, $nextPath, $values); + $data[$k] = static::insert($v, $nextPath, $values); } } } @@ -341,26 +341,26 @@ public static function remove(array $data, $path) { if (strpos($path, '[') === false) { $tokens = explode('.', $path); } else { - $tokens = String::tokenize($path, '.', '[', ']'); + $tokens = CakeText::tokenize($path, '.', '[', ']'); } if (strpos($path, '{') === false && strpos($path, '[') === false) { - return self::_simpleOp('remove', $data, $tokens); + return static::_simpleOp('remove', $data, $tokens); } $token = array_shift($tokens); $nextPath = implode('.', $tokens); - list($token, $conditions) = self::_splitConditions($token); + list($token, $conditions) = static::_splitConditions($token); foreach ($data as $k => $v) { - $match = self::_matchToken($k, $token); + $match = static::_matchToken($k, $token); if ($match && is_array($v)) { - if ($conditions && self::_matches($v, $conditions)) { + if ($conditions && static::_matches($v, $conditions)) { unset($data[$k]); continue; } - $data[$k] = self::remove($v, $nextPath); + $data[$k] = static::remove($v, $nextPath); if (empty($data[$k])) { unset($data[$k]); } @@ -392,9 +392,9 @@ public static function combine(array $data, $keyPath, $valuePath = null, $groupP if (is_array($keyPath)) { $format = array_shift($keyPath); - $keys = self::format($data, $keyPath, $format); + $keys = static::format($data, $keyPath, $format); } else { - $keys = self::extract($data, $keyPath); + $keys = static::extract($data, $keyPath); } if (empty($keys)) { return array(); @@ -402,9 +402,9 @@ public static function combine(array $data, $keyPath, $valuePath = null, $groupP if (!empty($valuePath) && is_array($valuePath)) { $format = array_shift($valuePath); - $vals = self::format($data, $valuePath, $format); + $vals = static::format($data, $valuePath, $format); } elseif (!empty($valuePath)) { - $vals = self::extract($data, $valuePath); + $vals = static::extract($data, $valuePath); } if (empty($vals)) { $vals = array_fill(0, count($keys), null); @@ -418,7 +418,7 @@ public static function combine(array $data, $keyPath, $valuePath = null, $groupP } if ($groupPath !== null) { - $group = self::extract($data, $groupPath); + $group = static::extract($data, $groupPath); if (!empty($group)) { $c = count($keys); for ($i = 0; $i < $c; $i++) { @@ -465,11 +465,11 @@ public static function format(array $data, array $paths, $format) { $count = count($paths); if (!$count) { - return; + return null; } for ($i = 0; $i < $count; $i++) { - $extracted[] = self::extract($data, $paths[$i]); + $extracted[] = static::extract($data, $paths[$i]); } $out = array(); $data = $extracted; @@ -539,7 +539,7 @@ public static function contains(array $data, array $needle) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::check */ public static function check(array $data, $path) { - $results = self::extract($data, $path); + $results = static::extract($data, $path); if (!is_array($results)) { return false; } @@ -551,14 +551,14 @@ public static function check(array $data, $path) { * * @param array $data Either an array to filter, or value when in callback * @param callable $callback A function to filter the data with. Defaults to - * `self::_filter()` Which strips out all non-zero empty values. + * `static::_filter()` Which strips out all non-zero empty values. * @return array Filtered array * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::filter */ public static function filter(array $data, $callback = array('self', '_filter')) { foreach ($data as $k => $v) { if (is_array($v)) { - $data[$k] = self::filter($v, $callback); + $data[$k] = static::filter($v, $callback); } } return array_filter($data, $callback); @@ -759,14 +759,14 @@ public static function dimensions(array $data) { * @return int The maximum number of dimensions in $data * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::maxDimensions */ - public static function maxDimensions(array $data) { + public static function maxDimensions($data) { $depth = array(); if (is_array($data) && reset($data) !== false) { foreach ($data as $value) { - $depth[] = self::dimensions((array)$value) + 1; + $depth[] = static::maxDimensions($value) + 1; } } - return max($depth); + return empty($depth) ? 0 : max($depth); } /** @@ -780,7 +780,7 @@ public static function maxDimensions(array $data) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::map */ public static function map(array $data, $path, $function) { - $values = (array)self::extract($data, $path); + $values = (array)static::extract($data, $path); return array_map($function, $values); } @@ -794,7 +794,7 @@ public static function map(array $data, $path, $function) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::reduce */ public static function reduce(array $data, $path, $function) { - $values = (array)self::extract($data, $path); + $values = (array)static::extract($data, $path); return array_reduce($values, $function); } @@ -819,7 +819,7 @@ public static function reduce(array $data, $path, $function) { * @return mixed The results of the applied method. */ public static function apply(array $data, $path, $function) { - $values = (array)self::extract($data, $path); + $values = (array)static::extract($data, $path); return call_user_func($function, $values); } @@ -855,7 +855,7 @@ public static function sort(array $data, $path, $dir = 'asc', $type = 'regular') if ($numeric) { $data = array_values($data); } - $sortValues = self::extract($data, $path); + $sortValues = static::extract($data, $path); $sortCount = count($sortValues); $dataCount = count($data); @@ -864,9 +864,9 @@ public static function sort(array $data, $path, $dir = 'asc', $type = 'regular') if ($sortCount < $dataCount) { $sortValues = array_pad($sortValues, $dataCount, null); } - $result = self::_squash($sortValues); - $keys = self::extract($result, '{n}.id'); - $values = self::extract($result, '{n}.value'); + $result = static::_squash($sortValues); + $keys = static::extract($result, '{n}.id'); + $values = static::extract($result, '{n}.value'); $dir = strtolower($dir); $type = strtolower($type); @@ -921,7 +921,7 @@ protected static function _squash($data, $key = null) { $id = $key; } if (is_array($r) && !empty($r)) { - $stack = array_merge($stack, self::_squash($r, $id)); + $stack = array_merge($stack, static::_squash($r, $id)); } else { $stack[] = array('id' => $id, 'value' => $r); } @@ -977,7 +977,7 @@ public static function mergeDiff(array $data, $compare) { if (!array_key_exists($key, $data)) { $data[$key] = $value; } elseif (is_array($value)) { - $data[$key] = self::mergeDiff($data[$key], $compare[$key]); + $data[$key] = static::mergeDiff($data[$key], $compare[$key]); } } return $data; @@ -1051,7 +1051,7 @@ public static function nest(array $data, $options = array()) { ); $return = $idMap = array(); - $ids = self::extract($data, $options['idPath']); + $ids = static::extract($data, $options['idPath']); $idKeys = explode('.', $options['idPath']); array_shift($idKeys); @@ -1062,8 +1062,8 @@ public static function nest(array $data, $options = array()) { foreach ($data as $result) { $result[$options['children']] = array(); - $id = self::get($result, $idKeys); - $parentId = self::get($result, $parentKeys); + $id = static::get($result, $idKeys); + $parentId = static::get($result, $parentKeys); if (isset($idMap[$id][$options['children']])) { $idMap[$id] = array_merge($result, (array)$idMap[$id]); @@ -1086,12 +1086,12 @@ public static function nest(array $data, $options = array()) { if ($options['root']) { $root = $options['root']; } else { - $root = self::get($return[0], $parentKeys); + $root = static::get($return[0], $parentKeys); } foreach ($return as $i => $result) { - $id = self::get($result, $idKeys); - $parentId = self::get($result, $parentKeys); + $id = static::get($result, $idKeys); + $parentId = static::get($result, $parentKeys); if ($id !== $root && $parentId != $root) { unset($return[$i]); } diff --git a/lib/Cake/Utility/Inflector.php b/lib/Cake/Utility/Inflector.php index 6e7b4d4934b..40b9de2810e 100644 --- a/lib/Cake/Utility/Inflector.php +++ b/lib/Cake/Utility/Inflector.php @@ -47,7 +47,7 @@ class Inflector { '/(? '\1en', '/(c)hild$/i' => '\1hildren', '/(buffal|tomat)o$/i' => '\1\2oes', - '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin)us$/i' => '\1i', '/us$/i' => 'uses', '/(alias)$/i' => '\1es', '/(ax|cris|test)is$/i' => '\1es', @@ -107,7 +107,8 @@ class Inflector { 'hero' => 'heroes', 'tooth' => 'teeth', 'goose' => 'geese', - 'foot' => 'feet' + 'foot' => 'feet', + 'sieve' => 'sieves' ) ); @@ -273,13 +274,13 @@ protected static function _cache($type, $key, $value = false) { $key = '_' . $key; $type = '_' . $type; if ($value !== false) { - self::$_cache[$type][$key] = $value; + static::$_cache[$type][$key] = $value; return $value; } - if (!isset(self::$_cache[$type][$key])) { + if (!isset(static::$_cache[$type][$key])) { return false; } - return self::$_cache[$type][$key]; + return static::$_cache[$type][$key]; } /** @@ -289,13 +290,13 @@ protected static function _cache($type, $key, $value = false) { * @return void */ public static function reset() { - if (empty(self::$_initialState)) { - self::$_initialState = get_class_vars('Inflector'); + if (empty(static::$_initialState)) { + static::$_initialState = get_class_vars('Inflector'); return; } - foreach (self::$_initialState as $key => $val) { + foreach (static::$_initialState as $key => $val) { if ($key !== '_initialState') { - self::${$key} = $val; + static::${$key} = $val; } } } @@ -327,9 +328,9 @@ public static function rules($type, $rules, $reset = false) { switch ($type) { case 'transliteration': if ($reset) { - self::$_transliteration = $rules; + static::$_transliteration = $rules; } else { - self::$_transliteration = $rules + self::$_transliteration; + static::$_transliteration = $rules + static::$_transliteration; } break; @@ -337,26 +338,26 @@ public static function rules($type, $rules, $reset = false) { foreach ($rules as $rule => $pattern) { if (is_array($pattern)) { if ($reset) { - self::${$var}[$rule] = $pattern; + static::${$var}[$rule] = $pattern; } else { if ($rule === 'uninflected') { - self::${$var}[$rule] = array_merge($pattern, self::${$var}[$rule]); + static::${$var}[$rule] = array_merge($pattern, static::${$var}[$rule]); } else { - self::${$var}[$rule] = $pattern + self::${$var}[$rule]; + static::${$var}[$rule] = $pattern + static::${$var}[$rule]; } } - unset($rules[$rule], self::${$var}['cache' . ucfirst($rule)]); - if (isset(self::${$var}['merged'][$rule])) { - unset(self::${$var}['merged'][$rule]); + unset($rules[$rule], static::${$var}['cache' . ucfirst($rule)]); + if (isset(static::${$var}['merged'][$rule])) { + unset(static::${$var}['merged'][$rule]); } if ($type === 'plural') { - self::$_cache['pluralize'] = self::$_cache['tableize'] = array(); + static::$_cache['pluralize'] = static::$_cache['tableize'] = array(); } elseif ($type === 'singular') { - self::$_cache['singularize'] = array(); + static::$_cache['singularize'] = array(); } } } - self::${$var}['rules'] = $rules + self::${$var}['rules']; + static::${$var}['rules'] = $rules + static::${$var}['rules']; } } @@ -368,37 +369,39 @@ public static function rules($type, $rules, $reset = false) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html#Inflector::pluralize */ public static function pluralize($word) { - if (isset(self::$_cache['pluralize'][$word])) { - return self::$_cache['pluralize'][$word]; + if (isset(static::$_cache['pluralize'][$word])) { + return static::$_cache['pluralize'][$word]; } - if (!isset(self::$_plural['merged']['irregular'])) { - self::$_plural['merged']['irregular'] = self::$_plural['irregular']; + if (!isset(static::$_plural['merged']['irregular'])) { + static::$_plural['merged']['irregular'] = static::$_plural['irregular']; } - if (!isset(self::$_plural['merged']['uninflected'])) { - self::$_plural['merged']['uninflected'] = array_merge(self::$_plural['uninflected'], self::$_uninflected); + if (!isset(static::$_plural['merged']['uninflected'])) { + static::$_plural['merged']['uninflected'] = array_merge(static::$_plural['uninflected'], static::$_uninflected); } - if (!isset(self::$_plural['cacheUninflected']) || !isset(self::$_plural['cacheIrregular'])) { - self::$_plural['cacheUninflected'] = '(?:' . implode('|', self::$_plural['merged']['uninflected']) . ')'; - self::$_plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$_plural['merged']['irregular'])) . ')'; + if (!isset(static::$_plural['cacheUninflected']) || !isset(static::$_plural['cacheIrregular'])) { + static::$_plural['cacheUninflected'] = '(?:' . implode('|', static::$_plural['merged']['uninflected']) . ')'; + static::$_plural['cacheIrregular'] = '(?:' . implode('|', array_keys(static::$_plural['merged']['irregular'])) . ')'; } - if (preg_match('/(.*)\\b(' . self::$_plural['cacheIrregular'] . ')$/i', $word, $regs)) { - self::$_cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$_plural['merged']['irregular'][strtolower($regs[2])], 1); - return self::$_cache['pluralize'][$word]; + if (preg_match('/(.*?(?:\\b|_))(' . static::$_plural['cacheIrregular'] . ')$/i', $word, $regs)) { + static::$_cache['pluralize'][$word] = $regs[1] . + substr($regs[2], 0, 1) . + substr(static::$_plural['merged']['irregular'][strtolower($regs[2])], 1); + return static::$_cache['pluralize'][$word]; } - if (preg_match('/^(' . self::$_plural['cacheUninflected'] . ')$/i', $word, $regs)) { - self::$_cache['pluralize'][$word] = $word; + if (preg_match('/^(' . static::$_plural['cacheUninflected'] . ')$/i', $word, $regs)) { + static::$_cache['pluralize'][$word] = $word; return $word; } - foreach (self::$_plural['rules'] as $rule => $replacement) { + foreach (static::$_plural['rules'] as $rule => $replacement) { if (preg_match($rule, $word)) { - self::$_cache['pluralize'][$word] = preg_replace($rule, $replacement, $word); - return self::$_cache['pluralize'][$word]; + static::$_cache['pluralize'][$word] = preg_replace($rule, $replacement, $word); + return static::$_cache['pluralize'][$word]; } } } @@ -411,46 +414,48 @@ public static function pluralize($word) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html#Inflector::singularize */ public static function singularize($word) { - if (isset(self::$_cache['singularize'][$word])) { - return self::$_cache['singularize'][$word]; + if (isset(static::$_cache['singularize'][$word])) { + return static::$_cache['singularize'][$word]; } - if (!isset(self::$_singular['merged']['uninflected'])) { - self::$_singular['merged']['uninflected'] = array_merge( - self::$_singular['uninflected'], - self::$_uninflected + if (!isset(static::$_singular['merged']['uninflected'])) { + static::$_singular['merged']['uninflected'] = array_merge( + static::$_singular['uninflected'], + static::$_uninflected ); } - if (!isset(self::$_singular['merged']['irregular'])) { - self::$_singular['merged']['irregular'] = array_merge( - self::$_singular['irregular'], - array_flip(self::$_plural['irregular']) + if (!isset(static::$_singular['merged']['irregular'])) { + static::$_singular['merged']['irregular'] = array_merge( + static::$_singular['irregular'], + array_flip(static::$_plural['irregular']) ); } - if (!isset(self::$_singular['cacheUninflected']) || !isset(self::$_singular['cacheIrregular'])) { - self::$_singular['cacheUninflected'] = '(?:' . implode('|', self::$_singular['merged']['uninflected']) . ')'; - self::$_singular['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$_singular['merged']['irregular'])) . ')'; + if (!isset(static::$_singular['cacheUninflected']) || !isset(static::$_singular['cacheIrregular'])) { + static::$_singular['cacheUninflected'] = '(?:' . implode('|', static::$_singular['merged']['uninflected']) . ')'; + static::$_singular['cacheIrregular'] = '(?:' . implode('|', array_keys(static::$_singular['merged']['irregular'])) . ')'; } - if (preg_match('/(.*)\\b(' . self::$_singular['cacheIrregular'] . ')$/i', $word, $regs)) { - self::$_cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$_singular['merged']['irregular'][strtolower($regs[2])], 1); - return self::$_cache['singularize'][$word]; + if (preg_match('/(.*?(?:\\b|_))(' . static::$_singular['cacheIrregular'] . ')$/i', $word, $regs)) { + static::$_cache['singularize'][$word] = $regs[1] . + substr($regs[2], 0, 1) . + substr(static::$_singular['merged']['irregular'][strtolower($regs[2])], 1); + return static::$_cache['singularize'][$word]; } - if (preg_match('/^(' . self::$_singular['cacheUninflected'] . ')$/i', $word, $regs)) { - self::$_cache['singularize'][$word] = $word; + if (preg_match('/^(' . static::$_singular['cacheUninflected'] . ')$/i', $word, $regs)) { + static::$_cache['singularize'][$word] = $word; return $word; } - foreach (self::$_singular['rules'] as $rule => $replacement) { + foreach (static::$_singular['rules'] as $rule => $replacement) { if (preg_match($rule, $word)) { - self::$_cache['singularize'][$word] = preg_replace($rule, $replacement, $word); - return self::$_cache['singularize'][$word]; + static::$_cache['singularize'][$word] = preg_replace($rule, $replacement, $word); + return static::$_cache['singularize'][$word]; } } - self::$_cache['singularize'][$word] = $word; + static::$_cache['singularize'][$word] = $word; return $word; } @@ -462,9 +467,9 @@ public static function singularize($word) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html#Inflector::camelize */ public static function camelize($lowerCaseAndUnderscoredWord) { - if (!($result = self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) { + if (!($result = static::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) { $result = str_replace(' ', '', Inflector::humanize($lowerCaseAndUnderscoredWord)); - self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result); + static::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result); } return $result; } @@ -477,9 +482,10 @@ public static function camelize($lowerCaseAndUnderscoredWord) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html#Inflector::underscore */ public static function underscore($camelCasedWord) { - if (!($result = self::_cache(__FUNCTION__, $camelCasedWord))) { - $result = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord)); - self::_cache(__FUNCTION__, $camelCasedWord, $result); + if (!($result = static::_cache(__FUNCTION__, $camelCasedWord))) { + $underscoredWord = preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord); + $result = mb_strtolower($underscoredWord); + static::_cache(__FUNCTION__, $camelCasedWord, $result); } return $result; } @@ -493,9 +499,13 @@ public static function underscore($camelCasedWord) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html#Inflector::humanize */ public static function humanize($lowerCaseAndUnderscoredWord) { - if (!($result = self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) { - $result = ucwords(str_replace('_', ' ', $lowerCaseAndUnderscoredWord)); - self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result); + if (!($result = static::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) { + $result = explode(' ', str_replace('_', ' ', $lowerCaseAndUnderscoredWord)); + foreach ($result as &$word) { + $word = mb_strtoupper(mb_substr($word, 0, 1)) . mb_substr($word, 1); + } + $result = implode(' ', $result); + static::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result); } return $result; } @@ -508,9 +518,9 @@ public static function humanize($lowerCaseAndUnderscoredWord) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html#Inflector::tableize */ public static function tableize($className) { - if (!($result = self::_cache(__FUNCTION__, $className))) { + if (!($result = static::_cache(__FUNCTION__, $className))) { $result = Inflector::pluralize(Inflector::underscore($className)); - self::_cache(__FUNCTION__, $className, $result); + static::_cache(__FUNCTION__, $className, $result); } return $result; } @@ -523,9 +533,9 @@ public static function tableize($className) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html#Inflector::classify */ public static function classify($tableName) { - if (!($result = self::_cache(__FUNCTION__, $tableName))) { + if (!($result = static::_cache(__FUNCTION__, $tableName))) { $result = Inflector::camelize(Inflector::singularize($tableName)); - self::_cache(__FUNCTION__, $tableName, $result); + static::_cache(__FUNCTION__, $tableName, $result); } return $result; } @@ -538,11 +548,11 @@ public static function classify($tableName) { * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html#Inflector::variable */ public static function variable($string) { - if (!($result = self::_cache(__FUNCTION__, $string))) { + if (!($result = static::_cache(__FUNCTION__, $string))) { $camelized = Inflector::camelize(Inflector::underscore($string)); $replace = strtolower(substr($camelized, 0, 1)); $result = preg_replace('/\\w/', $replace, $camelized, 1); - self::_cache(__FUNCTION__, $string, $result); + static::_cache(__FUNCTION__, $string, $result); } return $result; } @@ -565,7 +575,7 @@ public static function slug($string, $replacement = '_') { sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '', ); - $map = self::$_transliteration + $merge; + $map = static::$_transliteration + $merge; return preg_replace(array_keys($map), array_values($map), $string); } diff --git a/lib/Cake/Utility/ObjectCollection.php b/lib/Cake/Utility/ObjectCollection.php index 4bbb1a3ea41..49e4ae9f89c 100644 --- a/lib/Cake/Utility/ObjectCollection.php +++ b/lib/Cake/Utility/ObjectCollection.php @@ -176,6 +176,7 @@ public function __isset($name) { public function enable($name, $prioritize = true) { $enabled = false; foreach ((array)$name as $object) { + list(, $object) = pluginSplit($object); if (isset($this->_loaded[$object]) && !isset($this->_enabled[$object])) { $priority = $this->defaultPriority; if (isset($this->_loaded[$object]->settings['priority'])) { @@ -219,6 +220,7 @@ public function setPriority($name, $priority = null) { $name = array($name => $priority); } foreach ($name as $object => $objectPriority) { + list(, $object) = pluginSplit($object); if (isset($this->_loaded[$object])) { if ($objectPriority === null) { $objectPriority = $this->defaultPriority; @@ -241,6 +243,7 @@ public function setPriority($name, $priority = null) { */ public function disable($name) { foreach ((array)$name as $object) { + list(, $object) = pluginSplit($object); unset($this->_enabled[$object]); } } @@ -255,6 +258,7 @@ public function disable($name) { */ public function enabled($name = null) { if (!empty($name)) { + list(, $name) = pluginSplit($name); return isset($this->_enabled[$name]); } return array_keys($this->_enabled); @@ -283,6 +287,7 @@ public function attached($name = null) { */ public function loaded($name = null) { if (!empty($name)) { + list(, $name) = pluginSplit($name); return isset($this->_loaded[$name]); } return array_keys($this->_loaded); diff --git a/lib/Cake/Utility/Security.php b/lib/Cake/Utility/Security.php index 7d3afa8f4f1..6ad3c01c14d 100644 --- a/lib/Cake/Utility/Security.php +++ b/lib/Cake/Utility/Security.php @@ -16,7 +16,7 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -App::uses('String', 'Utility'); +App::uses('CakeText', 'Utility'); /** * Security Library contains utility methods related to security @@ -63,7 +63,7 @@ public static function inactiveMins() { * @return string Hash */ public static function generateAuthKey() { - return Security::hash(String::uuid()); + return Security::hash(CakeText::uuid()); } /** @@ -105,12 +105,12 @@ public static function validateAuthKey($authKey) { */ public static function hash($string, $type = null, $salt = false) { if (empty($type)) { - $type = self::$hashType; + $type = static::$hashType; } $type = strtolower($type); if ($type === 'blowfish') { - return self::_crypt($string, $salt); + return static::_crypt($string, $salt); } if ($salt) { if (!is_string($salt)) { @@ -145,7 +145,7 @@ public static function hash($string, $type = null, $salt = false) { * @see Security::hash() */ public static function setHash($hash) { - self::$hashType = $hash; + static::$hashType = $hash; } /** @@ -163,7 +163,7 @@ public static function setCost($cost) { ), E_USER_WARNING); return null; } - self::$hashCost = $cost; + static::$hashCost = $cost; } /** @@ -273,8 +273,8 @@ protected static function _salt($length = 22) { */ protected static function _crypt($password, $salt = false) { if ($salt === false) { - $salt = self::_salt(22); - $salt = vsprintf('$2a$%02d$%s', array(self::$hashCost, $salt)); + $salt = static::_salt(22); + $salt = vsprintf('$2a$%02d$%s', array(static::$hashCost, $salt)); } $invalidCipher = ( @@ -307,7 +307,7 @@ protected static function _crypt($password, $salt = false) { * @throws CakeException On invalid data or key. */ public static function encrypt($plain, $key, $hmacSalt = null) { - self::_checkKey($key, 'encrypt()'); + static::_checkKey($key, 'encrypt()'); if ($hmacSalt === null) { $hmacSalt = Configure::read('Security.salt'); @@ -350,7 +350,7 @@ protected static function _checkKey($key, $method) { * @throws CakeException On invalid data or key. */ public static function decrypt($cipher, $key, $hmacSalt = null) { - self::_checkKey($key, 'decrypt()'); + static::_checkKey($key, 'decrypt()'); if (empty($cipher)) { throw new CakeException(__d('cake_dev', 'The data to decrypt cannot be empty.')); } diff --git a/lib/Cake/Utility/Set.php b/lib/Cake/Utility/Set.php index 5d05e0c8b26..10750c1c62e 100644 --- a/lib/Cake/Utility/Set.php +++ b/lib/Cake/Utility/Set.php @@ -16,7 +16,7 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -App::uses('String', 'Utility'); +App::uses('CakeText', 'Utility'); App::uses('Hash', 'Utility'); /** @@ -228,7 +228,7 @@ public static function enum($select, $list = null) { * @param array $data Source array from which to extract the data * @param string $format Format string into which values will be inserted, see sprintf() * @param array $keys An array containing one or more Set::extract()-style key paths - * @return array An array of strings extracted from $keys and formatted with $format + * @return array|null An array of strings extracted from $keys and formatted with $format, otherwise null. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::format */ public static function format($data, $format, $keys) { @@ -236,7 +236,7 @@ public static function format($data, $format, $keys) { $count = count($keys); if (!$count) { - return; + return null; } for ($i = 0; $i < $count; $i++) { @@ -549,7 +549,7 @@ public static function classicExtract($data, $path = null) { return null; } if (is_string($path) && strpos($path, '{') !== false) { - $path = String::tokenize($path, '.', '{', '}'); + $path = CakeText::tokenize($path, '.', '{', '}'); } elseif (is_string($path)) { $path = explode('.', $path); } diff --git a/lib/Cake/Utility/String.php b/lib/Cake/Utility/String.php index 42b8aab6d36..bacb45f4140 100644 --- a/lib/Cake/Utility/String.php +++ b/lib/Cake/Utility/String.php @@ -15,695 +15,13 @@ * @since CakePHP(tm) v 1.2.0.5551 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ +App::uses('CakeText', 'Utility'); /** * String handling methods. * - * @package Cake.Utility - */ -class String { - -/** - * Generate a random UUID - * - * @see http://www.ietf.org/rfc/rfc4122.txt - * @return RFC 4122 UUID - */ - public static function uuid() { - $node = env('SERVER_ADDR'); - - if (strpos($node, ':') !== false) { - if (substr_count($node, '::')) { - $node = str_replace( - '::', str_repeat(':0000', 8 - substr_count($node, ':')) . ':', $node - ); - } - $node = explode(':', $node); - $ipSix = ''; - - foreach ($node as $id) { - $ipSix .= str_pad(base_convert($id, 16, 2), 16, 0, STR_PAD_LEFT); - } - $node = base_convert($ipSix, 2, 10); - - if (strlen($node) < 38) { - $node = null; - } else { - $node = crc32($node); - } - } elseif (empty($node)) { - $host = env('HOSTNAME'); - - if (empty($host)) { - $host = env('HOST'); - } - - if (!empty($host)) { - $ip = gethostbyname($host); - - if ($ip === $host) { - $node = crc32($host); - } else { - $node = ip2long($ip); - } - } - } elseif ($node !== '127.0.0.1') { - $node = ip2long($node); - } else { - $node = null; - } - - if (empty($node)) { - $node = crc32(Configure::read('Security.salt')); - } - - if (function_exists('hphp_get_thread_id')) { - $pid = hphp_get_thread_id(); - } elseif (function_exists('zend_thread_id')) { - $pid = zend_thread_id(); - } else { - $pid = getmypid(); - } - - if (!$pid || $pid > 65535) { - $pid = mt_rand(0, 0xfff) | 0x4000; - } - - list($timeMid, $timeLow) = explode(' ', microtime()); - return sprintf( - "%08x-%04x-%04x-%02x%02x-%04x%08x", (int)$timeLow, (int)substr($timeMid, 2) & 0xffff, - mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3f) | 0x80, mt_rand(0, 0xff), $pid, $node - ); - } - -/** - * Tokenizes a string using $separator, ignoring any instance of $separator that appears between - * $leftBound and $rightBound. - * - * @param string $data The data to tokenize. - * @param string $separator The token to split the data on. - * @param string $leftBound The left boundary to ignore separators in. - * @param string $rightBound The right boundary to ignore separators in. - * @return mixed Array of tokens in $data or original input if empty. - */ - public static function tokenize($data, $separator = ',', $leftBound = '(', $rightBound = ')') { - if (empty($data)) { - return array(); - } - - $depth = 0; - $offset = 0; - $buffer = ''; - $results = array(); - $length = strlen($data); - $open = false; - - while ($offset <= $length) { - $tmpOffset = -1; - $offsets = array( - strpos($data, $separator, $offset), - strpos($data, $leftBound, $offset), - strpos($data, $rightBound, $offset) - ); - for ($i = 0; $i < 3; $i++) { - if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset == -1)) { - $tmpOffset = $offsets[$i]; - } - } - if ($tmpOffset !== -1) { - $buffer .= substr($data, $offset, ($tmpOffset - $offset)); - if (!$depth && $data{$tmpOffset} === $separator) { - $results[] = $buffer; - $buffer = ''; - } else { - $buffer .= $data{$tmpOffset}; - } - if ($leftBound !== $rightBound) { - if ($data{$tmpOffset} === $leftBound) { - $depth++; - } - if ($data{$tmpOffset} === $rightBound) { - $depth--; - } - } else { - if ($data{$tmpOffset} === $leftBound) { - if (!$open) { - $depth++; - $open = true; - } else { - $depth--; - } - } - } - $offset = ++$tmpOffset; - } else { - $results[] = $buffer . substr($data, $offset); - $offset = $length + 1; - } - } - if (empty($results) && !empty($buffer)) { - $results[] = $buffer; - } - - if (!empty($results)) { - return array_map('trim', $results); - } - - return array(); - } - -/** - * Replaces variable placeholders inside a $str with any given $data. Each key in the $data array - * corresponds to a variable placeholder name in $str. - * Example: `String::insert(':name is :age years old.', array('name' => 'Bob', '65'));` - * Returns: Bob is 65 years old. - * - * Available $options are: - * - * - before: The character or string in front of the name of the variable placeholder (Defaults to `:`) - * - after: The character or string after the name of the variable placeholder (Defaults to null) - * - escape: The character or string used to escape the before character / string (Defaults to `\`) - * - format: A regex to use for matching variable placeholders. Default is: `/(? val array where each key stands for a placeholder variable name - * to be replaced with val - * @param array $options An array of options, see description above - * @return string - */ - public static function insert($str, $data, $options = array()) { - $defaults = array( - 'before' => ':', 'after' => null, 'escape' => '\\', 'format' => null, 'clean' => false - ); - $options += $defaults; - $format = $options['format']; - $data = (array)$data; - if (empty($data)) { - return ($options['clean']) ? String::cleanInsert($str, $options) : $str; - } - - if (!isset($format)) { - $format = sprintf( - '/(? $hashVal) { - $key = sprintf($format, preg_quote($key, '/')); - $str = preg_replace($key, $hashVal, $str); - } - $dataReplacements = array_combine($hashKeys, array_values($data)); - foreach ($dataReplacements as $tmpHash => $tmpValue) { - $tmpValue = (is_array($tmpValue)) ? '' : $tmpValue; - $str = str_replace($tmpHash, $tmpValue, $str); - } - - if (!isset($options['format']) && isset($options['before'])) { - $str = str_replace($options['escape'] . $options['before'], $options['before'], $str); - } - return ($options['clean']) ? String::cleanInsert($str, $options) : $str; - } - -/** - * Cleans up a String::insert() formatted string with given $options depending on the 'clean' key in - * $options. The default method used is text but html is also available. The goal of this function - * is to replace all whitespace and unneeded markup around placeholders that did not get replaced - * by String::insert(). - * - * @param string $str String to clean. - * @param array $options Options list. - * @return string - * @see String::insert() - */ - public static function cleanInsert($str, $options) { - $clean = $options['clean']; - if (!$clean) { - return $str; - } - if ($clean === true) { - $clean = array('method' => 'text'); - } - if (!is_array($clean)) { - $clean = array('method' => $options['clean']); - } - switch ($clean['method']) { - case 'html': - $clean = array_merge(array( - 'word' => '[\w,.]+', - 'andText' => true, - 'replacement' => '', - ), $clean); - $kleenex = sprintf( - '/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i', - preg_quote($options['before'], '/'), - $clean['word'], - preg_quote($options['after'], '/') - ); - $str = preg_replace($kleenex, $clean['replacement'], $str); - if ($clean['andText']) { - $options['clean'] = array('method' => 'text'); - $str = String::cleanInsert($str, $options); - } - break; - case 'text': - $clean = array_merge(array( - 'word' => '[\w,.]+', - 'gap' => '[\s]*(?:(?:and|or)[\s]*)?', - 'replacement' => '', - ), $clean); - - $kleenex = sprintf( - '/(%s%s%s%s|%s%s%s%s)/', - preg_quote($options['before'], '/'), - $clean['word'], - preg_quote($options['after'], '/'), - $clean['gap'], - $clean['gap'], - preg_quote($options['before'], '/'), - $clean['word'], - preg_quote($options['after'], '/') - ); - $str = preg_replace($kleenex, $clean['replacement'], $str); - break; - } - return $str; - } - -/** - * Wraps text to a specific width, can optionally wrap at word breaks. - * - * ### Options - * - * - `width` The width to wrap to. Defaults to 72. - * - `wordWrap` Only wrap on words breaks (spaces) Defaults to true. - * - `indent` String to indent with. Defaults to null. - * - `indentAt` 0 based index to start indenting at. Defaults to 0. - * - * @param string $text The text to format. - * @param array|int $options Array of options to use, or an integer to wrap the text to. - * @return string Formatted text. - */ - public static function wrap($text, $options = array()) { - if (is_numeric($options)) { - $options = array('width' => $options); - } - $options += array('width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0); - if ($options['wordWrap']) { - $wrapped = self::wordWrap($text, $options['width'], "\n"); - } else { - $wrapped = trim(chunk_split($text, $options['width'] - 1, "\n")); - } - if (!empty($options['indent'])) { - $chunks = explode("\n", $wrapped); - for ($i = $options['indentAt'], $len = count($chunks); $i < $len; $i++) { - $chunks[$i] = $options['indent'] . $chunks[$i]; - } - $wrapped = implode("\n", $chunks); - } - return $wrapped; - } - -/** - * Unicode and newline aware version of wordwrap. - * - * @param string $text The text to format. - * @param int $width The width to wrap to. Defaults to 72. - * @param string $break The line is broken using the optional break parameter. Defaults to '\n'. - * @param bool $cut If the cut is set to true, the string is always wrapped at the specified width. - * @return string Formatted text. - */ - public static function wordWrap($text, $width = 72, $break = "\n", $cut = false) { - $paragraphs = explode($break, $text); - foreach ($paragraphs as &$paragraph) { - $paragraph = String::_wordWrap($paragraph, $width, $break, $cut); - } - return implode($break, $paragraphs); - } - -/** - * Unicode aware version of wordwrap as helper method. - * - * @param string $text The text to format. - * @param int $width The width to wrap to. Defaults to 72. - * @param string $break The line is broken using the optional break parameter. Defaults to '\n'. - * @param bool $cut If the cut is set to true, the string is always wrapped at the specified width. - * @return string Formatted text. - */ - protected static function _wordWrap($text, $width = 72, $break = "\n", $cut = false) { - if ($cut) { - $parts = array(); - while (mb_strlen($text) > 0) { - $part = mb_substr($text, 0, $width); - $parts[] = trim($part); - $text = trim(mb_substr($text, mb_strlen($part))); - } - return implode($break, $parts); - } - - $parts = array(); - while (mb_strlen($text) > 0) { - if ($width >= mb_strlen($text)) { - $parts[] = trim($text); - break; - } - - $part = mb_substr($text, 0, $width); - $nextChar = mb_substr($text, $width, 1); - if ($nextChar !== ' ') { - $breakAt = mb_strrpos($part, ' '); - if ($breakAt === false) { - $breakAt = mb_strpos($text, ' ', $width); - } - if ($breakAt === false) { - $parts[] = trim($text); - break; - } - $part = mb_substr($text, 0, $breakAt); - } - - $part = trim($part); - $parts[] = $part; - $text = trim(mb_substr($text, mb_strlen($part))); - } - - return implode($break, $parts); - } - -/** - * Highlights a given phrase in a text. You can specify any expression in highlighter that - * may include the \1 expression to include the $phrase found. - * - * ### Options: - * - * - `format` The piece of html with that the phrase will be highlighted - * - `html` If true, will ignore any HTML tags, ensuring that only the correct text is highlighted - * - `regex` a custom regex rule that is used to match words, default is '|$tag|iu' - * - * @param string $text Text to search the phrase in. - * @param string|array $phrase The phrase or phrases that will be searched. - * @param array $options An array of html attributes and options. - * @return string The highlighted text - * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::highlight - */ - public static function highlight($text, $phrase, $options = array()) { - if (empty($phrase)) { - return $text; - } - - $defaults = array( - 'format' => '\1', - 'html' => false, - 'regex' => "|%s|iu" - ); - $options += $defaults; - extract($options); - - if (is_array($phrase)) { - $replace = array(); - $with = array(); - - foreach ($phrase as $key => $segment) { - $segment = '(' . preg_quote($segment, '|') . ')'; - if ($html) { - $segment = "(?![^<]+>)$segment(?![^<]+>)"; - } - - $with[] = (is_array($format)) ? $format[$key] : $format; - $replace[] = sprintf($options['regex'], $segment); - } - - return preg_replace($replace, $with, $text); - } - - $phrase = '(' . preg_quote($phrase, '|') . ')'; - if ($html) { - $phrase = "(?![^<]+>)$phrase(?![^<]+>)"; - } - - return preg_replace(sprintf($options['regex'], $phrase), $format, $text); - } - -/** - * Strips given text of all links (]+>|im', '', preg_replace('|<\/a>|im', '', $text)); - } - -/** - * Truncates text starting from the end. - * - * Cuts a string to the length of $length and replaces the first characters - * with the ellipsis if the text is longer than length. - * - * ### Options: - * - * - `ellipsis` Will be used as Beginning and prepended to the trimmed string - * - `exact` If false, $text will not be cut mid-word - * - * @param string $text String to truncate. - * @param int $length Length of returned string, including ellipsis. - * @param array $options An array of options. - * @return string Trimmed string. - */ - public static function tail($text, $length = 100, $options = array()) { - $defaults = array( - 'ellipsis' => '...', 'exact' => true - ); - $options += $defaults; - extract($options); - - if (!function_exists('mb_strlen')) { - class_exists('Multibyte'); - } - - if (mb_strlen($text) <= $length) { - return $text; - } - - $truncate = mb_substr($text, mb_strlen($text) - $length + mb_strlen($ellipsis)); - if (!$exact) { - $spacepos = mb_strpos($truncate, ' '); - $truncate = $spacepos === false ? '' : trim(mb_substr($truncate, $spacepos)); - } - - return $ellipsis . $truncate; - } - -/** - * Truncates text. - * - * Cuts a string to the length of $length and replaces the last characters - * with the ellipsis if the text is longer than length. - * - * ### Options: - * - * - `ellipsis` Will be used as Ending and appended to the trimmed string (`ending` is deprecated) - * - `exact` If false, $text will not be cut mid-word - * - `html` If true, HTML tags would be handled correctly - * - * @param string $text String to truncate. - * @param int $length Length of returned string, including ellipsis. - * @param array $options An array of html attributes and options. - * @return string Trimmed string. - * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::truncate + * @package Cake.Utility + * @deprecated 3.0.0 Deprecated since version 2.7, use CakeText class instead. */ - public static function truncate($text, $length = 100, $options = array()) { - $defaults = array( - 'ellipsis' => '...', 'exact' => true, 'html' => false - ); - if (isset($options['ending'])) { - $defaults['ellipsis'] = $options['ending']; - } elseif (!empty($options['html']) && Configure::read('App.encoding') === 'UTF-8') { - $defaults['ellipsis'] = "\xe2\x80\xa6"; - } - $options += $defaults; - extract($options); - - if (!function_exists('mb_strlen')) { - class_exists('Multibyte'); - } - - if ($html) { - if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) { - return $text; - } - $totalLength = mb_strlen(strip_tags($ellipsis)); - $openTags = array(); - $truncate = ''; - - preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER); - foreach ($tags as $tag) { - if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2])) { - if (preg_match('/<[\w]+[^>]*>/s', $tag[0])) { - array_unshift($openTags, $tag[2]); - } elseif (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag)) { - $pos = array_search($closeTag[1], $openTags); - if ($pos !== false) { - array_splice($openTags, $pos, 1); - } - } - } - $truncate .= $tag[1]; - - $contentLength = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|[0-9]{1,7};|[0-9a-f]{1,6};/i', ' ', $tag[3])); - if ($contentLength + $totalLength > $length) { - $left = $length - $totalLength; - $entitiesLength = 0; - if (preg_match_all('/&[0-9a-z]{2,8};|[0-9]{1,7};|[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE)) { - foreach ($entities[0] as $entity) { - if ($entity[1] + 1 - $entitiesLength <= $left) { - $left--; - $entitiesLength += mb_strlen($entity[0]); - } else { - break; - } - } - } - - $truncate .= mb_substr($tag[3], 0, $left + $entitiesLength); - break; - } else { - $truncate .= $tag[3]; - $totalLength += $contentLength; - } - if ($totalLength >= $length) { - break; - } - } - } else { - if (mb_strlen($text) <= $length) { - return $text; - } - $truncate = mb_substr($text, 0, $length - mb_strlen($ellipsis)); - } - if (!$exact) { - $spacepos = mb_strrpos($truncate, ' '); - if ($html) { - $truncateCheck = mb_substr($truncate, 0, $spacepos); - $lastOpenTag = mb_strrpos($truncateCheck, '<'); - $lastCloseTag = mb_strrpos($truncateCheck, '>'); - if ($lastOpenTag > $lastCloseTag) { - preg_match_all('/<[\w]+[^>]*>/s', $truncate, $lastTagMatches); - $lastTag = array_pop($lastTagMatches[0]); - $spacepos = mb_strrpos($truncate, $lastTag) + mb_strlen($lastTag); - } - $bits = mb_substr($truncate, $spacepos); - preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER); - if (!empty($droppedTags)) { - if (!empty($openTags)) { - foreach ($droppedTags as $closingTag) { - if (!in_array($closingTag[1], $openTags)) { - array_unshift($openTags, $closingTag[1]); - } - } - } else { - foreach ($droppedTags as $closingTag) { - $openTags[] = $closingTag[1]; - } - } - } - } - $truncate = mb_substr($truncate, 0, $spacepos); - } - $truncate .= $ellipsis; - - if ($html) { - foreach ($openTags as $tag) { - $truncate .= '' . $tag . '>'; - } - } - - return $truncate; - } - -/** - * Extracts an excerpt from the text surrounding the phrase with a number of characters on each side - * determined by radius. - * - * @param string $text String to search the phrase in - * @param string $phrase Phrase that will be searched for - * @param int $radius The amount of characters that will be returned on each side of the founded phrase - * @param string $ellipsis Ending that will be appended - * @return string Modified string - * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::excerpt - */ - public static function excerpt($text, $phrase, $radius = 100, $ellipsis = '...') { - if (empty($text) || empty($phrase)) { - return self::truncate($text, $radius * 2, array('ellipsis' => $ellipsis)); - } - - $append = $prepend = $ellipsis; - - $phraseLen = mb_strlen($phrase); - $textLen = mb_strlen($text); - - $pos = mb_strpos(mb_strtolower($text), mb_strtolower($phrase)); - if ($pos === false) { - return mb_substr($text, 0, $radius) . $ellipsis; - } - - $startPos = $pos - $radius; - if ($startPos <= 0) { - $startPos = 0; - $prepend = ''; - } - - $endPos = $pos + $phraseLen + $radius; - if ($endPos >= $textLen) { - $endPos = $textLen; - $append = ''; - } - - $excerpt = mb_substr($text, $startPos, $endPos - $startPos); - $excerpt = $prepend . $excerpt . $append; - - return $excerpt; - } - -/** - * Creates a comma separated list where the last two items are joined with 'and', forming natural language. - * - * @param array $list The list to be joined. - * @param string $and The word used to join the last and second last items together with. Defaults to 'and'. - * @param string $separator The separator used to join all the other items together. Defaults to ', '. - * @return string The glued together string. - * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::toList - */ - public static function toList($list, $and = null, $separator = ', ') { - if ($and === null) { - $and = __d('cake', 'and'); - } - if (count($list) > 1) { - return implode($separator, array_slice($list, null, -1)) . ' ' . $and . ' ' . array_pop($list); - } - - return array_pop($list); - } +class String extends CakeText { } diff --git a/lib/Cake/Utility/Validation.php b/lib/Cake/Utility/Validation.php index a2243694d16..40449ab0d4f 100644 --- a/lib/Cake/Utility/Validation.php +++ b/lib/Cake/Utility/Validation.php @@ -48,6 +48,19 @@ class Validation { */ public static $errors = array(); +/** + * Backwards compatibility wrapper for Validation::notBlank(). + * + * @param string|array $check Value to check. + * @return bool Success. + * @deprecated 2.7.0 Use Validation::notBlank() instead. + * @see Validation::notBlank() + */ + public static function notEmpty($check) { + trigger_error('Validation::notEmpty() is deprecated. Use Validation::notBlank() instead.', E_USER_DEPRECATED); + return static::notBlank($check); + } + /** * Checks that a string contains something other than whitespace * @@ -59,15 +72,15 @@ class Validation { * @param string|array $check Value to check * @return bool Success */ - public static function notEmpty($check) { + public static function notBlank($check) { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } - if (empty($check) && $check != '0') { + if (empty($check) && (string)$check !== '0') { return false; } - return self::_check($check, '/[^\s]+/m'); + return static::_check($check, '/[^\s]+/m'); } /** @@ -83,13 +96,13 @@ public static function notEmpty($check) { */ public static function alphaNumeric($check) { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } if (empty($check) && $check != '0') { return false; } - return self::_check($check, '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/Du'); + return static::_check($check, '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/Du'); } /** @@ -118,7 +131,7 @@ public static function lengthBetween($check, $min, $max) { * @deprecated Deprecated 2.6. Use Validator::lengthBetween() instead. */ public static function between($check, $min, $max) { - return self::lengthBetween($check, $min, $max); + return static::lengthBetween($check, $min, $max); } /** @@ -133,9 +146,9 @@ public static function between($check, $min, $max) { */ public static function blank($check) { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } - return !self::_check($check, '/[^\\s]/'); + return !static::_check($check, '/[^\\s]/'); } /** @@ -143,7 +156,7 @@ public static function blank($check) { * Returns true if $check is in the proper credit card format. * * @param string|array $check credit card number to validate - * @param string|array $type 'all' may be passed as a sting, defaults to fast which checks format of most major credit + * @param string|array $type 'all' may be passed as a sting, defaults to fast which checks format of most major credit * cards * if an array is used only the values of the array are checked. * Example: array('amex', 'bankcard', 'maestro') @@ -154,7 +167,7 @@ public static function blank($check) { */ public static function cc($check, $type = 'fast', $deep = false, $regex = null) { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } $check = str_replace(array('-', ' '), '', $check); @@ -163,8 +176,8 @@ public static function cc($check, $type = 'fast', $deep = false, $regex = null) } if ($regex !== null) { - if (self::_check($check, $regex)) { - return self::luhn($check, $deep); + if (static::_check($check, $regex)) { + return static::luhn($check, $deep); } } $cards = array( @@ -192,23 +205,23 @@ public static function cc($check, $type = 'fast', $deep = false, $regex = null) foreach ($type as $value) { $regex = $cards['all'][strtolower($value)]; - if (self::_check($check, $regex)) { - return self::luhn($check, $deep); + if (static::_check($check, $regex)) { + return static::luhn($check, $deep); } } } elseif ($type === 'all') { foreach ($cards['all'] as $value) { $regex = $value; - if (self::_check($check, $regex)) { - return self::luhn($check, $deep); + if (static::_check($check, $regex)) { + return static::luhn($check, $deep); } } } else { $regex = $cards['fast']; - if (self::_check($check, $regex)) { - return self::luhn($check, $deep); + if (static::_check($check, $regex)) { + return static::luhn($check, $deep); } } return false; @@ -229,6 +242,10 @@ public static function comparison($check1, $operator = null, $check2 = null) { if (is_array($check1)) { extract($check1, EXTR_OVERWRITE); } + + if ((float)$check1 != $check1) { + return false; + } $operator = str_replace(array(' ', "\t", "\n", "\r", "\0", "\x0B"), '', strtolower($operator)); switch ($operator) { @@ -269,7 +286,7 @@ public static function comparison($check1, $operator = null, $check2 = null) { } break; default: - self::$errors[] = __d('cake_dev', 'You must define the $operator parameter for %s', 'Validation::comparison()'); + static::$errors[] = __d('cake_dev', 'You must define the $operator parameter for %s', 'Validation::comparison()'); } return false; } @@ -284,13 +301,13 @@ public static function comparison($check1, $operator = null, $check2 = null) { */ public static function custom($check, $regex = null) { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } if ($regex === null) { - self::$errors[] = __d('cake_dev', 'You must define a regular expression for %s', 'Validation::custom()'); + static::$errors[] = __d('cake_dev', 'You must define a regular expression for %s', 'Validation::custom()'); return false; } - return self::_check($check, $regex); + return static::_check($check, $regex); } /** @@ -319,7 +336,7 @@ public static function custom($check, $regex = null) { */ public static function date($check, $format = 'ymd', $regex = null) { if ($regex !== null) { - return self::_check($check, $regex); + return static::_check($check, $regex); } $month = '(0[123456789]|10|11|12)'; $separator = '([- /.])'; @@ -353,7 +370,7 @@ public static function date($check, $format = 'ymd', $regex = null) { $format = (is_array($format)) ? array_values($format) : array($format); foreach ($format as $key) { - if (self::_check($check, $regex[$key]) === true) { + if (static::_check($check, $regex[$key]) === true) { return true; } } @@ -378,7 +395,7 @@ public static function datetime($check, $dateFormat = 'ymd', $regex = null) { if (!empty($parts) && count($parts) > 1) { $time = array_pop($parts); $date = implode(' ', $parts); - $valid = self::date($date, $dateFormat, $regex) && self::time($time); + $valid = static::date($date, $dateFormat, $regex) && static::time($time); } return $valid; } @@ -392,7 +409,7 @@ public static function datetime($check, $dateFormat = 'ymd', $regex = null) { * @return bool Success */ public static function time($check) { - return self::_check($check, '%^((0?[1-9]|1[012])(:[0-5]\d){0,2} ?([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%'); + return static::_check($check, '%^((0?[1-9]|1[012])(:[0-5]\d){0,2} ?([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%'); } /** @@ -448,14 +465,14 @@ public static function decimal($check, $places = null, $regex = null) { $check = str_replace($data['thousands_sep'], '', $check); $check = str_replace($data['decimal_point'], '.', $check); - return self::_check($check, $regex); + return static::_check($check, $regex); } /** * Validates for an email address. * * Only uses getmxrr() checking for deep validation if PHP 5.3.0+ is used, or - * any PHP version on a non-windows distribution + * any PHP version on a non-Windows distribution * * @param string $check Value to check * @param bool $deep Perform a deeper validation (if true), by also checking availability of host @@ -464,18 +481,18 @@ public static function decimal($check, $places = null, $regex = null) { */ public static function email($check, $deep = false, $regex = null) { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } if ($regex === null) { - $regex = '/^[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . self::$_pattern['hostname'] . '$/ui'; + $regex = '/^[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . static::$_pattern['hostname'] . '$/ui'; } - $return = self::_check($check, $regex); + $return = static::_check($check, $regex); if ($deep === false || $deep === null) { return $return; } - if ($return === true && preg_match('/@(' . self::$_pattern['hostname'] . ')$/i', $check, $regs)) { + if ($return === true && preg_match('/@(' . static::$_pattern['hostname'] . ')$/i', $check, $regs)) { if (function_exists('getmxrr') && getmxrr($regs[1], $mxhosts)) { return true; } @@ -507,7 +524,7 @@ public static function equalTo($check, $comparedTo) { */ public static function extension($check, $extensions = array('gif', 'jpeg', 'png', 'jpg')) { if (is_array($check)) { - return self::extension(array_shift($check), $extensions); + return static::extension(array_shift($check), $extensions); } $extension = strtolower(pathinfo($check, PATHINFO_EXTENSION)); foreach ($extensions as $value) { @@ -573,7 +590,7 @@ public static function money($check, $symbolPosition = 'left') { } else { $regex = '/^(?!\x{00a2})\p{Sc}?' . $money . '$/u'; } - return self::_check($check, $regex); + return static::_check($check, $regex); } /** @@ -594,7 +611,7 @@ public static function multiple($check, $options = array(), $caseInsensitive = f $defaults = array('in' => null, 'max' => null, 'min' => null); $options += $defaults; - $check = array_filter((array)$check); + $check = array_filter((array)$check, 'strlen'); if (empty($check)) { return false; } @@ -641,7 +658,7 @@ public static function numeric($check) { */ public static function naturalNumber($check, $allowZero = false) { $regex = $allowZero ? '/^(?:0|[1-9][0-9]*)$/' : '/^[1-9][0-9]*$/'; - return self::_check($check, $regex); + return static::_check($check, $regex); } /** @@ -654,7 +671,7 @@ public static function naturalNumber($check, $allowZero = false) { */ public static function phone($check, $regex = null, $country = 'all') { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } if ($regex === null) { @@ -684,9 +701,9 @@ public static function phone($check, $regex = null, $country = 'all') { } } if (empty($regex)) { - return self::_pass('phone', $check, $country); + return static::_pass('phone', $check, $country); } - return self::_check($check, $regex); + return static::_check($check, $regex); } /** @@ -699,7 +716,7 @@ public static function phone($check, $regex = null, $country = 'all') { */ public static function postal($check, $regex = null, $country = 'us') { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } if ($regex === null) { @@ -725,9 +742,9 @@ public static function postal($check, $regex = null, $country = 'us') { } } if (empty($regex)) { - return self::_pass('postal', $check, $country); + return static::_pass('postal', $check, $country); } - return self::_check($check, $regex); + return static::_check($check, $regex); } /** @@ -744,6 +761,9 @@ public static function range($check, $lower = null, $upper = null) { if (!is_numeric($check)) { return false; } + if ((float)$check != $check) { + return false; + } if (isset($lower) && isset($upper)) { return ($check > $lower && $check < $upper); } @@ -761,7 +781,7 @@ public static function range($check, $lower = null, $upper = null) { */ public static function ssn($check, $regex = null, $country = null) { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } if ($regex === null) { @@ -778,9 +798,9 @@ public static function ssn($check, $regex = null, $country = null) { } } if (empty($regex)) { - return self::_pass('ssn', $check, $country); + return static::_pass('ssn', $check, $country); } - return self::_check($check, $regex); + return static::_check($check, $regex); } /** @@ -801,14 +821,14 @@ public static function ssn($check, $regex = null, $country = null) { * @return bool Success */ public static function url($check, $strict = false) { - self::_populateIp(); + static::_populateIp(); $validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=~[]') . '\/0-9\p{L}\p{N}]|(%[0-9a-f]{2}))'; $regex = '/^(?:(?:https?|ftps?|sftp|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') . - '(?:' . self::$_pattern['IPv4'] . '|\[' . self::$_pattern['IPv6'] . '\]|' . self::$_pattern['hostname'] . ')(?::[1-9][0-9]{0,4})?' . + '(?:' . static::$_pattern['IPv4'] . '|\[' . static::$_pattern['IPv6'] . '\]|' . static::$_pattern['hostname'] . ')(?::[1-9][0-9]{0,4})?' . '(?:\/?|\/' . $validChars . '*)?' . '(?:\?' . $validChars . '*)?' . '(?:#' . $validChars . '*)?$/iu'; - return self::_check($check, $regex); + return static::_check($check, $regex); } /** @@ -850,7 +870,7 @@ public static function userDefined($check, $object, $method, $args = null) { */ public static function uuid($check) { $regex = '/^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[0-5][a-fA-F0-9]{3}-[089aAbB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$/'; - return self::_check($check, $regex); + return static::_check($check, $regex); } /** @@ -899,7 +919,7 @@ protected static function _check($check, $regex) { * @return void */ protected static function _defaults($params) { - self::_reset(); + static::_reset(); $defaults = array( 'check' => null, 'regex' => null, @@ -924,7 +944,7 @@ protected static function _defaults($params) { */ public static function luhn($check, $deep = false) { if (is_array($check)) { - extract(self::_defaults($check)); + extract(static::_defaults($check)); } if ($deep !== true) { return true; @@ -968,7 +988,7 @@ public static function mimeType($check, $mimeTypes = array()) { } if (is_string($mimeTypes)) { - return self::_check($mime, $mimeTypes); + return static::_check($mime, $mimeTypes); } foreach ($mimeTypes as $key => $val) { @@ -995,7 +1015,7 @@ public static function fileSize($check, $operator = null, $size = null) { } $filesize = filesize($check); - return self::comparison($filesize, $operator, $size); + return static::comparison($filesize, $operator, $size); } /** @@ -1019,7 +1039,7 @@ public static function uploadError($check) { * @return void */ protected static function _populateIp() { - if (!isset(self::$_pattern['IPv6'])) { + if (!isset(static::$_pattern['IPv6'])) { $pattern = '((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}'; $pattern .= '(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})'; $pattern .= '|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})'; @@ -1035,11 +1055,11 @@ protected static function _populateIp() { $pattern .= '\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4})'; $pattern .= '{1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?'; - self::$_pattern['IPv6'] = $pattern; + static::$_pattern['IPv6'] = $pattern; } - if (!isset(self::$_pattern['IPv4'])) { + if (!isset(static::$_pattern['IPv4'])) { $pattern = '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])'; - self::$_pattern['IPv4'] = $pattern; + static::$_pattern['IPv4'] = $pattern; } } @@ -1049,7 +1069,7 @@ protected static function _populateIp() { * @return void */ protected static function _reset() { - self::$errors = array(); + static::$errors = array(); } } diff --git a/lib/Cake/Utility/Xml.php b/lib/Cake/Utility/Xml.php index e3bb4282da4..95bb4659e14 100644 --- a/lib/Cake/Utility/Xml.php +++ b/lib/Cake/Utility/Xml.php @@ -77,6 +77,9 @@ class Xml { * - `return` Can be 'simplexml' to return object of SimpleXMLElement or 'domdocument' to return DOMDocument. * - `loadEntities` Defaults to false. Set to true to enable loading of ` 'simplexml', 'loadEntities' => false, + 'readFile' => true ); $options += $defaults; if (is_array($input) || is_object($input)) { - return self::fromArray((array)$input, $options); + return static::fromArray((array)$input, $options); } elseif (strpos($input, '<') !== false) { - return self::_loadXml($input, $options); - } elseif (file_exists($input)) { - return self::_loadXml(file_get_contents($input), $options); - } elseif (strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) { + return static::_loadXml($input, $options); + } elseif ($options['readFile'] && file_exists($input)) { + return static::_loadXml(file_get_contents($input), $options); + } elseif ($options['readFile'] && strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) { try { $socket = new HttpSocket(array('request' => array('redirect' => 10))); $response = $socket->get($input); if (!$response->isOk()) { throw new XmlException(__d('cake_dev', 'XML cannot be read.')); } - return self::_loadXml($response->body, $options); + return static::_loadXml($response->body, $options); } catch (SocketException $e) { throw new XmlException(__d('cake_dev', 'XML cannot be read.')); } @@ -156,7 +160,7 @@ protected static function _loadXml($input, $options) { * * ### Options * - * - `format` If create childs ('tags') or attributes ('attribute'). + * - `format` If create childs ('tags') or attributes ('attributes'). * - `pretty` Returns formatted Xml when set to `true`. Defaults to `false` * - `version` Version of XML document. Default is 1.0. * - `encoding` Encoding of XML document. If null remove from XML header. Default is the some of application. @@ -180,7 +184,7 @@ protected static function _loadXml($input, $options) { * * `: ' . h($pluginDot . $class) . ''); ?> - +
@@ -29,4 +29,6 @@ $pluginDot = empty($plugin) ? null : $plugin . '.'; -element('exception_stack_trace'); ?> +element('exception_stack_trace'); +?> diff --git a/lib/Cake/View/Errors/missing_datasource_config.ctp b/lib/Cake/View/Errors/missing_datasource_config.ctp index 0fe5929a67f..115bdc7ba1e 100644 --- a/lib/Cake/View/Errors/missing_datasource_config.ctp +++ b/lib/Cake/View/Errors/missing_datasource_config.ctp @@ -24,4 +24,6 @@ -element('exception_stack_trace'); ?> +element('exception_stack_trace'); +?> diff --git a/lib/Cake/View/Errors/missing_helper.ctp b/lib/Cake/View/Errors/missing_helper.ctp index 93fa54ed239..6e2dc0d679e 100644 --- a/lib/Cake/View/Errors/missing_helper.ctp +++ b/lib/Cake/View/Errors/missing_helper.ctp @@ -36,4 +36,6 @@ class extends AppHelper { -element('exception_stack_trace'); ?> +element('exception_stack_trace'); +?> diff --git a/lib/Cake/View/Errors/missing_layout.ctp b/lib/Cake/View/Errors/missing_layout.ctp index b8c5d81cf50..cf1b53e10ab 100644 --- a/lib/Cake/View/Errors/missing_layout.ctp +++ b/lib/Cake/View/Errors/missing_layout.ctp @@ -41,4 +41,6 @@ -element('exception_stack_trace'); ?> +element('exception_stack_trace'); +?> diff --git a/lib/Cake/View/Errors/missing_plugin.ctp b/lib/Cake/View/Errors/missing_plugin.ctp index 0d5592e71aa..3e7b4d1c3e4 100644 --- a/lib/Cake/View/Errors/missing_plugin.ctp +++ b/lib/Cake/View/Errors/missing_plugin.ctp @@ -40,4 +40,6 @@ CakePlugin::loadAll(); -element('exception_stack_trace'); ?> +element('exception_stack_trace'); +?> diff --git a/lib/Cake/View/Errors/missing_table.ctp b/lib/Cake/View/Errors/missing_table.ctp index 7c308a864bb..5fcf2ac3222 100644 --- a/lib/Cake/View/Errors/missing_table.ctp +++ b/lib/Cake/View/Errors/missing_table.ctp @@ -17,11 +17,13 @@: - ' . h($table) . '', '' . h($class) . '', '' . h($ds) . ''); ?> + ' . h($table) . '', '' . h($class) . '', '' . h($ds) . ''); ?>
:
-element('exception_stack_trace'); ?> +element('exception_stack_trace'); +?> diff --git a/lib/Cake/View/Errors/missing_view.ctp b/lib/Cake/View/Errors/missing_view.ctp index b2579d57857..208afffe733 100644 --- a/lib/Cake/View/Errors/missing_view.ctp +++ b/lib/Cake/View/Errors/missing_view.ctp @@ -41,4 +41,6 @@ -element('exception_stack_trace'); ?> +element('exception_stack_trace'); +?> diff --git a/lib/Cake/View/Errors/pdo_error.ctp b/lib/Cake/View/Errors/pdo_error.ctp index b9c7a0ca350..2f8d4aa2b4e 100644 --- a/lib/Cake/View/Errors/pdo_error.ctp +++ b/lib/Cake/View/Errors/pdo_error.ctp @@ -33,4 +33,6 @@ : -element('exception_stack_trace'); ?> +element('exception_stack_trace'); +?> diff --git a/lib/Cake/View/Errors/private_action.ctp b/lib/Cake/View/Errors/private_action.ctp index 11a18a3b809..0fd18585591 100644 --- a/lib/Cake/View/Errors/private_action.ctp +++ b/lib/Cake/View/Errors/private_action.ctp @@ -24,4 +24,6 @@ -element('exception_stack_trace'); ?> +element('exception_stack_trace'); +?> diff --git a/lib/Cake/View/Errors/scaffold_error.ctp b/lib/Cake/View/Errors/scaffold_error.ctp index ce1eea8e677..9cfe10490ff 100644 --- a/lib/Cake/View/Errors/scaffold_error.ctp +++ b/lib/Cake/View/Errors/scaffold_error.ctp @@ -33,6 +33,6 @@ function _scaffoldError() {%s' : "\n%s\n"; + $template = PHP_SAPI !== 'cli' ? '
%s' : "\n%s\n"; printf($template, print_r($var, true)); } } @@ -580,7 +580,7 @@ function stripslashes_deep($values) { */ function __($singular, $args = null) { if (!$singular) { - return; + return null; } App::uses('I18n', 'I18n'); @@ -600,13 +600,13 @@ function __($singular, $args = null) { * @param string $singular Singular text to translate * @param string $plural Plural text * @param int $count Count - * @param mixed $args Array with arguments or multiple arguments in function + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. * @return mixed plural form of translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__n */ function __n($singular, $plural, $count, $args = null) { if (!$singular) { - return; + return null; } App::uses('I18n', 'I18n'); @@ -624,13 +624,13 @@ function __n($singular, $plural, $count, $args = null) { * * @param string $domain Domain * @param string $msg String to translate - * @param mixed $args Array with arguments or multiple arguments in function - * @return translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__d */ function __d($domain, $msg, $args = null) { if (!$msg) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($msg, null, $domain); @@ -651,13 +651,13 @@ function __d($domain, $msg, $args = null) { * @param string $singular Singular string to translate * @param string $plural Plural * @param int $count Count - * @param mixed $args Array with arguments or multiple arguments in function - * @return plural form of translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string plural form of translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__dn */ function __dn($domain, $singular, $plural, $count, $args = null) { if (!$singular) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($singular, $plural, $domain, I18n::LC_MESSAGES, $count); @@ -689,13 +689,13 @@ function __dn($domain, $singular, $plural, $count, $args = null) { * @param string $domain Domain * @param string $msg Message to translate * @param int $category Category - * @param mixed $args Array with arguments or multiple arguments in function - * @return translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__dc */ function __dc($domain, $msg, $category, $args = null) { if (!$msg) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($msg, null, $domain, $category); @@ -731,13 +731,13 @@ function __dc($domain, $msg, $category, $args = null) { * @param string $plural Plural * @param int $count Count * @param int $category Category - * @param mixed $args Array with arguments or multiple arguments in function - * @return plural form of translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string plural form of translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__dcn */ function __dcn($domain, $singular, $plural, $count, $category, $args = null) { if (!$singular) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($singular, $plural, $domain, $category, $count); @@ -765,13 +765,13 @@ function __dcn($domain, $singular, $plural, $count, $category, $args = null) { * * @param string $msg String to translate * @param int $category Category - * @param mixed $args Array with arguments or multiple arguments in function - * @return translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__c */ function __c($msg, $category, $args = null) { if (!$msg) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($msg, null, null, $category); @@ -788,13 +788,13 @@ function __c($msg, $category, $args = null) { * * @param string $context Context of the text * @param string $singular Text to translate - * @param mixed $args Array with arguments or multiple arguments in function + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. * @return mixed translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__ */ function __x($context, $singular, $args = null) { if (!$singular) { - return; + return null; } App::uses('I18n', 'I18n'); @@ -815,13 +815,13 @@ function __x($context, $singular, $args = null) { * @param string $singular Singular text to translate * @param string $plural Plural text * @param int $count Count - * @param mixed $args Array with arguments or multiple arguments in function + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. * @return mixed plural form of translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__n */ function __xn($context, $singular, $plural, $count, $args = null) { if (!$singular) { - return; + return null; } App::uses('I18n', 'I18n'); @@ -840,13 +840,13 @@ function __xn($context, $singular, $plural, $count, $args = null) { * @param string $domain Domain * @param string $context Context of the text * @param string $msg String to translate - * @param mixed $args Array with arguments or multiple arguments in function - * @return translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__d */ function __dx($domain, $context, $msg, $args = null) { if (!$msg) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($msg, null, $domain, null, null, null, $context); @@ -868,13 +868,13 @@ function __dx($domain, $context, $msg, $args = null) { * @param string $singular Singular string to translate * @param string $plural Plural * @param int $count Count - * @param mixed $args Array with arguments or multiple arguments in function - * @return plural form of translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string plural form of translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__dn */ function __dxn($domain, $context, $singular, $plural, $count, $args = null) { if (!$singular) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($singular, $plural, $domain, I18n::LC_MESSAGES, $count, null, $context); @@ -907,13 +907,13 @@ function __dxn($domain, $context, $singular, $plural, $count, $args = null) { * @param string $context Context of the text * @param string $msg Message to translate * @param int $category Category - * @param mixed $args Array with arguments or multiple arguments in function - * @return translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__dc */ function __dxc($domain, $context, $msg, $category, $args = null) { if (!$msg) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($msg, null, $domain, $category, null, null, $context); @@ -950,13 +950,13 @@ function __dxc($domain, $context, $msg, $category, $args = null) { * @param string $plural Plural * @param int $count Count * @param int $category Category - * @param mixed $args Array with arguments or multiple arguments in function - * @return plural form of translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string plural form of translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__dcn */ function __dxcn($domain, $context, $singular, $plural, $count, $category, $args = null) { if (!$singular) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($singular, $plural, $domain, $category, $count, null, $context); @@ -985,13 +985,13 @@ function __dxcn($domain, $context, $singular, $plural, $count, $category, $args * @param string $context Context of the text * @param string $msg String to translate * @param int $category Category - * @param mixed $args Array with arguments or multiple arguments in function - * @return translated string + * @param mixed $args Array with arguments or multiple arguments in function, otherwise null. + * @return string translated string * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#__c */ function __xc($context, $msg, $category, $args = null) { if (!$msg) { - return; + return null; } App::uses('I18n', 'I18n'); $translated = I18n::translate($msg, null, null, $category, null, null, $context); @@ -1025,7 +1025,7 @@ function LogError($message) { * Searches include path for files. * * @param string $file File to look for - * @return Full path to file if exists, otherwise false + * @return string Full path to file if exists, otherwise false * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#fileExistsInPath */ function fileExistsInPath($file) { @@ -1061,3 +1061,25 @@ function convertSlash($string) { } } + +if (!function_exists('json_last_error_msg')) { + +/** + * Provides the fallback implementation of json_last_error_msg() available in PHP 5.5 and above. + * + * @return string Error message. + */ + function json_last_error_msg() { + static $errors = array( + JSON_ERROR_NONE => '', + JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', + JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', + JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded', + JSON_ERROR_SYNTAX => 'Syntax error', + JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded' + ); + $error = json_last_error(); + return array_key_exists($error, $errors) ? $errors[$error] : "Unknown error ({$error})"; + } + +} diff --git a/lib/Cake/bootstrap.php b/lib/Cake/bootstrap.php index 4ad77a21b5a..b17c9f68ffe 100644 --- a/lib/Cake/bootstrap.php +++ b/lib/Cake/bootstrap.php @@ -172,17 +172,6 @@ Configure::write('App.cssBaseUrl', CSS_URL); Configure::write('App.jsBaseUrl', JS_URL); -Configure::bootstrap(isset($boot) ? $boot : true); - -if (function_exists('mb_internal_encoding')) { - $encoding = Configure::read('App.encoding'); - if (!empty($encoding)) { - mb_internal_encoding($encoding); - } - if (!empty($encoding) && function_exists('mb_regex_encoding')) { - mb_regex_encoding($encoding); - } -} if (!function_exists('mb_stripos')) { @@ -438,3 +427,15 @@ function mb_encode_mimeheader($str, $charset = 'UTF-8', $transferEncoding = 'B', } } + +Configure::bootstrap(isset($boot) ? $boot : true); + +if (function_exists('mb_internal_encoding')) { + $encoding = Configure::read('App.encoding'); + if (!empty($encoding)) { + mb_internal_encoding($encoding); + } + if (!empty($encoding) && function_exists('mb_regex_encoding')) { + mb_regex_encoding($encoding); + } +}