Merge pull request #5559 from cakephp/2.7-caketext

Rename String class to CakeText. Backport from 3.0
This commit is contained in:
José Lorenzo Rodríguez 2015-01-05 22:34:43 +01:00
commit bc09c9a3aa
22 changed files with 823 additions and 791 deletions

View file

@ -122,7 +122,7 @@ class SchemaShell extends AppShell {
if ($this->params['force']) { if ($this->params['force']) {
$options['models'] = false; $options['models'] = false;
} elseif (!empty($this->params['models'])) { } elseif (!empty($this->params['models'])) {
$options['models'] = String::tokenize($this->params['models']); $options['models'] = CakeText::tokenize($this->params['models']);
} }
$snapshot = false; $snapshot = false;
@ -151,7 +151,7 @@ class SchemaShell extends AppShell {
Configure::write('Cache.disable', $cacheDisable); Configure::write('Cache.disable', $cacheDisable);
if (!empty($this->params['exclude']) && !empty($content)) { if (!empty($this->params['exclude']) && !empty($content)) {
$excluded = String::tokenize($this->params['exclude']); $excluded = CakeText::tokenize($this->params['exclude']);
foreach ($excluded as $table) { foreach ($excluded as $table) {
unset($content['tables'][$table]); unset($content['tables'][$table]);
} }

View file

@ -340,7 +340,7 @@ class FixtureTask extends BakeTask {
isset($fieldInfo['length']) && $fieldInfo['length'] == 36 isset($fieldInfo['length']) && $fieldInfo['length'] == 36
); );
if ($isPrimaryUuid) { if ($isPrimaryUuid) {
$insert = String::uuid(); $insert = CakeText::uuid();
} else { } else {
$insert = "Lorem ipsum dolor sit amet"; $insert = "Lorem ipsum dolor sit amet";
if (!empty($fieldInfo['length'])) { if (!empty($fieldInfo['length'])) {

View file

@ -18,7 +18,7 @@
App::uses('AppShell', 'Console/Command'); App::uses('AppShell', 'Console/Command');
App::uses('File', 'Utility'); App::uses('File', 'Utility');
App::uses('Folder', 'Utility'); App::uses('Folder', 'Utility');
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
App::uses('Security', 'Utility'); App::uses('Security', 'Utility');
/** /**
@ -212,7 +212,7 @@ class ProjectTask extends AppShell {
} }
foreach ($Folder->messages() as $message) { foreach ($Folder->messages() as $message) {
$this->out(String::wrap(' * ' . $message), 1, Shell::VERBOSE); $this->out(CakeText::wrap(' * ' . $message), 1, Shell::VERBOSE);
} }
return true; return true;

View file

@ -14,7 +14,7 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
/** /**
* HelpFormatter formats help for console shells. Can format to either * HelpFormatter formats help for console shells. Can format to either
@ -64,7 +64,7 @@ class HelpFormatter {
$out = array(); $out = array();
$description = $parser->description(); $description = $parser->description();
if (!empty($description)) { if (!empty($description)) {
$out[] = String::wrap($description, $width); $out[] = CakeText::wrap($description, $width);
$out[] = ''; $out[] = '';
} }
$out[] = __d('cake_console', '<info>Usage:</info>'); $out[] = __d('cake_console', '<info>Usage:</info>');
@ -76,7 +76,7 @@ class HelpFormatter {
$out[] = ''; $out[] = '';
$max = $this->_getMaxLength($subcommands) + 2; $max = $this->_getMaxLength($subcommands) + 2;
foreach ($subcommands as $command) { foreach ($subcommands as $command) {
$out[] = String::wrap($command->help($max), array( $out[] = CakeText::wrap($command->help($max), array(
'width' => $width, 'width' => $width,
'indent' => str_repeat(' ', $max), 'indent' => str_repeat(' ', $max),
'indentAt' => 1 'indentAt' => 1
@ -93,7 +93,7 @@ class HelpFormatter {
$out[] = __d('cake_console', '<info>Options:</info>'); $out[] = __d('cake_console', '<info>Options:</info>');
$out[] = ''; $out[] = '';
foreach ($options as $option) { foreach ($options as $option) {
$out[] = String::wrap($option->help($max), array( $out[] = CakeText::wrap($option->help($max), array(
'width' => $width, 'width' => $width,
'indent' => str_repeat(' ', $max), 'indent' => str_repeat(' ', $max),
'indentAt' => 1 'indentAt' => 1
@ -108,7 +108,7 @@ class HelpFormatter {
$out[] = __d('cake_console', '<info>Arguments:</info>'); $out[] = __d('cake_console', '<info>Arguments:</info>');
$out[] = ''; $out[] = '';
foreach ($arguments as $argument) { foreach ($arguments as $argument) {
$out[] = String::wrap($argument->help($max), array( $out[] = CakeText::wrap($argument->help($max), array(
'width' => $width, 'width' => $width,
'indent' => str_repeat(' ', $max), 'indent' => str_repeat(' ', $max),
'indentAt' => 1 'indentAt' => 1
@ -118,7 +118,7 @@ class HelpFormatter {
} }
$epilog = $parser->epilog(); $epilog = $parser->epilog();
if (!empty($epilog)) { if (!empty($epilog)) {
$out[] = String::wrap($epilog, $width); $out[] = CakeText::wrap($epilog, $width);
$out[] = ''; $out[] = '';
} }
return implode("\n", $out); return implode("\n", $out);

View file

@ -584,11 +584,11 @@ class Shell extends Object {
* @param string $text Text the text to format. * @param string $text Text the text to format.
* @param string|int|array $options Array of options to use, or an integer to wrap the text to. * @param string|int|array $options Array of options to use, or an integer to wrap the text to.
* @return string Wrapped / indented text * @return string Wrapped / indented text
* @see String::wrap() * @see CakeText::wrap()
* @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::wrapText * @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::wrapText
*/ */
public function wrapText($text, $options = array()) { public function wrapText($text, $options = array()) {
return String::wrap($text, $options); return CakeText::wrap($text, $options);
} }
/** /**

View file

@ -17,7 +17,7 @@
*/ */
App::uses('Component', 'Controller'); App::uses('Component', 'Controller');
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
App::uses('Hash', 'Utility'); App::uses('Hash', 'Utility');
App::uses('Security', 'Utility'); App::uses('Security', 'Utility');

View file

@ -103,10 +103,10 @@ class Configure {
self::$_values['Exception'] self::$_values['Exception']
); );
// Preload Debugger + String in case of E_STRICT errors when loading files. // Preload Debugger + CakeText in case of E_STRICT errors when loading files.
if (self::$_values['debug'] > 0) { if (self::$_values['debug'] > 0) {
class_exists('Debugger'); class_exists('Debugger');
class_exists('String'); class_exists('CakeText');
} }
} }
} }

View file

@ -140,7 +140,7 @@ class TranslateBehavior extends ModelBehavior {
unset($this->_joinTable, $this->_runtimeModel); unset($this->_joinTable, $this->_runtimeModel);
return $query; return $query;
} elseif (is_string($query['fields'])) { } elseif (is_string($query['fields'])) {
$query['fields'] = String::tokenize($query['fields']); $query['fields'] = CakeText::tokenize($query['fields']);
} }
$fields = array_merge( $fields = array_merge(

View file

@ -17,7 +17,7 @@
*/ */
App::uses('DboSource', 'Model/Datasource'); App::uses('DboSource', 'Model/Datasource');
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
/** /**
* DBO implementation for the SQLite3 DBMS. * DBO implementation for the SQLite3 DBMS.
@ -303,7 +303,7 @@ class Sqlite extends DboSource {
if (stripos($querystring, 'SELECT') === 0 && stripos($querystring, 'FROM') > 0) { if (stripos($querystring, 'SELECT') === 0 && stripos($querystring, 'FROM') > 0) {
$selectpart = substr($querystring, 7); $selectpart = substr($querystring, 7);
$selects = array(); $selects = array();
foreach (String::tokenize($selectpart, ',', '(', ')') as $part) { foreach (CakeText::tokenize($selectpart, ',', '(', ')') as $part) {
$fromPos = stripos($part, ' FROM '); $fromPos = stripos($part, ' FROM ');
if ($fromPos !== false) { if ($fromPos !== false) {
$selects[] = trim(substr($part, 0, $fromPos)); $selects[] = trim(substr($part, 0, $fromPos));

View file

@ -17,7 +17,7 @@
*/ */
App::uses('DataSource', 'Model/Datasource'); App::uses('DataSource', 'Model/Datasource');
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
App::uses('View', 'View'); App::uses('View', 'View');
/** /**
@ -2513,7 +2513,7 @@ class DboSource extends DataSource {
if ($allFields) { if ($allFields) {
$fields = array_keys($Model->schema()); $fields = array_keys($Model->schema());
} elseif (!is_array($fields)) { } elseif (!is_array($fields)) {
$fields = String::tokenize($fields); $fields = CakeText::tokenize($fields);
} }
$fields = array_values(array_filter($fields)); $fields = array_values(array_filter($fields));
$allFields = $allFields || in_array('*', $fields) || in_array($Model->alias . '.*', $fields); $allFields = $allFields || in_array('*', $fields) || in_array($Model->alias . '.*', $fields);
@ -2813,7 +2813,7 @@ class DboSource extends DataSource {
} }
if ($bound) { if ($bound) {
return String::insert($key . ' ' . trim($operator), $value); return CakeText::insert($key . ' ' . trim($operator), $value);
} }
if (!preg_match($operatorMatch, trim($operator))) { if (!preg_match($operatorMatch, trim($operator))) {

View file

@ -20,7 +20,7 @@
App::uses('ClassRegistry', 'Utility'); App::uses('ClassRegistry', 'Utility');
App::uses('Validation', 'Utility'); App::uses('Validation', 'Utility');
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
App::uses('Hash', 'Utility'); App::uses('Hash', 'Utility');
App::uses('BehaviorCollection', 'Model'); App::uses('BehaviorCollection', 'Model');
App::uses('ModelBehavior', 'Model'); App::uses('ModelBehavior', 'Model');
@ -1915,9 +1915,9 @@ class Model extends Object implements CakeEventListener {
if (empty($this->data[$this->alias][$this->primaryKey]) && $this->_isUUIDField($this->primaryKey)) { if (empty($this->data[$this->alias][$this->primaryKey]) && $this->_isUUIDField($this->primaryKey)) {
if (array_key_exists($this->primaryKey, $this->data[$this->alias])) { if (array_key_exists($this->primaryKey, $this->data[$this->alias])) {
$j = array_search($this->primaryKey, $fields); $j = array_search($this->primaryKey, $fields);
$values[$j] = String::uuid(); $values[$j] = CakeText::uuid();
} else { } else {
list($fields[], $values[]) = array($this->primaryKey, String::uuid()); list($fields[], $values[]) = array($this->primaryKey, CakeText::uuid());
} }
} }
@ -2026,7 +2026,7 @@ class Model extends Object implements CakeEventListener {
$values = array($id, $row); $values = array($id, $row);
if ($isUUID && $primaryAdded) { if ($isUUID && $primaryAdded) {
$values[] = String::uuid(); $values[] = CakeText::uuid();
} }
$newValues[$row] = $values; $newValues[$row] = $values;
@ -3187,7 +3187,7 @@ class Model extends Object implements CakeEventListener {
$list = array("{n}.{$this->alias}.{$this->primaryKey}", "{n}.{$this->alias}.{$this->displayField}", null); $list = array("{n}.{$this->alias}.{$this->primaryKey}", "{n}.{$this->alias}.{$this->displayField}", null);
} else { } else {
if (!is_array($query['fields'])) { if (!is_array($query['fields'])) {
$query['fields'] = String::tokenize($query['fields']); $query['fields'] = CakeText::tokenize($query['fields']);
} }
if (count($query['fields']) === 1) { if (count($query['fields']) === 1) {

View file

@ -17,7 +17,7 @@
App::uses('Multibyte', 'I18n'); App::uses('Multibyte', 'I18n');
App::uses('AbstractTransport', 'Network/Email'); App::uses('AbstractTransport', 'Network/Email');
App::uses('File', 'Utility'); App::uses('File', 'Utility');
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
App::uses('View', 'View'); App::uses('View', 'View');
/** /**
@ -776,7 +776,7 @@ class CakeEmail {
} }
if ($this->_messageId !== false) { if ($this->_messageId !== false) {
if ($this->_messageId === true) { if ($this->_messageId === true) {
$headers['Message-ID'] = '<' . str_replace('-', '', String::UUID()) . '@' . $this->_domain . '>'; $headers['Message-ID'] = '<' . str_replace('-', '', CakeText::UUID()) . '@' . $this->_domain . '>';
} else { } else {
$headers['Message-ID'] = $this->_messageId; $headers['Message-ID'] = $this->_messageId;
} }

View file

@ -20,7 +20,7 @@
App::uses('Model', 'Model'); App::uses('Model', 'Model');
App::uses('AppModel', 'Model'); App::uses('AppModel', 'Model');
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
require_once dirname(dirname(__FILE__)) . DS . 'models.php'; require_once dirname(dirname(__FILE__)) . DS . 'models.php';
@ -73,7 +73,7 @@ class TreeBehaviorUuidTest extends CakeTestCase {
'conditions' => array($modelClass . '.name' => '1.1') 'conditions' => array($modelClass . '.name' => '1.1')
)); ));
$id = String::uuid(); $id = CakeText::uuid();
$this->Tree->create(); $this->Tree->create();
$result = $this->Tree->save(array($modelClass => array( $result = $this->Tree->save(array($modelClass => array(
'id' => $id, 'id' => $id,

View file

@ -1,6 +1,6 @@
<?php <?php
/** /**
* StringTest file * CakeTextTest file
* *
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
@ -16,18 +16,18 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
/** /**
* StringTest class * CakeTextTest class
* *
* @package Cake.Test.Case.Utility * @package Cake.Test.Case.Utility
*/ */
class StringTest extends CakeTestCase { class CakeTextTest extends CakeTestCase {
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
$this->Text = new String(); $this->Text = new CakeText();
} }
public function tearDown() { public function tearDown() {
@ -41,7 +41,7 @@ class StringTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testUuidGeneration() { public function testUuidGeneration() {
$result = String::uuid(); $result = CakeText::uuid();
$pattern = "/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/"; $pattern = "/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/";
$match = (bool)preg_match($pattern, $result); $match = (bool)preg_match($pattern, $result);
$this->assertTrue($match); $this->assertTrue($match);
@ -58,7 +58,7 @@ class StringTest extends CakeTestCase {
$pattern = "/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/"; $pattern = "/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/";
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$result = String::uuid(); $result = CakeText::uuid();
$match = (bool)preg_match($pattern, $result); $match = (bool)preg_match($pattern, $result);
$this->assertTrue($match); $this->assertTrue($match);
$this->assertFalse(in_array($result, $check)); $this->assertFalse(in_array($result, $check));
@ -74,127 +74,127 @@ class StringTest extends CakeTestCase {
public function testInsert() { public function testInsert() {
$string = 'some string'; $string = 'some string';
$expected = 'some string'; $expected = 'some string';
$result = String::insert($string, array()); $result = CakeText::insert($string, array());
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '2 + 2 = :sum. Cake is :adjective.'; $string = '2 + 2 = :sum. Cake is :adjective.';
$expected = '2 + 2 = 4. Cake is yummy.'; $expected = '2 + 2 = 4. Cake is yummy.';
$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy')); $result = CakeText::insert($string, array('sum' => '4', 'adjective' => 'yummy'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '2 + 2 = %sum. Cake is %adjective.'; $string = '2 + 2 = %sum. Cake is %adjective.';
$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('before' => '%')); $result = CakeText::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('before' => '%'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '2 + 2 = 2sum2. Cake is 9adjective9.'; $string = '2 + 2 = 2sum2. Cake is 9adjective9.';
$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('format' => '/([\d])%s\\1/')); $result = CakeText::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('format' => '/([\d])%s\\1/'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '2 + 2 = 12sum21. Cake is 23adjective45.'; $string = '2 + 2 = 12sum21. Cake is 23adjective45.';
$expected = '2 + 2 = 4. Cake is 23adjective45.'; $expected = '2 + 2 = 4. Cake is 23adjective45.';
$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('format' => '/([\d])([\d])%s\\2\\1/')); $result = CakeText::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('format' => '/([\d])([\d])%s\\2\\1/'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = ':web :web_site'; $string = ':web :web_site';
$expected = 'www http'; $expected = 'www http';
$result = String::insert($string, array('web' => 'www', 'web_site' => 'http')); $result = CakeText::insert($string, array('web' => 'www', 'web_site' => 'http'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '2 + 2 = <sum. Cake is <adjective>.'; $string = '2 + 2 = <sum. Cake is <adjective>.';
$expected = '2 + 2 = <sum. Cake is yummy.'; $expected = '2 + 2 = <sum. Cake is yummy.';
$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('before' => '<', 'after' => '>')); $result = CakeText::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('before' => '<', 'after' => '>'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '2 + 2 = \:sum. Cake is :adjective.'; $string = '2 + 2 = \:sum. Cake is :adjective.';
$expected = '2 + 2 = :sum. Cake is yummy.'; $expected = '2 + 2 = :sum. Cake is yummy.';
$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy')); $result = CakeText::insert($string, array('sum' => '4', 'adjective' => 'yummy'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '2 + 2 = !:sum. Cake is :adjective.'; $string = '2 + 2 = !:sum. Cake is :adjective.';
$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('escape' => '!')); $result = CakeText::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('escape' => '!'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '2 + 2 = \%sum. Cake is %adjective.'; $string = '2 + 2 = \%sum. Cake is %adjective.';
$expected = '2 + 2 = %sum. Cake is yummy.'; $expected = '2 + 2 = %sum. Cake is yummy.';
$result = String::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('before' => '%')); $result = CakeText::insert($string, array('sum' => '4', 'adjective' => 'yummy'), array('before' => '%'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = ':a :b \:a :a'; $string = ':a :b \:a :a';
$expected = '1 2 :a 1'; $expected = '1 2 :a 1';
$result = String::insert($string, array('a' => 1, 'b' => 2)); $result = CakeText::insert($string, array('a' => 1, 'b' => 2));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = ':a :b :c'; $string = ':a :b :c';
$expected = '2 3'; $expected = '2 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); $this->assertEquals($expected, $result);
$string = ':a :b :c'; $string = ':a :b :c';
$expected = '1 3'; $expected = '1 3';
$result = String::insert($string, array('a' => 1, 'c' => 3), array('clean' => true)); $result = CakeText::insert($string, array('a' => 1, 'c' => 3), array('clean' => true));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = ':a :b :c'; $string = ':a :b :c';
$expected = '2 3'; $expected = '2 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); $this->assertEquals($expected, $result);
$string = ':a, :b and :c'; $string = ':a, :b and :c';
$expected = '2 and 3'; $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); $this->assertEquals($expected, $result);
$string = '":a, :b and :c"'; $string = '":a, :b and :c"';
$expected = '"1, 2"'; $expected = '"1, 2"';
$result = String::insert($string, array('a' => 1, 'b' => 2), array('clean' => true)); $result = CakeText::insert($string, array('a' => 1, 'b' => 2), array('clean' => true));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '"${a}, ${b} and ${c}"'; $string = '"${a}, ${b} and ${c}"';
$expected = '"1, 2"'; $expected = '"1, 2"';
$result = String::insert($string, array('a' => 1, 'b' => 2), array('before' => '${', 'after' => '}', 'clean' => true)); $result = CakeText::insert($string, array('a' => 1, 'b' => 2), array('before' => '${', 'after' => '}', 'clean' => true));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '<img src=":src" alt=":alt" class="foo :extra bar"/>'; $string = '<img src=":src" alt=":alt" class="foo :extra bar"/>';
$expected = '<img src="foo" class="foo bar"/>'; $expected = '<img src="foo" class="foo bar"/>';
$result = String::insert($string, array('src' => 'foo'), array('clean' => 'html')); $result = CakeText::insert($string, array('src' => 'foo'), array('clean' => 'html'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '<img src=":src" class=":no :extra"/>'; $string = '<img src=":src" class=":no :extra"/>';
$expected = '<img src="foo"/>'; $expected = '<img src="foo"/>';
$result = String::insert($string, array('src' => 'foo'), array('clean' => 'html')); $result = CakeText::insert($string, array('src' => 'foo'), array('clean' => 'html'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = '<img src=":src" class=":no :extra"/>'; $string = '<img src=":src" class=":no :extra"/>';
$expected = '<img src="foo" class="bar"/>'; $expected = '<img src="foo" class="bar"/>';
$result = String::insert($string, array('src' => 'foo', 'extra' => 'bar'), array('clean' => 'html')); $result = CakeText::insert($string, array('src' => 'foo', 'extra' => 'bar'), array('clean' => 'html'));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::insert("this is a ? string", "test"); $result = CakeText::insert("this is a ? string", "test");
$expected = "this is a test string"; $expected = "this is a test string";
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::insert("this is a ? string with a ? ? ?", array('long', 'few?', 'params', 'you know')); $result = CakeText::insert("this is a ? string with a ? ? ?", array('long', 'few?', 'params', 'you know'));
$expected = "this is a long string with a few? params you know"; $expected = "this is a long string with a few? params you know";
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::insert('update saved_urls set url = :url where id = :id', array('url' => 'http://www.testurl.com/param1:url/param2:id', 'id' => 1)); $result = CakeText::insert('update saved_urls set url = :url where id = :id', array('url' => 'http://www.testurl.com/param1:url/param2:id', 'id' => 1));
$expected = "update saved_urls set url = http://www.testurl.com/param1:url/param2:id where id = 1"; $expected = "update saved_urls set url = http://www.testurl.com/param1:url/param2:id where id = 1";
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::insert('update saved_urls set url = :url where id = :id', array('id' => 1, 'url' => 'http://www.testurl.com/param1:url/param2:id')); $result = CakeText::insert('update saved_urls set url = :url where id = :id', array('id' => 1, 'url' => 'http://www.testurl.com/param1:url/param2:id'));
$expected = "update saved_urls set url = http://www.testurl.com/param1:url/param2:id where id = 1"; $expected = "update saved_urls set url = http://www.testurl.com/param1:url/param2:id where id = 1";
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::insert(':me cake. :subject :verb fantastic.', array('me' => 'I :verb', 'subject' => 'cake', 'verb' => 'is')); $result = CakeText::insert(':me cake. :subject :verb fantastic.', array('me' => 'I :verb', 'subject' => 'cake', 'verb' => 'is'));
$expected = "I :verb cake. cake is fantastic."; $expected = "I :verb cake. cake is fantastic.";
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::insert(':I.am: :not.yet: passing.', array('I.am' => 'We are'), array('before' => ':', 'after' => ':', 'clean' => array('replacement' => ' of course', 'method' => 'text'))); $result = CakeText::insert(':I.am: :not.yet: passing.', array('I.am' => 'We are'), array('before' => ':', 'after' => ':', 'clean' => array('replacement' => ' of course', 'method' => 'text')));
$expected = "We are of course passing."; $expected = "We are of course passing.";
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::insert( $result = CakeText::insert(
':I.am: :not.yet: passing.', ':I.am: :not.yet: passing.',
array('I.am' => 'We are'), array('I.am' => 'We are'),
array('before' => ':', 'after' => ':', 'clean' => true) array('before' => ':', 'after' => ':', 'clean' => true)
@ -202,28 +202,28 @@ class StringTest extends CakeTestCase {
$expected = "We are passing."; $expected = "We are passing.";
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::insert('?-pended result', array('Pre')); $result = CakeText::insert('?-pended result', array('Pre'));
$expected = "Pre-pended result"; $expected = "Pre-pended result";
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = 'switching :timeout / :timeout_count'; $string = 'switching :timeout / :timeout_count';
$expected = 'switching 5 / 10'; $expected = 'switching 5 / 10';
$result = String::insert($string, array('timeout' => 5, 'timeout_count' => 10)); $result = CakeText::insert($string, array('timeout' => 5, 'timeout_count' => 10));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = 'switching :timeout / :timeout_count'; $string = 'switching :timeout / :timeout_count';
$expected = 'switching 5 / 10'; $expected = 'switching 5 / 10';
$result = String::insert($string, array('timeout_count' => 10, 'timeout' => 5)); $result = CakeText::insert($string, array('timeout_count' => 10, 'timeout' => 5));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = 'switching :timeout_count by :timeout'; $string = 'switching :timeout_count by :timeout';
$expected = 'switching 10 by 5'; $expected = 'switching 10 by 5';
$result = String::insert($string, array('timeout' => 5, 'timeout_count' => 10)); $result = CakeText::insert($string, array('timeout' => 5, 'timeout_count' => 10));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$string = 'switching :timeout_count by :timeout'; $string = 'switching :timeout_count by :timeout';
$expected = 'switching 10 by 5'; $expected = 'switching 10 by 5';
$result = String::insert($string, array('timeout_count' => 10, 'timeout' => 5)); $result = CakeText::insert($string, array('timeout_count' => 10, 'timeout' => 5));
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
@ -233,33 +233,33 @@ class StringTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testCleanInsert() { public function testCleanInsert() {
$result = String::cleanInsert(':incomplete', array( $result = CakeText::cleanInsert(':incomplete', array(
'clean' => true, 'before' => ':', 'after' => '' 'clean' => true, 'before' => ':', 'after' => ''
)); ));
$this->assertEquals('', $result); $this->assertEquals('', $result);
$result = String::cleanInsert(':incomplete', array( $result = CakeText::cleanInsert(':incomplete', array(
'clean' => array('method' => 'text', 'replacement' => 'complete'), 'clean' => array('method' => 'text', 'replacement' => 'complete'),
'before' => ':', 'after' => '') 'before' => ':', 'after' => '')
); );
$this->assertEquals('complete', $result); $this->assertEquals('complete', $result);
$result = String::cleanInsert(':in.complete', array( $result = CakeText::cleanInsert(':in.complete', array(
'clean' => true, 'before' => ':', 'after' => '' 'clean' => true, 'before' => ':', 'after' => ''
)); ));
$this->assertEquals('', $result); $this->assertEquals('', $result);
$result = String::cleanInsert(':in.complete and', array( $result = CakeText::cleanInsert(':in.complete and', array(
'clean' => true, 'before' => ':', 'after' => '') 'clean' => true, 'before' => ':', 'after' => '')
); );
$this->assertEquals('', $result); $this->assertEquals('', $result);
$result = String::cleanInsert(':in.complete or stuff', array( $result = CakeText::cleanInsert(':in.complete or stuff', array(
'clean' => true, 'before' => ':', 'after' => '' 'clean' => true, 'before' => ':', 'after' => ''
)); ));
$this->assertEquals('stuff', $result); $this->assertEquals('stuff', $result);
$result = String::cleanInsert( $result = CakeText::cleanInsert(
'<p class=":missing" id=":missing">Text here</p>', '<p class=":missing" id=":missing">Text here</p>',
array('clean' => 'html', 'before' => ':', 'after' => '') array('clean' => 'html', 'before' => ':', 'after' => '')
); );
@ -268,13 +268,13 @@ class StringTest extends CakeTestCase {
/** /**
* Tests that non-insertable variables (i.e. arrays) are skipped when used as values in * Tests that non-insertable variables (i.e. arrays) are skipped when used as values in
* String::insert(). * CakeText::insert().
* *
* @return void * @return void
*/ */
public function testAutoIgnoreBadInsertData() { public function testAutoIgnoreBadInsertData() {
$data = array('foo' => 'alpha', 'bar' => 'beta', 'fale' => array()); $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); $this->assertEquals('(alpha > beta || !)', $result);
} }
@ -284,35 +284,40 @@ class StringTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testTokenize() { public function testTokenize() {
$result = String::tokenize('A,(short,boring test)'); $result = CakeText::tokenize('A,(short,boring test)');
$expected = array('A', '(short,boring test)'); $expected = array('A', '(short,boring test)');
$this->assertEquals($expected, $result); $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)'); $expected = array('A', '(short,more interesting( test)');
$this->assertEquals($expected, $result); $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))'); $expected = array('A', '(short,very interesting( test))');
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::tokenize('"single tag"', ' ', '"', '"'); $result = CakeText::tokenize('"single tag"', ' ', '"', '"');
$expected = array('"single tag"'); $expected = array('"single tag"');
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::tokenize('tagA "single tag" tagB', ' ', '"', '"'); $result = CakeText::tokenize('tagA "single tag" tagB', ' ', '"', '"');
$expected = array('tagA', '"single tag"', 'tagB'); $expected = array('tagA', '"single tag"', 'tagB');
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$result = String::tokenize(''); $result = CakeText::tokenize('');
$expected = array(); $expected = array();
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
/**
* testReplaceWithQuestionMarkInString method
*
* @return void
*/
public function testReplaceWithQuestionMarkInString() { public function testReplaceWithQuestionMarkInString() {
$string = ':a, :b and :c?'; $string = ':a, :b and :c?';
$expected = '2 and 3?'; $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); $this->assertEquals($expected, $result);
} }
@ -323,7 +328,7 @@ class StringTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testWordWrap($text, $width, $break = "\n", $cut = false) { 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); $expected = wordwrap($text, $width, $break, $cut);
$this->assertTextEquals($expected, $result, 'Text not wrapped same as built-in function.'); $this->assertTextEquals($expected, $result, 'Text not wrapped same as built-in function.');
} }
@ -357,7 +362,7 @@ class StringTest extends CakeTestCase {
*/ */
public function testWordWrapUnicodeAware() { public function testWordWrapUnicodeAware() {
$text = 'Но вим омниюм факёльиси элыктрам, мюнырэ лэгыры векж ыт. Выльёт квюандо нюмквуам ты кюм. Зыд эю рыбюм.'; $text = 'Но вим омниюм факёльиси элыктрам, мюнырэ лэгыры векж ыт. Выльёт квюандо нюмквуам ты кюм. Зыд эю рыбюм.';
$result = String::wordWrap($text, 33, "\n", true); $result = CakeText::wordWrap($text, 33, "\n", true);
$expected = <<<TEXT $expected = <<<TEXT
Но вим омниюм факёльиси элыктрам, Но вим омниюм факёльиси элыктрам,
мюнырэ лэгыры векж ыт. Выльёт квю мюнырэ лэгыры векж ыт. Выльёт квю
@ -367,7 +372,7 @@ TEXT;
$this->assertTextEquals($expected, $result, 'Text not wrapped.'); $this->assertTextEquals($expected, $result, 'Text not wrapped.');
$text = 'Но вим омниюм факёльиси элыктрам, мюнырэ лэгыры векж ыт. Выльёт квюандо нюмквуам ты кюм. Зыд эю рыбюм.'; $text = 'Но вим омниюм факёльиси элыктрам, мюнырэ лэгыры векж ыт. Выльёт квюандо нюмквуам ты кюм. Зыд эю рыбюм.';
$result = String::wordWrap($text, 33, "\n"); $result = CakeText::wordWrap($text, 33, "\n");
$expected = <<<TEXT $expected = <<<TEXT
Но вим омниюм факёльиси элыктрам, Но вим омниюм факёльиси элыктрам,
мюнырэ лэгыры векж ыт. Выльёт мюнырэ лэгыры векж ыт. Выльёт
@ -384,7 +389,7 @@ TEXT;
*/ */
public function testWrap() { public function testWrap() {
$text = 'This is the song that never ends. This is the song that never ends. This is the song that never ends.'; $text = 'This is the song that never ends. This is the song that never ends. This is the song that never ends.';
$result = String::wrap($text, 33); $result = CakeText::wrap($text, 33);
$expected = <<<TEXT $expected = <<<TEXT
This is the song that never ends. This is the song that never ends.
This is the song that never ends. This is the song that never ends.
@ -392,7 +397,7 @@ This is the song that never ends.
TEXT; TEXT;
$this->assertTextEquals($expected, $result, 'Text not wrapped.'); $this->assertTextEquals($expected, $result, 'Text not wrapped.');
$result = String::wrap($text, array('width' => 20, 'wordWrap' => false)); $result = CakeText::wrap($text, array('width' => 20, 'wordWrap' => false));
$expected = 'This is the song th' . "\n" . $expected = 'This is the song th' . "\n" .
'at never ends. This' . "\n" . 'at never ends. This' . "\n" .
' is the song that n' . "\n" . ' is the song that n' . "\n" .
@ -402,7 +407,7 @@ TEXT;
$this->assertTextEquals($expected, $result, 'Text not wrapped.'); $this->assertTextEquals($expected, $result, 'Text not wrapped.');
$text = 'Но вим омниюм факёльиси элыктрам, мюнырэ лэгыры векж ыт. Выльёт квюандо нюмквуам ты кюм. Зыд эю рыбюм.'; $text = 'Но вим омниюм факёльиси элыктрам, мюнырэ лэгыры векж ыт. Выльёт квюандо нюмквуам ты кюм. Зыд эю рыбюм.';
$result = String::wrap($text, 33); $result = CakeText::wrap($text, 33);
$expected = <<<TEXT $expected = <<<TEXT
Но вим омниюм факёльиси элыктрам, Но вим омниюм факёльиси элыктрам,
мюнырэ лэгыры векж ыт. Выльёт мюнырэ лэгыры векж ыт. Выльёт
@ -419,7 +424,7 @@ TEXT;
*/ */
public function testWrapIndent() { public function testWrapIndent() {
$text = 'This is the song that never ends. This is the song that never ends. This is the song that never ends.'; $text = 'This is the song that never ends. This is the song that never ends. This is the song that never ends.';
$result = String::wrap($text, array('width' => 33, 'indent' => "\t", 'indentAt' => 1)); $result = CakeText::wrap($text, array('width' => 33, 'indent' => "\t", 'indentAt' => 1));
$expected = <<<TEXT $expected = <<<TEXT
This is the song that never ends. This is the song that never ends.
This is the song that never ends. This is the song that never ends.

View file

@ -26,7 +26,7 @@ App::uses('TextHelper', 'View/Helper');
*/ */
class TextHelperTestObject extends TextHelper { class TextHelperTestObject extends TextHelper {
public function attach(StringMock $string) { public function attach(CakeTextMock $string) {
$this->_engine = $string; $this->_engine = $string;
} }
@ -37,11 +37,11 @@ class TextHelperTestObject extends TextHelper {
} }
/** /**
* StringMock class * CakeTextMock class
* *
* @package Cake.Test.Case.View.Helper * @package Cake.Test.Case.View.Helper
*/ */
class StringMock { class CakeTextMock {
} }
/** /**
@ -81,11 +81,11 @@ class TextHelperTest extends CakeTestCase {
$methods = array( $methods = array(
'highlight', 'stripLinks', 'truncate', 'tail', 'excerpt', 'toList', 'highlight', 'stripLinks', 'truncate', 'tail', 'excerpt', 'toList',
); );
$String = $this->getMock('StringMock', $methods); $CakeText = $this->getMock('CakeTextMock', $methods);
$Text = new TextHelperTestObject($this->View, array('engine' => 'StringMock')); $Text = new TextHelperTestObject($this->View, array('engine' => 'CakeTextMock'));
$Text->attach($String); $Text->attach($CakeText);
foreach ($methods as $method) { foreach ($methods as $method) {
$String->expects($this->at(0))->method($method); $CakeText->expects($this->at(0))->method($method);
$Text->{$method}('who', 'what', 'when', 'where', 'how'); $Text->{$method}('who', 'what', 'when', 'where', 'how');
} }
} }

View file

@ -0,0 +1,692 @@
<?php
/**
* String handling methods.
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package Cake.Utility
* @since CakePHP(tm) v 1.2.0.5551
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* String handling methods.
*
* @package Cake.Utility
*/
class CakeText {
/**
* 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: `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: `/(?<!\\)\:%s/`
* (Overwrites before, after, breaks escape / clean)
* - clean: A boolean or array with instructions for CakeText::cleanInsert
*
* @param string $str A string containing variable placeholders
* @param array $data A key => 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(
'/(?<!%s)%s%%s%s/',
preg_quote($options['escape'], '/'),
str_replace('%', '%%', preg_quote($options['before'], '/')),
str_replace('%', '%%', preg_quote($options['after'], '/'))
);
}
if (strpos($str, '?') !== false && is_numeric(key($data))) {
$offset = 0;
while (($pos = strpos($str, '?', $offset)) !== false) {
$val = array_shift($data);
$offset = $pos + strlen($val);
$str = substr_replace($str, $val, $pos, 1);
}
return ($options['clean']) ? CakeText::cleanInsert($str, $options) : $str;
}
asort($data);
$dataKeys = array_keys($data);
$hashKeys = array_map('crc32', $dataKeys);
$tempData = array_combine($dataKeys, $hashKeys);
krsort($tempData);
foreach ($tempData as $key => $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 = 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 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) {
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' => '<span class="highlight">\1</span>',
'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 (<a href=....).
*
* @param string $text Text
* @return string The text without links
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::stripLinks
*/
public static function stripLinks($text) {
return preg_replace('|<a\s+[^>]+>|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};|&#x[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};|&#x[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 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);
}
}

View file

@ -19,7 +19,7 @@
*/ */
App::uses('CakeLog', 'Log'); App::uses('CakeLog', 'Log');
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
/** /**
* Provide custom logging and error handling. * Provide custom logging and error handling.
@ -337,7 +337,7 @@ class Debugger {
$trace['path'] = self::trimPath($trace['file']); $trace['path'] = self::trimPath($trace['file']);
$trace['reference'] = $reference; $trace['reference'] = $reference;
unset($trace['object'], $trace['args']); unset($trace['object'], $trace['args']);
$back[] = String::insert($tpl, $trace, array('before' => '{:', 'after' => '}')); $back[] = CakeText::insert($tpl, $trace, array('before' => '{:', 'after' => '}'));
} }
} }
@ -637,7 +637,7 @@ class Debugger {
* *
* `Debugger::addFormat('custom', $data);` * `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. * replacement. The template vars should be in a `{:id}` style.
* An error formatter can have the following keys: * An error formatter can have the following keys:
* *
@ -775,7 +775,7 @@ class Debugger {
if (isset($tpl['links'])) { if (isset($tpl['links'])) {
foreach ($tpl['links'] as $key => $val) { foreach ($tpl['links'] as $key => $val) {
$links[$key] = String::insert($val, $data, $insertOpts); $links[$key] = CakeText::insert($val, $data, $insertOpts);
} }
} }
@ -791,14 +791,14 @@ class Debugger {
if (is_array($value)) { if (is_array($value)) {
$value = implode("\n", $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); $links = implode(' ', $links);
if (isset($tpl['callback']) && is_callable($tpl['callback'])) { if (isset($tpl['callback']) && is_callable($tpl['callback'])) {
return call_user_func($tpl['callback'], $data, compact('links', 'info')); 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);
} }
/** /**

View file

@ -14,7 +14,7 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @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 * Library of array functions for manipulating and extracting data
@ -112,7 +112,7 @@ class Hash {
if (strpos($path, '[') === false) { if (strpos($path, '[') === false) {
$tokens = explode('.', $path); $tokens = explode('.', $path);
} else { } else {
$tokens = String::tokenize($path, '.', '[', ']'); $tokens = CakeText::tokenize($path, '.', '[', ']');
} }
$_key = '__set_item__'; $_key = '__set_item__';
@ -258,7 +258,7 @@ class Hash {
if (strpos($path, '[') === false) { if (strpos($path, '[') === false) {
$tokens = explode('.', $path); $tokens = explode('.', $path);
} else { } else {
$tokens = String::tokenize($path, '.', '[', ']'); $tokens = CakeText::tokenize($path, '.', '[', ']');
} }
if (strpos($path, '{') === false && strpos($path, '[') === false) { if (strpos($path, '{') === false && strpos($path, '[') === false) {
@ -341,7 +341,7 @@ class Hash {
if (strpos($path, '[') === false) { if (strpos($path, '[') === false) {
$tokens = explode('.', $path); $tokens = explode('.', $path);
} else { } else {
$tokens = String::tokenize($path, '.', '[', ']'); $tokens = CakeText::tokenize($path, '.', '[', ']');
} }
if (strpos($path, '{') === false && strpos($path, '[') === false) { if (strpos($path, '{') === false && strpos($path, '[') === false) {

View file

@ -16,7 +16,7 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @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 * Security Library contains utility methods related to security
@ -63,7 +63,7 @@ class Security {
* @return string Hash * @return string Hash
*/ */
public static function generateAuthKey() { public static function generateAuthKey() {
return Security::hash(String::uuid()); return Security::hash(CakeText::uuid());
} }
/** /**

View file

@ -16,7 +16,7 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('String', 'Utility'); App::uses('CakeText', 'Utility');
App::uses('Hash', 'Utility'); App::uses('Hash', 'Utility');
/** /**
@ -549,7 +549,7 @@ class Set {
return null; return null;
} }
if (is_string($path) && strpos($path, '{') !== false) { if (is_string($path) && strpos($path, '{') !== false) {
$path = String::tokenize($path, '.', '{', '}'); $path = CakeText::tokenize($path, '.', '{', '}');
} elseif (is_string($path)) { } elseif (is_string($path)) {
$path = explode('.', $path); $path = explode('.', $path);
} }

View file

@ -15,678 +15,13 @@
* @since CakePHP(tm) v 1.2.0.5551 * @since CakePHP(tm) v 1.2.0.5551
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('CakeText', 'Utility');
/** /**
* String handling methods. * String handling methods.
* *
* @package Cake.Utility * @package Cake.Utility
* @deprecated 3.0.0 Deprecated since version 2.7, use CakeText class instead.
*/ */
class String { class String extends CakeText {
/**
* 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: `/(?<!\\)\:%s/`
* (Overwrites before, after, breaks escape / clean)
* - clean: A boolean or array with instructions for String::cleanInsert
*
* @param string $str A string containing variable placeholders
* @param array $data A key => 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(
'/(?<!%s)%s%%s%s/',
preg_quote($options['escape'], '/'),
str_replace('%', '%%', preg_quote($options['before'], '/')),
str_replace('%', '%%', preg_quote($options['after'], '/'))
);
}
if (strpos($str, '?') !== false && is_numeric(key($data))) {
$offset = 0;
while (($pos = strpos($str, '?', $offset)) !== false) {
$val = array_shift($data);
$offset = $pos + strlen($val);
$str = substr_replace($str, $val, $pos, 1);
}
return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
}
asort($data);
$dataKeys = array_keys($data);
$hashKeys = array_map('crc32', $dataKeys);
$tempData = array_combine($dataKeys, $hashKeys);
krsort($tempData);
foreach ($tempData as $key => $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 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) {
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' => '<span class="highlight">\1</span>',
'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 (<a href=....).
*
* @param string $text Text
* @return string The text without links
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::stripLinks
*/
public static function stripLinks($text) {
return preg_replace('|<a\s+[^>]+>|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
*/
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};|&#x[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};|&#x[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);
}
} }

View file

@ -49,7 +49,7 @@ class TextHelper extends AppHelper {
protected $_placeholders = array(); protected $_placeholders = array();
/** /**
* String utility instance * CakeText utility instance
* *
* @var stdClass * @var stdClass
*/ */
@ -60,7 +60,7 @@ class TextHelper extends AppHelper {
* *
* ### Settings: * ### Settings:
* *
* - `engine` Class name to use to replace String functionality. * - `engine` Class name to use to replace CakeText functionality.
* The class needs to be placed in the `Utility` directory. * The class needs to be placed in the `Utility` directory.
* *
* @param View $View the view object the helper is attached to. * @param View $View the view object the helper is attached to.
@ -68,7 +68,7 @@ class TextHelper extends AppHelper {
* @throws CakeException when the engine class could not be found. * @throws CakeException when the engine class could not be found.
*/ */
public function __construct(View $View, $settings = array()) { public function __construct(View $View, $settings = array()) {
$settings = Hash::merge(array('engine' => 'String'), $settings); $settings = Hash::merge(array('engine' => 'CakeText'), $settings);
parent::__construct($View, $settings); parent::__construct($View, $settings);
list($plugin, $engineClass) = pluginSplit($settings['engine'], true); list($plugin, $engineClass) = pluginSplit($settings['engine'], true);
App::uses($engineClass, $plugin . 'Utility'); App::uses($engineClass, $plugin . 'Utility');
@ -80,7 +80,7 @@ class TextHelper extends AppHelper {
} }
/** /**
* Call methods from String utility class * Call methods from CakeText utility class
* *
* @param string $method Method to call. * @param string $method Method to call.
* @param array $params Parameters to pass to method. * @param array $params Parameters to pass to method.
@ -225,7 +225,7 @@ class TextHelper extends AppHelper {
* @param string $phrase The phrase that will be searched * @param string $phrase The phrase that will be searched
* @param array $options An array of html attributes and options. * @param array $options An array of html attributes and options.
* @return string The highlighted text * @return string The highlighted text
* @see String::highlight() * @see CakeText::highlight()
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::highlight * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::highlight
*/ */
public function highlight($text, $phrase, $options = array()) { public function highlight($text, $phrase, $options = array()) {
@ -260,7 +260,7 @@ class TextHelper extends AppHelper {
* *
* @param string $text Text * @param string $text Text
* @return string The text without links * @return string The text without links
* @see String::stripLinks() * @see CakeText::stripLinks()
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::stripLinks * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::stripLinks
*/ */
public function stripLinks($text) { public function stripLinks($text) {
@ -283,7 +283,7 @@ class TextHelper extends AppHelper {
* @param int $length Length of returned string, including ellipsis. * @param int $length Length of returned string, including ellipsis.
* @param array $options An array of html attributes and options. * @param array $options An array of html attributes and options.
* @return string Trimmed string. * @return string Trimmed string.
* @see String::truncate() * @see CakeText::truncate()
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::truncate * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::truncate
*/ */
public function truncate($text, $length = 100, $options = array()) { public function truncate($text, $length = 100, $options = array()) {
@ -305,7 +305,7 @@ class TextHelper extends AppHelper {
* @param int $length Length of returned string, including ellipsis. * @param int $length Length of returned string, including ellipsis.
* @param array $options An array of html attributes and options. * @param array $options An array of html attributes and options.
* @return string Trimmed string. * @return string Trimmed string.
* @see String::tail() * @see CakeText::tail()
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::tail * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::tail
*/ */
public function tail($text, $length = 100, $options = array()) { public function tail($text, $length = 100, $options = array()) {
@ -321,7 +321,7 @@ class TextHelper extends AppHelper {
* @param int $radius The amount of characters that will be returned on each side of the founded phrase * @param int $radius The amount of characters that will be returned on each side of the founded phrase
* @param string $ending Ending that will be appended * @param string $ending Ending that will be appended
* @return string Modified string * @return string Modified string
* @see String::excerpt() * @see CakeText::excerpt()
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::excerpt * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::excerpt
*/ */
public function excerpt($text, $phrase, $radius = 100, $ending = '...') { public function excerpt($text, $phrase, $radius = 100, $ending = '...') {
@ -335,7 +335,7 @@ class TextHelper extends AppHelper {
* @param string $and The word used to join the last and second last items together with. Defaults to 'and'. * @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 ', '. * @param string $separator The separator used to join all the other items together. Defaults to ', '.
* @return string The glued together string. * @return string The glued together string.
* @see String::toList() * @see CakeText::toList()
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::toList * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::toList
*/ */
public function toList($list, $and = null, $separator = ', ') { public function toList($list, $and = null, $separator = ', ') {