mirror of
https://github.com/brmlab/brmsklad.git
synced 2025-10-29 23:33:58 +01:00
Upgrade CakePHP from 2.2.5 to 2.9.5
This commit is contained in:
parent
5a580df460
commit
235a541597
793 changed files with 60746 additions and 23753 deletions
|
|
@ -4,19 +4,18 @@
|
|||
*
|
||||
* Methods to make numbers more readable.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Utility
|
||||
* @since CakePHP(tm) v 0.10.0.1076
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -30,24 +29,42 @@
|
|||
class CakeNumber {
|
||||
|
||||
/**
|
||||
* Currencies supported by the helper. You can add additional currency formats
|
||||
* Currencies supported by the helper. You can add additional currency formats
|
||||
* with CakeNumber::addFormat
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $_currencies = array(
|
||||
'AUD' => array(
|
||||
'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after',
|
||||
'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
|
||||
'fractionExponent' => 2
|
||||
),
|
||||
'CAD' => array(
|
||||
'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after',
|
||||
'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
|
||||
'fractionExponent' => 2
|
||||
),
|
||||
'USD' => array(
|
||||
'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after',
|
||||
'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true
|
||||
),
|
||||
'GBP' => array(
|
||||
'wholeSymbol' => '£', 'wholePosition' => 'before', 'fractionSymbol' => 'p', 'fractionPosition' => 'after',
|
||||
'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()','escape' => false
|
||||
'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
|
||||
'fractionExponent' => 2
|
||||
),
|
||||
'EUR' => array(
|
||||
'wholeSymbol' => '€', 'wholePosition' => 'before', 'fractionSymbol' => false, 'fractionPosition' => 'after',
|
||||
'zero' => 0, 'places' => 2, 'thousands' => '.', 'decimals' => ',', 'negative' => '()', 'escape' => false
|
||||
)
|
||||
'wholeSymbol' => '€', 'wholePosition' => 'before', 'fractionSymbol' => false, 'fractionPosition' => 'after',
|
||||
'zero' => 0, 'places' => 2, 'thousands' => '.', 'decimals' => ',', 'negative' => '()', 'escape' => true,
|
||||
'fractionExponent' => 0
|
||||
),
|
||||
'GBP' => array(
|
||||
'wholeSymbol' => '£', 'wholePosition' => 'before', 'fractionSymbol' => 'p', 'fractionPosition' => 'after',
|
||||
'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
|
||||
'fractionExponent' => 2
|
||||
),
|
||||
'JPY' => array(
|
||||
'wholeSymbol' => '¥', 'wholePosition' => 'before', 'fractionSymbol' => false, 'fractionPosition' => 'after',
|
||||
'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
|
||||
'fractionExponent' => 0
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
@ -56,33 +73,41 @@ class CakeNumber {
|
|||
* @var array
|
||||
*/
|
||||
protected static $_currencyDefaults = array(
|
||||
'wholeSymbol' => '', 'wholePosition' => 'before', 'fractionSymbol' => '', 'fractionPosition' => 'after',
|
||||
'zero' => '0', 'places' => 2, 'thousands' => ',', 'decimals' => '.','negative' => '()', 'escape' => true,
|
||||
'wholeSymbol' => '', 'wholePosition' => 'before', 'fractionSymbol' => false, 'fractionPosition' => 'after',
|
||||
'zero' => '0', 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
|
||||
'fractionExponent' => 2
|
||||
);
|
||||
|
||||
/**
|
||||
* Default currency used by CakeNumber::currency()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $_defaultCurrency = 'USD';
|
||||
|
||||
/**
|
||||
* If native number_format() should be used. If >= PHP5.4
|
||||
*
|
||||
* @var boolean
|
||||
* @var bool
|
||||
*/
|
||||
protected static $_numberFormatSupport = null;
|
||||
|
||||
/**
|
||||
* Formats a number with a level of precision.
|
||||
*
|
||||
* @param float $number A floating point number.
|
||||
* @param integer $precision The precision of the returned number.
|
||||
* @param float $value A floating point number.
|
||||
* @param int $precision The precision of the returned number.
|
||||
* @return float Formatted float.
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::precision
|
||||
*/
|
||||
public static function precision($number, $precision = 3) {
|
||||
return sprintf("%01.{$precision}F", $number);
|
||||
public static function precision($value, $precision = 3) {
|
||||
return sprintf("%01.{$precision}f", $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted-for-humans file size.
|
||||
*
|
||||
* @param integer $size Size in bytes
|
||||
* @param int $size Size in bytes
|
||||
* @return string Human readable size
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::toReadableSize
|
||||
*/
|
||||
|
|
@ -91,38 +116,84 @@ class CakeNumber {
|
|||
case $size < 1024:
|
||||
return __dn('cake', '%d Byte', '%d Bytes', $size, $size);
|
||||
case round($size / 1024) < 1024:
|
||||
return __d('cake', '%d 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', '%.2f 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', '%.2f GB', self::precision($size / 1024 / 1024 / 1024, 2));
|
||||
return __d('cake', '%s GB', static::precision($size / 1024 / 1024 / 1024, 2));
|
||||
default:
|
||||
return __d('cake', '%.2f TB', self::precision($size / 1024 / 1024 / 1024 / 1024, 2));
|
||||
return __d('cake', '%s TB', static::precision($size / 1024 / 1024 / 1024 / 1024, 2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts filesize from human readable string to bytes
|
||||
*
|
||||
* @param string $size Size in human readable string like '5MB', '5M', '500B', '50kb' etc.
|
||||
* @param mixed $default Value to be returned when invalid size was used, for example 'Unknown type'
|
||||
* @return mixed Number of bytes as integer on success, `$default` on failure if not false
|
||||
* @throws CakeException On invalid Unit type.
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::fromReadableSize
|
||||
*/
|
||||
public static function fromReadableSize($size, $default = false) {
|
||||
if (ctype_digit($size)) {
|
||||
return (int)$size;
|
||||
}
|
||||
$size = strtoupper($size);
|
||||
|
||||
$l = -2;
|
||||
$i = array_search(substr($size, -2), array('KB', 'MB', 'GB', 'TB', 'PB'));
|
||||
if ($i === false) {
|
||||
$l = -1;
|
||||
$i = array_search(substr($size, -1), array('K', 'M', 'G', 'T', 'P'));
|
||||
}
|
||||
if ($i !== false) {
|
||||
$size = substr($size, 0, $l);
|
||||
return $size * pow(1024, $i + 1);
|
||||
}
|
||||
|
||||
if (substr($size, -1) === 'B' && ctype_digit(substr($size, 0, -1))) {
|
||||
$size = substr($size, 0, -1);
|
||||
return (int)$size;
|
||||
}
|
||||
|
||||
if ($default !== false) {
|
||||
return $default;
|
||||
}
|
||||
throw new CakeException(__d('cake_dev', 'No unit type.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a number into a percentage string.
|
||||
*
|
||||
* @param float $number A floating point number
|
||||
* @param integer $precision The precision of the returned number
|
||||
* Options:
|
||||
*
|
||||
* - `multiply`: Multiply the input value by 100 for decimal percentages.
|
||||
*
|
||||
* @param float $value A floating point number
|
||||
* @param int $precision The precision of the returned number
|
||||
* @param array $options Options
|
||||
* @return string Percentage string
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::toPercentage
|
||||
*/
|
||||
public static function toPercentage($number, $precision = 2) {
|
||||
return self::precision($number, $precision) . '%';
|
||||
public static function toPercentage($value, $precision = 2, $options = array()) {
|
||||
$options += array('multiply' => false);
|
||||
if ($options['multiply']) {
|
||||
$value *= 100;
|
||||
}
|
||||
return static::precision($value, $precision) . '%';
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a number into a currency format.
|
||||
*
|
||||
* @param float $number A floating point number
|
||||
* @param integer $options if int then places, if string then before, if (,.-) then use it
|
||||
* @param float $value A floating point number
|
||||
* @param int $options If integer then places, if string then before, if (,.-) then use it
|
||||
* or array with places and before keys
|
||||
* @return string formatted number
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::format
|
||||
*/
|
||||
public static function format($number, $options = false) {
|
||||
public static function format($value, $options = false) {
|
||||
$places = 0;
|
||||
if (is_int($options)) {
|
||||
$places = $options;
|
||||
|
|
@ -145,11 +216,13 @@ class CakeNumber {
|
|||
|
||||
$escape = true;
|
||||
if (is_array($options)) {
|
||||
$options = array_merge(array('before' => '$', 'places' => 2, 'thousands' => ',', 'decimals' => '.'), $options);
|
||||
$defaults = array('before' => '$', 'places' => 2, 'thousands' => ',', 'decimals' => '.');
|
||||
$options += $defaults;
|
||||
extract($options);
|
||||
}
|
||||
|
||||
$out = $before . self::_numberFormat($number, $places, $decimals, $thousands) . $after;
|
||||
$value = static::_numberFormat($value, $places, '.', '');
|
||||
$out = $before . static::_numberFormat($value, $places, $decimals, $thousands) . $after;
|
||||
|
||||
if ($escape) {
|
||||
return h($out);
|
||||
|
|
@ -157,34 +230,59 @@ class CakeNumber {
|
|||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a number into a currency format to show deltas (signed differences in value).
|
||||
*
|
||||
* ### Options
|
||||
*
|
||||
* - `places` - Number of decimal places to use. ie. 2
|
||||
* - `fractionExponent` - Fraction exponent of this specific currency. Defaults to 2.
|
||||
* - `before` - The string to place before whole numbers. ie. '['
|
||||
* - `after` - The string to place after decimal numbers. ie. ']'
|
||||
* - `thousands` - Thousands separator ie. ','
|
||||
* - `decimals` - Decimal separator symbol ie. '.'
|
||||
*
|
||||
* @param float $value A floating point number
|
||||
* @param array $options Options list.
|
||||
* @return string formatted delta
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::formatDelta
|
||||
*/
|
||||
public static function formatDelta($value, $options = array()) {
|
||||
$places = isset($options['places']) ? $options['places'] : 0;
|
||||
$value = static::_numberFormat($value, $places, '.', '');
|
||||
$sign = $value > 0 ? '+' : '';
|
||||
$options['before'] = isset($options['before']) ? $options['before'] . $sign : $sign;
|
||||
return static::format($value, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternative number_format() to accommodate multibyte decimals and thousands < PHP 5.4
|
||||
*
|
||||
* @param float $number
|
||||
* @param integer $places
|
||||
* @param string $decimals
|
||||
* @param string $thousands
|
||||
* @param float $value Value to format.
|
||||
* @param int $places Decimal places to use.
|
||||
* @param string $decimals Decimal position string.
|
||||
* @param string $thousands Thousands separator string.
|
||||
* @return string
|
||||
*/
|
||||
protected static function _numberFormat($number, $places = 0, $decimals = '.', $thousands = ',') {
|
||||
if (!isset(self::$_numberFormatSupport)) {
|
||||
self::$_numberFormatSupport = version_compare(PHP_VERSION, '5.4.0', '>=');
|
||||
protected static function _numberFormat($value, $places = 0, $decimals = '.', $thousands = ',') {
|
||||
if (!isset(static::$_numberFormatSupport)) {
|
||||
static::$_numberFormatSupport = version_compare(PHP_VERSION, '5.4.0', '>=');
|
||||
}
|
||||
if (self::$_numberFormatSupport) {
|
||||
return number_format($number, $places, $decimals, $thousands);
|
||||
if (static::$_numberFormatSupport) {
|
||||
return number_format($value, $places, $decimals, $thousands);
|
||||
}
|
||||
$number = number_format($number, $places, '.', '');
|
||||
$value = number_format($value, $places, '.', '');
|
||||
$after = '';
|
||||
$foundDecimal = strpos($number, '.');
|
||||
$foundDecimal = strpos($value, '.');
|
||||
if ($foundDecimal !== false) {
|
||||
$after = substr($number, $foundDecimal);
|
||||
$number = substr($number, 0, $foundDecimal);
|
||||
$after = substr($value, $foundDecimal);
|
||||
$value = substr($value, 0, $foundDecimal);
|
||||
}
|
||||
while (($foundThousand = preg_replace('/(\d+)(\d\d\d)/', '\1 \2', $number)) != $number) {
|
||||
$number = $foundThousand;
|
||||
while (($foundThousand = preg_replace('/(\d+)(\d\d\d)/', '\1 \2', $value)) !== $value) {
|
||||
$value = $foundThousand;
|
||||
}
|
||||
$number .= $after;
|
||||
return strtr($number, array(' ' => $thousands, '.' => $decimals));
|
||||
$value .= $after;
|
||||
return strtr($value, array(' ' => $thousands, '.' => $decimals));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -203,36 +301,40 @@ class CakeNumber {
|
|||
* ie. '$'. `before` is an alias for `wholeSymbol`.
|
||||
* - `after` - The currency symbol to place after decimal numbers
|
||||
* ie. 'c'. Set to boolean false to use no decimal symbol.
|
||||
* eg. 0.35 => $0.35. `after` is an alias for `fractionSymbol`
|
||||
* eg. 0.35 => $0.35. `after` is an alias for `fractionSymbol`
|
||||
* - `zero` - The text to use for zero values, can be a
|
||||
* string or a number. ie. 0, 'Free!'
|
||||
* - `places` - Number of decimal places to use. ie. 2
|
||||
* - `fractionExponent` - Fraction exponent of this specific currency. Defaults to 2.
|
||||
* - `thousands` - Thousands separator ie. ','
|
||||
* - `decimals` - Decimal separator symbol ie. '.'
|
||||
* - `negative` - Symbol for negative numbers. If equal to '()',
|
||||
* the number will be wrapped with ( and )
|
||||
* - `escape` - Should the output be escaped for html special characters.
|
||||
* The default value for this option is controlled by the currency settings.
|
||||
* By default the EUR, and GBP contain HTML encoded symbols. If you require non HTML
|
||||
* encoded symbols you will need to update the settings with the correct bytes.
|
||||
* By default all currencies contain utf-8 symbols and don't need this changed. If you require
|
||||
* non HTML encoded symbols you will need to update the settings with the correct bytes.
|
||||
*
|
||||
* @param float $number
|
||||
* @param float $value Value to format.
|
||||
* @param string $currency Shortcut to default options. Valid values are
|
||||
* 'USD', 'EUR', 'GBP', otherwise set at least 'before' and 'after' options.
|
||||
* @param array $options
|
||||
* @param array $options Options list.
|
||||
* @return string Number formatted as a currency.
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::currency
|
||||
*/
|
||||
public static function currency($number, $currency = 'USD', $options = array()) {
|
||||
$default = self::$_currencyDefaults;
|
||||
public static function currency($value, $currency = null, $options = array()) {
|
||||
$defaults = static::$_currencyDefaults;
|
||||
if ($currency === null) {
|
||||
$currency = static::defaultCurrency();
|
||||
}
|
||||
|
||||
if (isset(self::$_currencies[$currency])) {
|
||||
$default = self::$_currencies[$currency];
|
||||
if (isset(static::$_currencies[$currency])) {
|
||||
$defaults = static::$_currencies[$currency];
|
||||
} elseif (is_string($currency)) {
|
||||
$options['before'] = $currency;
|
||||
}
|
||||
|
||||
$options = array_merge($default, $options);
|
||||
$options += $defaults;
|
||||
|
||||
if (isset($options['before']) && $options['before'] !== '') {
|
||||
$options['wholeSymbol'] = $options['before'];
|
||||
|
|
@ -244,27 +346,28 @@ class CakeNumber {
|
|||
$result = $options['before'] = $options['after'] = null;
|
||||
|
||||
$symbolKey = 'whole';
|
||||
if ($number == 0 ) {
|
||||
if ($options['zero'] !== 0 ) {
|
||||
$value = (float)$value;
|
||||
if (!$value) {
|
||||
if ($options['zero'] !== 0) {
|
||||
return $options['zero'];
|
||||
}
|
||||
} elseif ($number < 1 && $number > -1 ) {
|
||||
} elseif ($value < 1 && $value > -1) {
|
||||
if ($options['fractionSymbol'] !== false) {
|
||||
$multiply = intval('1' . str_pad('', $options['places'], '0'));
|
||||
$number = $number * $multiply;
|
||||
$multiply = pow(10, $options['fractionExponent']);
|
||||
$value = $value * $multiply;
|
||||
$options['places'] = null;
|
||||
$symbolKey = 'fraction';
|
||||
}
|
||||
}
|
||||
|
||||
$position = $options[$symbolKey . 'Position'] != 'after' ? 'before' : 'after';
|
||||
$position = $options[$symbolKey . 'Position'] !== 'after' ? 'before' : 'after';
|
||||
$options[$position] = $options[$symbolKey . 'Symbol'];
|
||||
|
||||
$abs = abs($number);
|
||||
$result = self::format($abs, $options);
|
||||
$abs = abs($value);
|
||||
$result = static::format($abs, $options);
|
||||
|
||||
if ($number < 0 ) {
|
||||
if ($options['negative'] == '()') {
|
||||
if ($value < 0) {
|
||||
if ($options['negative'] === '()') {
|
||||
$result = '(' . $result . ')';
|
||||
} else {
|
||||
$result = $options['negative'] . $result;
|
||||
|
|
@ -274,14 +377,14 @@ class CakeNumber {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add a currency format to the Number helper. Makes reusing
|
||||
* Add a currency format to the Number helper. Makes reusing
|
||||
* currency formats easier.
|
||||
*
|
||||
* {{{ $number->addFormat('NOK', array('before' => 'Kr. ')); }}}
|
||||
* ``` $number->addFormat('NOK', array('before' => 'Kr. ')); ```
|
||||
*
|
||||
* You can now use `NOK` as a shortform when formatting currency amounts.
|
||||
*
|
||||
* {{{ $number->currency($value, 'NOK'); }}}
|
||||
* ``` $number->currency($value, 'NOK'); ```
|
||||
*
|
||||
* Added formats are merged with the defaults defined in CakeNumber::$_currencyDefaults
|
||||
* See CakeNumber::currency() for more information on the various options and their function.
|
||||
|
|
@ -293,7 +396,21 @@ class CakeNumber {
|
|||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter/setter for default currency
|
||||
*
|
||||
* @param string $currency Default currency string used by currency() if $currency argument is not provided
|
||||
* @return string Currency
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::defaultCurrency
|
||||
*/
|
||||
public static function defaultCurrency($currency = null) {
|
||||
if ($currency) {
|
||||
static::$_defaultCurrency = $currency;
|
||||
}
|
||||
return static::$_defaultCurrency;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
710
lib/Cake/Utility/CakeText.php
Normal file
710
lib/Cake/Utility/CakeText.php
Normal file
|
|
@ -0,0 +1,710 @@
|
|||
<?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 string 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 = 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: `/(?<!\\)\:%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 = 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' => '<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 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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,16 +1,17 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Utility
|
||||
* @since CakePHP(tm) v 0.9.2
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -57,7 +58,7 @@ class ClassRegistry {
|
|||
*
|
||||
* @return ClassRegistry instance
|
||||
*/
|
||||
public static function &getInstance() {
|
||||
public static function getInstance() {
|
||||
static $instance = array();
|
||||
if (!$instance) {
|
||||
$instance[0] = new ClassRegistry();
|
||||
|
|
@ -72,30 +73,29 @@ class ClassRegistry {
|
|||
* Examples
|
||||
* Simple Use: Get a Post model instance ```ClassRegistry::init('Post');```
|
||||
*
|
||||
* Expanded: ```array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model');```
|
||||
* Expanded: ```array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry');```
|
||||
*
|
||||
* Model Classes can accept optional ```array('id' => $id, 'table' => $table, 'ds' => $ds, 'alias' => $alias);```
|
||||
*
|
||||
* When $class is a numeric keyed array, multiple class instances will be stored in the registry,
|
||||
* no instance of the object will be returned
|
||||
* {{{
|
||||
* ```
|
||||
* array(
|
||||
* array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry'),
|
||||
* array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry'),
|
||||
* array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry')
|
||||
* );
|
||||
* }}}
|
||||
* ```
|
||||
*
|
||||
* @param string|array $class as a string or a single key => value array instance will be created,
|
||||
* stored in the registry and returned.
|
||||
* @param boolean $strict if set to true it will return false if the class was not found instead
|
||||
* @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) {
|
||||
$_this = ClassRegistry::getInstance();
|
||||
$false = false;
|
||||
$true = true;
|
||||
|
||||
if (is_array($class)) {
|
||||
$objects = $class;
|
||||
|
|
@ -105,19 +105,28 @@ class ClassRegistry {
|
|||
} else {
|
||||
$objects = array(array('class' => $class));
|
||||
}
|
||||
$defaults = isset($_this->_config['Model']) ? $_this->_config['Model'] : array();
|
||||
$defaults = array();
|
||||
if (isset($_this->_config['Model'])) {
|
||||
$defaults = $_this->_config['Model'];
|
||||
}
|
||||
$count = count($objects);
|
||||
$availableDs = array_keys(ConnectionManager::enumConnectionObjects());
|
||||
$availableDs = null;
|
||||
|
||||
foreach ($objects as $settings) {
|
||||
if (is_numeric($settings)) {
|
||||
trigger_error(__d('cake_dev', '(ClassRegistry::init() Attempted to create instance of a class with a numeric name'), E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($objects as $key => $settings) {
|
||||
if (is_array($settings)) {
|
||||
$pluginPath = null;
|
||||
$settings = array_merge($defaults, $settings);
|
||||
$settings += $defaults;
|
||||
$class = $settings['class'];
|
||||
|
||||
list($plugin, $class) = pluginSplit($class);
|
||||
if ($plugin) {
|
||||
$pluginPath = $plugin . '.';
|
||||
$settings['plugin'] = $plugin;
|
||||
}
|
||||
|
||||
if (empty($settings['alias'])) {
|
||||
|
|
@ -125,7 +134,8 @@ class ClassRegistry {
|
|||
}
|
||||
$alias = $settings['alias'];
|
||||
|
||||
if ($model = $_this->_duplicate($alias, $class)) {
|
||||
$model = $_this->_duplicate($alias, $class);
|
||||
if ($model) {
|
||||
$_this->map($alias, $class);
|
||||
return $model;
|
||||
}
|
||||
|
|
@ -144,6 +154,9 @@ class ClassRegistry {
|
|||
$defaultProperties = $reflection->getDefaultProperties();
|
||||
if (isset($defaultProperties['useDbConfig'])) {
|
||||
$useDbConfig = $defaultProperties['useDbConfig'];
|
||||
if ($availableDs === null) {
|
||||
$availableDs = array_keys(ConnectionManager::enumConnectionObjects());
|
||||
}
|
||||
if (in_array('test_' . $useDbConfig, $availableDs)) {
|
||||
$useDbConfig = 'test_' . $useDbConfig;
|
||||
}
|
||||
|
|
@ -157,37 +170,27 @@ class ClassRegistry {
|
|||
} else {
|
||||
$instance = $reflection->newInstance();
|
||||
}
|
||||
if ($strict) {
|
||||
$instance = ($instance instanceof Model) ? $instance : null;
|
||||
if ($strict && !$instance instanceof Model) {
|
||||
$instance = null;
|
||||
}
|
||||
}
|
||||
if (!isset($instance)) {
|
||||
$appModel = 'AppModel';
|
||||
if ($strict) {
|
||||
return false;
|
||||
} elseif ($plugin && class_exists($plugin . 'AppModel')) {
|
||||
$appModel = $plugin . 'AppModel';
|
||||
} else {
|
||||
$appModel = 'AppModel';
|
||||
}
|
||||
if (!empty($appModel)) {
|
||||
$settings['name'] = $class;
|
||||
$instance = new $appModel($settings);
|
||||
}
|
||||
|
||||
if (!isset($instance)) {
|
||||
trigger_error(__d('cake_dev', '(ClassRegistry::init() could not create instance of %s', $class), E_USER_WARNING);
|
||||
return $false;
|
||||
}
|
||||
$settings['name'] = $class;
|
||||
$instance = new $appModel($settings);
|
||||
}
|
||||
$_this->map($alias, $class);
|
||||
} elseif (is_numeric($settings)) {
|
||||
trigger_error(__d('cake_dev', '(ClassRegistry::init() Attempted to create instance of a class with a numeric name'), E_USER_WARNING);
|
||||
return $false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($count > 1) {
|
||||
return $true;
|
||||
return true;
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
|
@ -195,9 +198,9 @@ class ClassRegistry {
|
|||
/**
|
||||
* Add $object to the registry, associating it with the name $key.
|
||||
*
|
||||
* @param string $key Key for the object in registry
|
||||
* @param object $object Object to store
|
||||
* @return boolean True if the object was written, false if $key already exists
|
||||
* @param string $key Key for the object in registry
|
||||
* @param object $object Object to store
|
||||
* @return bool True if the object was written, false if $key already exists
|
||||
*/
|
||||
public static function addObject($key, $object) {
|
||||
$_this = ClassRegistry::getInstance();
|
||||
|
|
@ -212,7 +215,7 @@ class ClassRegistry {
|
|||
/**
|
||||
* Remove object which corresponds to given key.
|
||||
*
|
||||
* @param string $key Key of object to remove from registry
|
||||
* @param string $key Key of object to remove from registry
|
||||
* @return void
|
||||
*/
|
||||
public static function removeObject($key) {
|
||||
|
|
@ -227,17 +230,13 @@ class ClassRegistry {
|
|||
* Returns true if given key is present in the ClassRegistry.
|
||||
*
|
||||
* @param string $key Key to look for
|
||||
* @return boolean true if key exists in registry, false otherwise
|
||||
* @return bool true if key exists in registry, false otherwise
|
||||
*/
|
||||
public static function isKeySet($key) {
|
||||
$_this = ClassRegistry::getInstance();
|
||||
$key = Inflector::underscore($key);
|
||||
if (isset($_this->_objects[$key])) {
|
||||
return true;
|
||||
} elseif (isset($_this->_map[$key])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return isset($_this->_objects[$key]) || isset($_this->_map[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -246,8 +245,7 @@ class ClassRegistry {
|
|||
* @return array Set of keys stored in registry
|
||||
*/
|
||||
public static function keys() {
|
||||
$_this = ClassRegistry::getInstance();
|
||||
return array_keys($_this->_objects);
|
||||
return array_keys(ClassRegistry::getInstance()->_objects);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -256,7 +254,7 @@ class ClassRegistry {
|
|||
* @param string $key Key of object to look for
|
||||
* @return mixed Object stored in registry or boolean false if the object does not exist.
|
||||
*/
|
||||
public static function &getObject($key) {
|
||||
public static function getObject($key) {
|
||||
$_this = ClassRegistry::getInstance();
|
||||
$key = Inflector::underscore($key);
|
||||
$return = false;
|
||||
|
|
@ -274,10 +272,10 @@ class ClassRegistry {
|
|||
/**
|
||||
* Sets the default constructor parameter for an object type
|
||||
*
|
||||
* @param string $type Type of object. If this parameter is omitted, defaults to "Model"
|
||||
* @param string $type Type of object. If this parameter is omitted, defaults to "Model"
|
||||
* @param array $param The parameter that will be passed to object constructors when objects
|
||||
* of $type are created
|
||||
* @return mixed Void if $param is being set. Otherwise, if only $type is passed, returns
|
||||
* @return mixed Void if $param is being set. Otherwise, if only $type is passed, returns
|
||||
* the previously-set value of $param, or null if not set.
|
||||
*/
|
||||
public static function config($type, $param = array()) {
|
||||
|
|
@ -286,7 +284,7 @@ class ClassRegistry {
|
|||
if (empty($param) && is_array($type)) {
|
||||
$param = $type;
|
||||
$type = 'Model';
|
||||
} elseif (is_null($param)) {
|
||||
} elseif ($param === null) {
|
||||
unset($_this->_config[$type]);
|
||||
} elseif (empty($param) && is_string($type)) {
|
||||
return isset($_this->_config[$type]) ? $_this->_config[$type] : null;
|
||||
|
|
@ -300,15 +298,15 @@ class ClassRegistry {
|
|||
/**
|
||||
* Checks to see if $alias is a duplicate $class Object
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $class
|
||||
* @return boolean
|
||||
* @param string $alias Alias to check.
|
||||
* @param string $class Class name.
|
||||
* @return bool
|
||||
*/
|
||||
protected function &_duplicate($alias, $class) {
|
||||
protected function &_duplicate($alias, $class) {
|
||||
$duplicate = false;
|
||||
if ($this->isKeySet($alias)) {
|
||||
$model = $this->getObject($alias);
|
||||
if (is_object($model) && (is_a($model, $class) || $model->alias === $class)) {
|
||||
if (is_object($model) && ($model instanceof $class || $model->alias === $class)) {
|
||||
$duplicate = $model;
|
||||
}
|
||||
unset($model);
|
||||
|
|
@ -338,8 +336,7 @@ class ClassRegistry {
|
|||
* @return array Keys of registry's map
|
||||
*/
|
||||
public static function mapKeys() {
|
||||
$_this = ClassRegistry::getInstance();
|
||||
return array_keys($_this->_map);
|
||||
return array_keys(ClassRegistry::getInstance()->_map);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,23 +4,22 @@
|
|||
*
|
||||
* Provides enhanced logging, stack traces, and rendering debug views
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @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.4560
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('CakeLog', 'Log');
|
||||
App::uses('String', 'Utility');
|
||||
App::uses('CakeText', 'Utility');
|
||||
|
||||
/**
|
||||
* Provide custom logging and error handling.
|
||||
|
|
@ -47,7 +46,7 @@ class Debugger {
|
|||
protected $_outputFormat = 'js';
|
||||
|
||||
/**
|
||||
* Templates used when generating trace or error strings. Can be global or indexed by the format
|
||||
* Templates used when generating trace or error strings. Can be global or indexed by the format
|
||||
* value used in $_outputFormat.
|
||||
*
|
||||
* @var string
|
||||
|
|
@ -80,8 +79,7 @@ class Debugger {
|
|||
'traceLine' => '{:reference} - {:path}, line {:line}',
|
||||
'trace' => "Trace:\n{:trace}\n",
|
||||
'context' => "Context:\n{:context}\n",
|
||||
),
|
||||
'log' => array(),
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
@ -93,7 +91,6 @@ class Debugger {
|
|||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*/
|
||||
public function __construct() {
|
||||
$docRef = ini_get('docref_root');
|
||||
|
|
@ -150,10 +147,10 @@ class Debugger {
|
|||
/**
|
||||
* Returns a reference to the Debugger singleton object instance.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $class Debugger class name.
|
||||
* @return object
|
||||
*/
|
||||
public static function &getInstance($class = null) {
|
||||
public static function getInstance($class = null) {
|
||||
static $instance = array();
|
||||
if (!empty($class)) {
|
||||
if (!$instance || strtolower($class) != strtolower(get_class($instance[0]))) {
|
||||
|
|
@ -169,40 +166,41 @@ class Debugger {
|
|||
/**
|
||||
* Recursively formats and outputs the contents of the supplied variable.
|
||||
*
|
||||
*
|
||||
* @param mixed $var the variable to dump
|
||||
* @param int $depth The depth to output to. Defaults to 3.
|
||||
* @return void
|
||||
* @see Debugger::exportVar()
|
||||
* @link http://book.cakephp.org/2.0/en/development/debugging.html#Debugger::dump
|
||||
*/
|
||||
public static function dump($var) {
|
||||
pr(self::exportVar($var));
|
||||
public static function dump($var, $depth = 3) {
|
||||
pr(static::exportVar($var, $depth));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entry in the log file. The log entry will contain a stack trace from where it was called.
|
||||
* Creates an entry in the log file. The log entry will contain a stack trace from where it was called.
|
||||
* as well as export the variable using exportVar. By default the log is written to the debug log.
|
||||
*
|
||||
* @param mixed $var Variable or content to log
|
||||
* @param integer $level type of log to use. Defaults to LOG_DEBUG
|
||||
* @param int $level type of log to use. Defaults to LOG_DEBUG
|
||||
* @param int $depth The depth to output to. Defaults to 3.
|
||||
* @return void
|
||||
* @link http://book.cakephp.org/2.0/en/development/debugging.html#Debugger::log
|
||||
*/
|
||||
public static function log($var, $level = LOG_DEBUG) {
|
||||
$source = self::trace(array('start' => 1)) . "\n";
|
||||
CakeLog::write($level, "\n" . $source . self::exportVar($var));
|
||||
public static function log($var, $level = LOG_DEBUG, $depth = 3) {
|
||||
$source = static::trace(array('start' => 1)) . "\n";
|
||||
CakeLog::write($level, "\n" . $source . static::exportVar($var, $depth));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides PHP's default error handling.
|
||||
*
|
||||
* @param integer $code Code of error
|
||||
* @param int $code Code of error
|
||||
* @param string $description Error description
|
||||
* @param string $file File on which error occurred
|
||||
* @param integer $line Line that triggered the error
|
||||
* @param int $line Line that triggered the error
|
||||
* @param array $context Context
|
||||
* @return boolean true if error was handled
|
||||
* @deprecated This function is superseded by Debugger::outputError()
|
||||
* @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) {
|
||||
$self = Debugger::getInstance();
|
||||
|
|
@ -218,7 +216,7 @@ class Debugger {
|
|||
if (!in_array($info, $self->errors)) {
|
||||
$self->errors[] = $info;
|
||||
} else {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ($code) {
|
||||
|
|
@ -229,26 +227,26 @@ class Debugger {
|
|||
case E_USER_ERROR:
|
||||
$error = 'Fatal Error';
|
||||
$level = LOG_ERR;
|
||||
break;
|
||||
break;
|
||||
case E_WARNING:
|
||||
case E_USER_WARNING:
|
||||
case E_COMPILE_WARNING:
|
||||
case E_RECOVERABLE_ERROR:
|
||||
$error = 'Warning';
|
||||
$level = LOG_WARNING;
|
||||
break;
|
||||
break;
|
||||
case E_NOTICE:
|
||||
case E_USER_NOTICE:
|
||||
$error = 'Notice';
|
||||
$level = LOG_NOTICE;
|
||||
break;
|
||||
break;
|
||||
case E_DEPRECATED:
|
||||
case E_USER_DEPRECATED:
|
||||
$error = 'Deprecated';
|
||||
$level = LOG_NOTICE;
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = compact(
|
||||
|
|
@ -256,7 +254,7 @@ class Debugger {
|
|||
);
|
||||
echo $self->outputError($data);
|
||||
|
||||
if ($error == 'Fatal Error') {
|
||||
if ($error === 'Fatal Error') {
|
||||
exit();
|
||||
}
|
||||
return true;
|
||||
|
|
@ -268,11 +266,11 @@ class Debugger {
|
|||
* ### Options
|
||||
*
|
||||
* - `depth` - The number of stack frames to return. Defaults to 999
|
||||
* - `format` - The format you want the return. Defaults to the currently selected format. If
|
||||
* - `format` - The format you want the return. Defaults to the currently selected format. If
|
||||
* format is 'array' or 'points' the return will be an array.
|
||||
* - `args` - Should arguments for functions be shown? If true, the arguments for each method call
|
||||
* will be displayed.
|
||||
* - `start` - The stack frame to start generating a trace from. Defaults to 0
|
||||
* - `start` - The stack frame to start generating a trace from. Defaults to 0
|
||||
*
|
||||
* @param array $options Format for outputting stack trace
|
||||
* @return mixed Formatted stack trace
|
||||
|
|
@ -295,9 +293,9 @@ class Debugger {
|
|||
$back = array();
|
||||
|
||||
$_trace = array(
|
||||
'line' => '??',
|
||||
'file' => '[internal]',
|
||||
'class' => null,
|
||||
'line' => '??',
|
||||
'file' => '[internal]',
|
||||
'class' => null,
|
||||
'function' => '[main]'
|
||||
);
|
||||
|
||||
|
|
@ -317,7 +315,7 @@ class Debugger {
|
|||
foreach ($next['args'] as $arg) {
|
||||
$args[] = Debugger::exportVar($arg);
|
||||
}
|
||||
$reference .= join(', ', $args);
|
||||
$reference .= implode(', ', $args);
|
||||
}
|
||||
$reference .= ')';
|
||||
}
|
||||
|
|
@ -325,9 +323,9 @@ class Debugger {
|
|||
if (in_array($signature, $options['exclude'])) {
|
||||
continue;
|
||||
}
|
||||
if ($options['format'] == 'points' && $trace['file'] != '[internal]') {
|
||||
if ($options['format'] === 'points' && $trace['file'] !== '[internal]') {
|
||||
$back[] = array('file' => $trace['file'], 'line' => $trace['line']);
|
||||
} elseif ($options['format'] == 'array') {
|
||||
} elseif ($options['format'] === 'array') {
|
||||
$back[] = $trace;
|
||||
} else {
|
||||
if (isset($self->_templates[$options['format']]['traceLine'])) {
|
||||
|
|
@ -335,14 +333,14 @@ class Debugger {
|
|||
} 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' => '}'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['format'] == 'array' || $options['format'] == 'points') {
|
||||
if ($options['format'] === 'array' || $options['format'] === 'points') {
|
||||
return $back;
|
||||
}
|
||||
return implode("\n", $back);
|
||||
|
|
@ -379,13 +377,13 @@ class Debugger {
|
|||
* `Debugger::excerpt('/path/to/file', 100, 4);`
|
||||
*
|
||||
* The above would return an array of 8 items. The 4th item would be the provided line,
|
||||
* and would be wrapped in `<span class="code-highlight"></span>`. All of the lines
|
||||
* and would be wrapped in `<span class="code-highlight"></span>`. All of the lines
|
||||
* are processed with highlight_string() as well, so they have basic PHP syntax highlighting
|
||||
* applied.
|
||||
*
|
||||
* @param string $file Absolute path to a PHP file
|
||||
* @param integer $line Line number to highlight
|
||||
* @param integer $context Number of lines of context to extract above and below $line
|
||||
* @param int $line Line number to highlight
|
||||
* @param int $context Number of lines of context to extract above and below $line
|
||||
* @return array Set of lines highlighted
|
||||
* @see http://php.net/highlight_string
|
||||
* @link http://book.cakephp.org/2.0/en/development/debugging.html#Debugger::excerpt
|
||||
|
|
@ -409,7 +407,7 @@ class Debugger {
|
|||
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[] = '<span class="code-highlight">' . $string . '</span>';
|
||||
} else {
|
||||
|
|
@ -420,7 +418,7 @@ class Debugger {
|
|||
}
|
||||
|
||||
/**
|
||||
* Wraps the highlight_string funciton in case the server API does not
|
||||
* Wraps the highlight_string function in case the server API does not
|
||||
* implement the function as it is the case of the HipHop interpreter
|
||||
*
|
||||
* @param string $str the string to convert
|
||||
|
|
@ -457,31 +455,29 @@ class Debugger {
|
|||
* - host
|
||||
* - database
|
||||
* - port
|
||||
* - prefix
|
||||
* - schema
|
||||
*
|
||||
* This is done to protect database credentials, which could be accidentally
|
||||
* shown in an error message if CakePHP is deployed in development mode.
|
||||
*
|
||||
* @param string $var Variable to convert
|
||||
* @param integer $depth The depth to output to. Defaults to 3.
|
||||
* @param int $depth The depth to output to. Defaults to 3.
|
||||
* @return string Variable as a formatted string
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected export function used to keep track of indentation and recursion.
|
||||
*
|
||||
* @param mixed $var The variable to dump.
|
||||
* @param integer $depth The remaining depth.
|
||||
* @param integer $indent The current indentation level.
|
||||
* @param int $depth The remaining depth.
|
||||
* @param int $indent The current indentation level.
|
||||
* @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':
|
||||
|
|
@ -489,23 +485,25 @@ class Debugger {
|
|||
case 'float':
|
||||
return '(float) ' . $var;
|
||||
case 'string':
|
||||
if (trim($var) == '') {
|
||||
if (trim($var) === '') {
|
||||
return "''";
|
||||
}
|
||||
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':
|
||||
return 'null';
|
||||
case 'unknown':
|
||||
return 'unknown';
|
||||
default:
|
||||
return self::_object($var, $depth - 1, $indent + 1);
|
||||
return static::_object($var, $depth - 1, $indent + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export an array type object. Filters out keys used in datasource configuration.
|
||||
* Export an array type object. Filters out keys used in datasource configuration.
|
||||
*
|
||||
* The following keys are replaced with ***'s
|
||||
*
|
||||
|
|
@ -514,31 +512,26 @@ class Debugger {
|
|||
* - host
|
||||
* - database
|
||||
* - port
|
||||
* - prefix
|
||||
* - schema
|
||||
*
|
||||
* @param array $var The array to export.
|
||||
* @param integer $depth The current depth, used for recursion tracking.
|
||||
* @param integer $indent The current indentation level.
|
||||
* @param int $depth The current depth, used for recursion tracking.
|
||||
* @param int $indent The current indentation level.
|
||||
* @return string Exported array.
|
||||
*/
|
||||
protected static function _array(array $var, $depth, $indent) {
|
||||
$secrets = array(
|
||||
'password' => '*****',
|
||||
'login' => '*****',
|
||||
'login' => '*****',
|
||||
'host' => '*****',
|
||||
'database' => '*****',
|
||||
'port' => '*****',
|
||||
'prefix' => '*****',
|
||||
'schema' => '*****'
|
||||
'port' => '*****'
|
||||
);
|
||||
$replace = array_intersect_key($secrets, $var);
|
||||
$var = $replace + $var;
|
||||
|
||||
$out = "array(";
|
||||
$n = $break = $end = null;
|
||||
$break = $end = null;
|
||||
if (!empty($var)) {
|
||||
$n = "\n";
|
||||
$break = "\n" . str_repeat("\t", $indent);
|
||||
$end = "\n" . str_repeat("\t", $indent - 1);
|
||||
}
|
||||
|
|
@ -549,10 +542,10 @@ class Debugger {
|
|||
// Sniff for globals as !== explodes in < 5.4
|
||||
if ($key === 'GLOBALS' && is_array($val) && isset($val['GLOBALS'])) {
|
||||
$val = '[recursion]';
|
||||
} else if ($val !== $var) {
|
||||
$val = self::_export($val, $depth, $indent);
|
||||
} elseif ($val !== $var) {
|
||||
$val = static::_export($val, $depth, $indent);
|
||||
}
|
||||
$vars[] = $break . self::exportVar($key) .
|
||||
$vars[] = $break . static::exportVar($key) .
|
||||
' => ' .
|
||||
$val;
|
||||
}
|
||||
|
|
@ -566,8 +559,8 @@ class Debugger {
|
|||
* Handles object to string conversion.
|
||||
*
|
||||
* @param string $var Object to convert
|
||||
* @param integer $depth The current depth, used for tracking recursion.
|
||||
* @param integer $indent The current indentation level.
|
||||
* @param int $depth The current depth, used for tracking recursion.
|
||||
* @param int $indent The current indentation level.
|
||||
* @return string
|
||||
* @see Debugger::exportVar()
|
||||
*/
|
||||
|
|
@ -583,9 +576,30 @@ class Debugger {
|
|||
$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;
|
||||
}
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
|
||||
$ref = new ReflectionObject($var);
|
||||
|
||||
$filters = array(
|
||||
ReflectionProperty::IS_PROTECTED => 'protected',
|
||||
ReflectionProperty::IS_PRIVATE => 'private',
|
||||
);
|
||||
foreach ($filters as $filter => $visibility) {
|
||||
$reflectionProperties = $ref->getProperties($filter);
|
||||
foreach ($reflectionProperties as $reflectionProperty) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$property = $reflectionProperty->getValue($var);
|
||||
|
||||
$value = static::_export($property, $depth - 1, $indent);
|
||||
$key = $reflectionProperty->name;
|
||||
$props[] = sprintf('[%s] %s => %s', $visibility, $key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$out .= $break . implode($break, $props) . $end;
|
||||
}
|
||||
$out .= '}';
|
||||
|
|
@ -597,7 +611,7 @@ class Debugger {
|
|||
*
|
||||
* @param string $format The format you want errors to be output as.
|
||||
* Leave null to get the current format.
|
||||
* @return mixed Returns null when setting. Returns the current format when getting.
|
||||
* @return mixed Returns null when setting. Returns the current format when getting.
|
||||
* @throws CakeException when choosing a format that doesn't exist.
|
||||
*/
|
||||
public static function outputAs($format = null) {
|
||||
|
|
@ -616,8 +630,8 @@ class Debugger {
|
|||
*
|
||||
* `Debugger::addFormat('custom', $data);`
|
||||
*
|
||||
* Where $data is an array of strings that use String::insert() variable
|
||||
* replacement. The template vars should be in a `{:id}` style.
|
||||
* 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:
|
||||
*
|
||||
* - 'error' - Used for the container for the error message. Gets the following template
|
||||
|
|
@ -630,7 +644,7 @@ class Debugger {
|
|||
* Gets the following templates: `id`, `context`
|
||||
* - 'links' - An array of HTML links that are used for creating links to other resources.
|
||||
* Typically this is used to create javascript links to open other sections.
|
||||
* Link keys, are: `code`, `context`, `help`. See the js output format for an
|
||||
* Link keys, are: `code`, `context`, `help`. See the js output format for an
|
||||
* example.
|
||||
* - 'traceLine' - Used for creating lines in the stacktrace. Gets the following
|
||||
* template variables: `reference`, `path`, `line`
|
||||
|
|
@ -640,9 +654,9 @@ class Debugger {
|
|||
*
|
||||
* `Debugger::addFormat('custom', array('callback' => array($foo, 'outputError'));`
|
||||
*
|
||||
* The callback can expect two parameters. The first is an array of all
|
||||
* The callback can expect two parameters. The first is an array of all
|
||||
* the error data. The second contains the formatted strings generated using
|
||||
* the other template strings. Keys like `info`, `links`, `code`, `context` and `trace`
|
||||
* the other template strings. Keys like `info`, `links`, `code`, `context` and `trace`
|
||||
* will be present depending on the other templates in the format type.
|
||||
*
|
||||
* @param string $format Format to use, including 'js' for JavaScript-enhanced HTML, 'html' for
|
||||
|
|
@ -675,14 +689,14 @@ class Debugger {
|
|||
* straight HTML output, or 'txt' for unformatted text.
|
||||
* @param array $strings Template strings to be used for the output format.
|
||||
* @return string
|
||||
* @deprecated Use Debugger::outputAs() and Debugger::addFormat(). Will be removed
|
||||
* @deprecated 3.0.0 Use Debugger::outputAs() and Debugger::addFormat(). Will be removed
|
||||
* in 3.0
|
||||
*/
|
||||
public function output($format = null, $strings = array()) {
|
||||
public static function output($format = null, $strings = array()) {
|
||||
$self = Debugger::getInstance();
|
||||
$data = null;
|
||||
|
||||
if (is_null($format)) {
|
||||
if ($format === null) {
|
||||
return Debugger::outputAs();
|
||||
}
|
||||
|
||||
|
|
@ -702,7 +716,7 @@ class Debugger {
|
|||
/**
|
||||
* Takes a processed array of data from an error and displays it in the chosen format.
|
||||
*
|
||||
* @param string $data
|
||||
* @param string $data Data to output.
|
||||
* @return void
|
||||
*/
|
||||
public function outputError($data) {
|
||||
|
|
@ -754,12 +768,13 @@ class Debugger {
|
|||
|
||||
if (isset($tpl['links'])) {
|
||||
foreach ($tpl['links'] as $key => $val) {
|
||||
$links[$key] = String::insert($val, $data, $insertOpts);
|
||||
$links[$key] = CakeText::insert($val, $data, $insertOpts);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($tpl['escapeContext'])) {
|
||||
$context = h($context);
|
||||
$data['description'] = h($data['description']);
|
||||
}
|
||||
|
||||
$infoData = compact('code', 'context', 'trace');
|
||||
|
|
@ -768,20 +783,20 @@ class Debugger {
|
|||
continue;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$value = join("\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 = join(' ', $links);
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the given variable. Will return the classname
|
||||
* Get the type of the given variable. Will return the class name
|
||||
* for objects.
|
||||
*
|
||||
* @param mixed $var The variable to get the type of
|
||||
|
|
@ -791,7 +806,7 @@ class Debugger {
|
|||
if (is_object($var)) {
|
||||
return get_class($var);
|
||||
}
|
||||
if (is_null($var)) {
|
||||
if ($var === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (is_string($var)) {
|
||||
|
|
@ -821,12 +836,12 @@ class Debugger {
|
|||
* @return void
|
||||
*/
|
||||
public static function checkSecurityKeys() {
|
||||
if (Configure::read('Security.salt') == 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi') {
|
||||
trigger_error(__d('cake_dev', 'Please change the value of \'Security.salt\' in app/Config/core.php to a salt value specific to your application'), E_USER_NOTICE);
|
||||
if (Configure::read('Security.salt') === 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi') {
|
||||
trigger_error(__d('cake_dev', 'Please change the value of %s in %s to a salt value specific to your application.', '\'Security.salt\'', 'APP/Config/core.php'), E_USER_NOTICE);
|
||||
}
|
||||
|
||||
if (Configure::read('Security.cipherSeed') === '76859309657453542496749683645') {
|
||||
trigger_error(__d('cake_dev', 'Please change the value of \'Security.cipherSeed\' in app/Config/core.php to a numeric (digits only) seed value specific to your application'), E_USER_NOTICE);
|
||||
trigger_error(__d('cake_dev', 'Please change the value of %s in %s to a numeric (digits only) seed value specific to your application.', '\'Security.cipherSeed\'', 'APP/Config/core.php'), E_USER_NOTICE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,19 +2,18 @@
|
|||
/**
|
||||
* Convenience class for reading, writing and appending to files.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Utility
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('Folder', 'Utility');
|
||||
|
|
@ -27,7 +26,7 @@ App::uses('Folder', 'Utility');
|
|||
class File {
|
||||
|
||||
/**
|
||||
* Folder object of the File
|
||||
* Folder object of the file
|
||||
*
|
||||
* @var Folder
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$Folder
|
||||
|
|
@ -35,7 +34,7 @@ class File {
|
|||
public $Folder = null;
|
||||
|
||||
/**
|
||||
* Filename
|
||||
* File name
|
||||
*
|
||||
* @var string
|
||||
* http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$name
|
||||
|
|
@ -61,7 +60,7 @@ class File {
|
|||
/**
|
||||
* Enable locking for file reading and writing
|
||||
*
|
||||
* @var boolean
|
||||
* @var bool
|
||||
* http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$lock
|
||||
*/
|
||||
public $lock = null;
|
||||
|
|
@ -71,7 +70,7 @@ class File {
|
|||
*
|
||||
* Current file's absolute path
|
||||
*
|
||||
* @var mixed null
|
||||
* @var mixed
|
||||
* http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$path
|
||||
*/
|
||||
public $path = null;
|
||||
|
|
@ -80,8 +79,8 @@ class File {
|
|||
* Constructor
|
||||
*
|
||||
* @param string $path Path to file
|
||||
* @param boolean $create Create file if it does not exist (if true)
|
||||
* @param integer $mode Mode to apply to the folder holding the file
|
||||
* @param bool $create Create file if it does not exist (if true)
|
||||
* @param int $mode Mode to apply to the folder holding the file
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File
|
||||
*/
|
||||
public function __construct($path, $create = false, $mode = 0755) {
|
||||
|
|
@ -95,16 +94,15 @@ class File {
|
|||
|
||||
/**
|
||||
* Closes the current file if it is opened
|
||||
*
|
||||
*/
|
||||
public function __destruct() {
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the File.
|
||||
* Creates the file.
|
||||
*
|
||||
* @return boolean Success
|
||||
* @return bool Success
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::create
|
||||
*/
|
||||
public function create() {
|
||||
|
|
@ -121,15 +119,14 @@ class File {
|
|||
* Opens the current file with a given $mode
|
||||
*
|
||||
* @param string $mode A valid 'fopen' mode string (r|w|a ...)
|
||||
* @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
|
||||
* @return boolean True on success, false on failure
|
||||
* @param bool $force If true then the file will be re-opened even if its already opened, otherwise it won't
|
||||
* @return bool True on success, false on failure
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::open
|
||||
*/
|
||||
public function open($mode = 'r', $force = false) {
|
||||
if (!$force && is_resource($this->handle)) {
|
||||
return true;
|
||||
}
|
||||
clearstatcache();
|
||||
if ($this->exists() === false) {
|
||||
if ($this->create() === false) {
|
||||
return false;
|
||||
|
|
@ -144,11 +141,11 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the contents of this File as a string.
|
||||
* Return the contents of this file as a string.
|
||||
*
|
||||
* @param string $bytes where to start
|
||||
* @param string $mode A `fread` compatible mode.
|
||||
* @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
|
||||
* @param bool $force If true then the file will be re-opened even if its already opened, otherwise it won't
|
||||
* @return mixed string on success, false on failure
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::read
|
||||
*/
|
||||
|
|
@ -183,8 +180,8 @@ class File {
|
|||
/**
|
||||
* Sets or gets the offset for the currently opened file.
|
||||
*
|
||||
* @param integer|boolean $offset The $offset in bytes to seek. If set to false then the current offset is returned.
|
||||
* @param integer $seek PHP Constant SEEK_SET | SEEK_CUR | SEEK_END determining what the $offset is relative to
|
||||
* @param int|bool $offset The $offset in bytes to seek. If set to false then the current offset is returned.
|
||||
* @param int $seek PHP Constant SEEK_SET | SEEK_CUR | SEEK_END determining what the $offset is relative to
|
||||
* @return mixed True on success, false on failure (set mode), false on failure or integer offset on success (get mode)
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::offset
|
||||
*/
|
||||
|
|
@ -200,30 +197,30 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Prepares a ascii string for writing. Converts line endings to the
|
||||
* correct terminator for the current platform. If windows "\r\n" will be used
|
||||
* Prepares an ASCII string for writing. Converts line endings to the
|
||||
* correct terminator for the current platform. If Windows, "\r\n" will be used,
|
||||
* all other platforms will use "\n"
|
||||
*
|
||||
* @param string $data Data to prepare for writing.
|
||||
* @param boolean $forceWindows
|
||||
* @param bool $forceWindows If true forces usage Windows newline string.
|
||||
* @return string The with converted line endings.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::prepare
|
||||
*/
|
||||
public static function prepare($data, $forceWindows = false) {
|
||||
$lineBreak = "\n";
|
||||
if (DIRECTORY_SEPARATOR == '\\' || $forceWindows === true) {
|
||||
if (DIRECTORY_SEPARATOR === '\\' || $forceWindows === true) {
|
||||
$lineBreak = "\r\n";
|
||||
}
|
||||
return strtr($data, array("\r\n" => $lineBreak, "\n" => $lineBreak, "\r" => $lineBreak));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write given data to this File.
|
||||
* Write given data to this file.
|
||||
*
|
||||
* @param string $data Data to write to this File.
|
||||
* @param string $mode Mode of writing. {@link http://php.net/fwrite See fwrite()}.
|
||||
* @param string $force force the file to open
|
||||
* @return boolean Success
|
||||
* @param bool $force Force the file to open
|
||||
* @return bool Success
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::write
|
||||
*/
|
||||
public function write($data, $mode = 'w', $force = false) {
|
||||
|
|
@ -246,11 +243,11 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Append given data string to this File.
|
||||
* Append given data string to this file.
|
||||
*
|
||||
* @param string $data Data to write
|
||||
* @param string $force force the file to open
|
||||
* @return boolean Success
|
||||
* @param string $force Force the file to open
|
||||
* @return bool Success
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::append
|
||||
*/
|
||||
public function append($data, $force = false) {
|
||||
|
|
@ -260,7 +257,7 @@ class File {
|
|||
/**
|
||||
* Closes the current file if it is opened.
|
||||
*
|
||||
* @return boolean True if closing was successful or file was already closed, otherwise false
|
||||
* @return bool True if closing was successful or file was already closed, otherwise false
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::close
|
||||
*/
|
||||
public function close() {
|
||||
|
|
@ -271,13 +268,12 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Deletes the File.
|
||||
* Deletes the file.
|
||||
*
|
||||
* @return boolean Success
|
||||
* @return bool Success
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::delete
|
||||
*/
|
||||
public function delete() {
|
||||
clearstatcache();
|
||||
if (is_resource($this->handle)) {
|
||||
fclose($this->handle);
|
||||
$this->handle = null;
|
||||
|
|
@ -289,7 +285,7 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the File info as an array with the following keys:
|
||||
* Returns the file info as an array with the following keys:
|
||||
*
|
||||
* - dirname
|
||||
* - basename
|
||||
|
|
@ -302,7 +298,7 @@ class File {
|
|||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::info
|
||||
*/
|
||||
public function info() {
|
||||
if ($this->info == null) {
|
||||
if (!$this->info) {
|
||||
$this->info = pathinfo($this->path);
|
||||
}
|
||||
if (!isset($this->info['filename'])) {
|
||||
|
|
@ -318,13 +314,13 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the File extension.
|
||||
* Returns the file extension.
|
||||
*
|
||||
* @return string The File extension
|
||||
* @return string The file extension
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::ext
|
||||
*/
|
||||
public function ext() {
|
||||
if ($this->info == null) {
|
||||
if (!$this->info) {
|
||||
$this->info();
|
||||
}
|
||||
if (isset($this->info['extension'])) {
|
||||
|
|
@ -334,13 +330,13 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the File name without extension.
|
||||
* Returns the file name without extension.
|
||||
*
|
||||
* @return string The File name without extension.
|
||||
* @return string The file name without extension.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::name
|
||||
*/
|
||||
public function name() {
|
||||
if ($this->info == null) {
|
||||
if (!$this->info) {
|
||||
$this->info();
|
||||
}
|
||||
if (isset($this->info['extension'])) {
|
||||
|
|
@ -352,11 +348,11 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* makes filename safe for saving
|
||||
* Makes file name safe for saving
|
||||
*
|
||||
* @param string $name The name of the file to make safe if different from $this->name
|
||||
* @param string $ext The name of the extension to make safe if different from $this->ext
|
||||
* @return string $ext the extension of the file
|
||||
* @return string ext The extension of the file
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::safe
|
||||
*/
|
||||
public function safe($name = null, $ext = null) {
|
||||
|
|
@ -372,8 +368,8 @@ class File {
|
|||
/**
|
||||
* Get md5 Checksum of file with previous check of Filesize
|
||||
*
|
||||
* @param integer|boolean $maxsize in MB or true to force
|
||||
* @return string md5 Checksum {@link http://php.net/md5_file See md5_file()}
|
||||
* @param int|bool $maxsize in MB or true to force
|
||||
* @return string|false md5 Checksum {@link http://php.net/md5_file See md5_file()}, or false in case of an error
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::md5
|
||||
*/
|
||||
public function md5($maxsize = 5) {
|
||||
|
|
@ -390,32 +386,36 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the full path of the File.
|
||||
* Returns the full path of the file.
|
||||
*
|
||||
* @return string Full path to file
|
||||
* @return string Full path to the file
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::pwd
|
||||
*/
|
||||
public function pwd() {
|
||||
if (is_null($this->path)) {
|
||||
$this->path = $this->Folder->slashTerm($this->Folder->pwd()) . $this->name;
|
||||
if ($this->path === null) {
|
||||
$dir = $this->Folder->pwd();
|
||||
if (is_dir($dir)) {
|
||||
$this->path = $this->Folder->slashTerm($dir) . $this->name;
|
||||
}
|
||||
}
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the File exists.
|
||||
* Returns true if the file exists.
|
||||
*
|
||||
* @return boolean true if it exists, false otherwise
|
||||
* @return bool True if it exists, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::exists
|
||||
*/
|
||||
public function exists() {
|
||||
$this->clearStatCache();
|
||||
return (file_exists($this->path) && is_file($this->path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "chmod" (permissions) of the File.
|
||||
* Returns the "chmod" (permissions) of the file.
|
||||
*
|
||||
* @return string Permissions for the file
|
||||
* @return string|false Permissions for the file, or false in case of an error
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::perms
|
||||
*/
|
||||
public function perms() {
|
||||
|
|
@ -426,9 +426,9 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the Filesize
|
||||
* Returns the file size
|
||||
*
|
||||
* @return integer size of the file in bytes, or false in case of an error
|
||||
* @return int|false Size of the file in bytes, or false in case of an error
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::size
|
||||
*/
|
||||
public function size() {
|
||||
|
|
@ -439,9 +439,9 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if the File is writable.
|
||||
* Returns true if the file is writable.
|
||||
*
|
||||
* @return boolean true if its writable, false otherwise
|
||||
* @return bool True if it's writable, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::writable
|
||||
*/
|
||||
public function writable() {
|
||||
|
|
@ -451,7 +451,7 @@ class File {
|
|||
/**
|
||||
* Returns true if the File is executable.
|
||||
*
|
||||
* @return boolean true if its executable, false otherwise
|
||||
* @return bool True if it's executable, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::executable
|
||||
*/
|
||||
public function executable() {
|
||||
|
|
@ -459,9 +459,9 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if the File is readable.
|
||||
* Returns true if the file is readable.
|
||||
*
|
||||
* @return boolean true if file is readable, false otherwise
|
||||
* @return bool True if file is readable, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::readable
|
||||
*/
|
||||
public function readable() {
|
||||
|
|
@ -469,9 +469,9 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the File's owner.
|
||||
* Returns the file's owner.
|
||||
*
|
||||
* @return integer the Fileowner
|
||||
* @return int|false The file owner, or false in case of an error
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::owner
|
||||
*/
|
||||
public function owner() {
|
||||
|
|
@ -482,9 +482,9 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the File's group.
|
||||
* Returns the file's group.
|
||||
*
|
||||
* @return integer the Filegroup
|
||||
* @return int|false The file group, or false in case of an error
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::group
|
||||
*/
|
||||
public function group() {
|
||||
|
|
@ -497,7 +497,7 @@ class File {
|
|||
/**
|
||||
* Returns last access time.
|
||||
*
|
||||
* @return integer timestamp Timestamp of last access time
|
||||
* @return int|false Timestamp of last access time, or false in case of an error
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::lastAccess
|
||||
*/
|
||||
public function lastAccess() {
|
||||
|
|
@ -510,7 +510,7 @@ class File {
|
|||
/**
|
||||
* Returns last modified time.
|
||||
*
|
||||
* @return integer timestamp Timestamp of last modification
|
||||
* @return int|false Timestamp of last modification, or false in case of an error
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::lastChange
|
||||
*/
|
||||
public function lastChange() {
|
||||
|
|
@ -526,16 +526,16 @@ class File {
|
|||
* @return Folder Current folder
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::Folder
|
||||
*/
|
||||
public function &folder() {
|
||||
public function folder() {
|
||||
return $this->Folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the File to $dest
|
||||
*
|
||||
* @param string $dest destination for the copy
|
||||
* @param boolean $overwrite Overwrite $dest if exists
|
||||
* @return boolean Success
|
||||
* @param string $dest Destination for the copy
|
||||
* @param bool $overwrite Overwrite $dest if exists
|
||||
* @return bool Success
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::copy
|
||||
*/
|
||||
public function copy($dest, $overwrite = true) {
|
||||
|
|
@ -546,7 +546,7 @@ class File {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the mime type of the file. Uses the finfo extension if
|
||||
* Get the mime type of the file. Uses the finfo extension if
|
||||
* its available, otherwise falls back to mime_content_type
|
||||
*
|
||||
* @return false|string The mimetype of the file, or false if reading fails.
|
||||
|
|
@ -557,12 +557,62 @@ class File {
|
|||
}
|
||||
if (function_exists('finfo_open')) {
|
||||
$finfo = finfo_open(FILEINFO_MIME);
|
||||
list($type, $charset) = explode(';', finfo_file($finfo, $this->pwd()));
|
||||
$finfo = finfo_file($finfo, $this->pwd());
|
||||
if (!$finfo) {
|
||||
return false;
|
||||
}
|
||||
list($type) = explode(';', $finfo);
|
||||
return $type;
|
||||
} elseif (function_exists('mime_content_type')) {
|
||||
}
|
||||
if (function_exists('mime_content_type')) {
|
||||
return mime_content_type($this->pwd());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear PHP's internal stat cache
|
||||
*
|
||||
* For 5.3 onwards it's possible to clear cache for just a single file. Passing true
|
||||
* will clear all the stat cache.
|
||||
*
|
||||
* @param bool $all Clear all cache or not
|
||||
* @return void
|
||||
*/
|
||||
public function clearStatCache($all = false) {
|
||||
if ($all === false && version_compare(PHP_VERSION, '5.3.0') >= 0) {
|
||||
return clearstatcache(true, $this->path);
|
||||
}
|
||||
|
||||
return clearstatcache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a given text and replaces the text if found.
|
||||
*
|
||||
* @param string|array $search Text(s) to search for.
|
||||
* @param string|array $replace Text(s) to replace with.
|
||||
* @return bool Success
|
||||
*/
|
||||
public function replaceText($search, $replace) {
|
||||
if (!$this->open('r+')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->lock !== null) {
|
||||
if (flock($this->handle, LOCK_EX) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$replaced = $this->write(str_replace($search, $replace, $this->read()), 'w', true);
|
||||
|
||||
if ($this->lock !== null) {
|
||||
flock($this->handle, LOCK_UN);
|
||||
}
|
||||
$this->close();
|
||||
|
||||
return $replaced;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Utility
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -21,6 +22,40 @@
|
|||
*/
|
||||
class Folder {
|
||||
|
||||
/**
|
||||
* Default scheme for Folder::copy
|
||||
* Recursively merges subfolders with the same name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const MERGE = 'merge';
|
||||
|
||||
/**
|
||||
* Overwrite scheme for Folder::copy
|
||||
* subfolders with the same name will be replaced
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OVERWRITE = 'overwrite';
|
||||
|
||||
/**
|
||||
* Skip scheme for Folder::copy
|
||||
* if a subfolder with the same name exists it will be skipped
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SKIP = 'skip';
|
||||
|
||||
/**
|
||||
* Sort mode by name
|
||||
*/
|
||||
const SORT_NAME = 'name';
|
||||
|
||||
/**
|
||||
* Sort mode by time
|
||||
*/
|
||||
const SORT_TIME = 'time';
|
||||
|
||||
/**
|
||||
* Path to Folder.
|
||||
*
|
||||
|
|
@ -33,19 +68,27 @@ class Folder {
|
|||
* Sortedness. Whether or not list results
|
||||
* should be sorted by name.
|
||||
*
|
||||
* @var boolean
|
||||
* @var bool
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::$sort
|
||||
*/
|
||||
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 integer
|
||||
* @var int
|
||||
* http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::$mode
|
||||
*/
|
||||
public $mode = 0755;
|
||||
|
||||
/**
|
||||
* Functions array to be called depending on the sort type chosen.
|
||||
*/
|
||||
protected $_fsorts = array(
|
||||
self::SORT_NAME => 'getPathname',
|
||||
self::SORT_TIME => 'getCTime'
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds messages from last method.
|
||||
*
|
||||
|
|
@ -78,8 +121,8 @@ class Folder {
|
|||
* Constructor.
|
||||
*
|
||||
* @param string $path Path to folder
|
||||
* @param boolean $create Create folder if not found
|
||||
* @param string|boolean $mode Mode (CHMOD) to apply to created folder, false to ignore
|
||||
* @param bool $create Create folder if not found
|
||||
* @param string|bool $mode Mode (CHMOD) to apply to created folder, false to ignore
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder
|
||||
*/
|
||||
public function __construct($path = false, $create = false, $mode = false) {
|
||||
|
|
@ -130,14 +173,14 @@ class Folder {
|
|||
* Returns an array of the contents of the current directory.
|
||||
* The returned array holds two arrays: One of directories and one of files.
|
||||
*
|
||||
* @param boolean $sort Whether you want the results sorted, set this and the sort property
|
||||
* @param string|bool $sort Whether you want the results sorted, set this and the sort property
|
||||
* to false to get unsorted results.
|
||||
* @param array|boolean $exceptions Either an array or boolean true will not grab dot files
|
||||
* @param boolean $fullPath True returns the full path
|
||||
* @param array|bool $exceptions Either an array or boolean true will not grab dot files
|
||||
* @param bool $fullPath True returns the full path
|
||||
* @return mixed Contents of current directory as an array, an empty array on failure
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::read
|
||||
*/
|
||||
public function read($sort = true, $exceptions = false, $fullPath = false) {
|
||||
public function read($sort = self::SORT_NAME, $exceptions = false, $fullPath = false) {
|
||||
$dirs = $files = array();
|
||||
|
||||
if (!$this->pwd()) {
|
||||
|
|
@ -153,6 +196,11 @@ class Folder {
|
|||
} catch (Exception $e) {
|
||||
return array($dirs, $files);
|
||||
}
|
||||
if (!is_bool($sort) && isset($this->_fsorts[$sort])) {
|
||||
$methodName = $this->_fsorts[$sort];
|
||||
} else {
|
||||
$methodName = $this->_fsorts[self::SORT_NAME];
|
||||
}
|
||||
|
||||
foreach ($iterator as $item) {
|
||||
if ($item->isDot()) {
|
||||
|
|
@ -166,14 +214,22 @@ class Folder {
|
|||
$name = $item->getPathName();
|
||||
}
|
||||
if ($item->isDir()) {
|
||||
$dirs[] = $name;
|
||||
$dirs[$item->{$methodName}()][] = $name;
|
||||
} else {
|
||||
$files[] = $name;
|
||||
$files[$item->{$methodName}()][] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
if ($sort || $this->sort) {
|
||||
sort($dirs);
|
||||
sort($files);
|
||||
ksort($dirs);
|
||||
ksort($files);
|
||||
}
|
||||
|
||||
if ($dirs) {
|
||||
$dirs = call_user_func_array('array_merge', $dirs);
|
||||
}
|
||||
if ($files) {
|
||||
$files = call_user_func_array('array_merge', $files);
|
||||
}
|
||||
return array($dirs, $files);
|
||||
}
|
||||
|
|
@ -182,12 +238,12 @@ class Folder {
|
|||
* Returns an array of all matching files in current directory.
|
||||
*
|
||||
* @param string $regexpPattern Preg_match pattern (Defaults to: .*)
|
||||
* @param boolean $sort Whether results should be sorted.
|
||||
* @param bool $sort Whether results should be sorted.
|
||||
* @return array Files that match given pattern
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::find
|
||||
*/
|
||||
public function find($regexpPattern = '.*', $sort = false) {
|
||||
list($dirs, $files) = $this->read($sort);
|
||||
list(, $files) = $this->read($sort);
|
||||
return array_values(preg_grep('/^' . $regexpPattern . '$/i', $files));
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +251,7 @@ class Folder {
|
|||
* Returns an array of all matching files in and below current directory.
|
||||
*
|
||||
* @param string $pattern Preg_match pattern (Defaults to: .*)
|
||||
* @param boolean $sort Whether results should be sorted.
|
||||
* @param bool $sort Whether results should be sorted.
|
||||
* @return array Files matching $pattern
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::findRecursive
|
||||
*/
|
||||
|
|
@ -213,7 +269,7 @@ class Folder {
|
|||
* Private helper function for findRecursive.
|
||||
*
|
||||
* @param string $pattern Pattern to match against
|
||||
* @param boolean $sort Whether results should be sorted.
|
||||
* @param bool $sort Whether results should be sorted.
|
||||
* @return array Files matching pattern
|
||||
*/
|
||||
protected function _findRecursive($pattern, $sort = false) {
|
||||
|
|
@ -238,22 +294,44 @@ class Folder {
|
|||
* Returns true if given $path is a Windows path.
|
||||
*
|
||||
* @param string $path Path to check
|
||||
* @return boolean 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) {
|
||||
return (preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\');
|
||||
return (preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) === '\\\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given $path is an absolute path.
|
||||
*
|
||||
* @param string $path Path to check
|
||||
* @return boolean true if path is absolute.
|
||||
* @return bool true if path is absolute.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::isAbsolute
|
||||
*/
|
||||
public static function isAbsolute($path) {
|
||||
return !empty($path) && ($path[0] === '/' || preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) == '\\\\');
|
||||
if (empty($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $path[0] === '/' ||
|
||||
preg_match('/^[A-Z]:\\\\/i', $path) ||
|
||||
substr($path, 0, 2) === '\\\\' ||
|
||||
static::isRegisteredStreamWrapper($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given $path is a registered stream wrapper.
|
||||
*
|
||||
* @param string $path Path to check
|
||||
* @return boo true If path is registered stream wrapper.
|
||||
*/
|
||||
public static function isRegisteredStreamWrapper($path) {
|
||||
if (preg_match('/^[A-Z]+(?=:\/\/)/i', $path, $matches) &&
|
||||
in_array($matches[0], stream_get_wrappers())
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -296,19 +374,21 @@ class Folder {
|
|||
* Returns $path with $element added, with correct slash in-between.
|
||||
*
|
||||
* @param string $path Path
|
||||
* @param string $element Element to and at end of path
|
||||
* @param string|array $element Element to add at end of path
|
||||
* @return string Combined path
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::addPathElement
|
||||
*/
|
||||
public static function addPathElement($path, $element) {
|
||||
return rtrim($path, DS) . DS . $element;
|
||||
$element = (array)$element;
|
||||
array_unshift($element, rtrim($path, DS));
|
||||
return implode(DS, $element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the File is in a given CakePath.
|
||||
* Returns true if the Folder is in the given Cake path.
|
||||
*
|
||||
* @param string $path The path to check.
|
||||
* @return boolean
|
||||
* @return bool
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::inCakePath
|
||||
*/
|
||||
public function inCakePath($path = '') {
|
||||
|
|
@ -319,21 +399,26 @@ class Folder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if the File is in given path.
|
||||
* Returns true if the Folder is in the given path.
|
||||
*
|
||||
* @param string $path The path to check that the current pwd() resides with in.
|
||||
* @param boolean $reverse Reverse the search, check that pwd() resides within $path.
|
||||
* @return boolean
|
||||
* @param string $path The absolute path to check that the current `pwd()` resides within.
|
||||
* @param bool $reverse Reverse the search, check if the given `$path` resides within the current `pwd()`.
|
||||
* @return bool
|
||||
* @throws \InvalidArgumentException When the given `$path` argument is not an absolute path.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::inPath
|
||||
*/
|
||||
public function inPath($path = '', $reverse = false) {
|
||||
if (!Folder::isAbsolute($path)) {
|
||||
throw new InvalidArgumentException(__d('cake_dev', 'The $path argument is expected to be an absolute path.'));
|
||||
}
|
||||
|
||||
$dir = Folder::slashTerm($path);
|
||||
$current = Folder::slashTerm($this->pwd());
|
||||
|
||||
if (!$reverse) {
|
||||
$return = preg_match('/^(.*)' . preg_quote($dir, '/') . '(.*)/', $current);
|
||||
$return = preg_match('/^' . preg_quote($dir, '/') . '(.*)/', $current);
|
||||
} else {
|
||||
$return = preg_match('/^(.*)' . preg_quote($current, '/') . '(.*)/', $dir);
|
||||
$return = preg_match('/^' . preg_quote($current, '/') . '(.*)/', $dir);
|
||||
}
|
||||
return (bool)$return;
|
||||
}
|
||||
|
|
@ -341,11 +426,11 @@ class Folder {
|
|||
/**
|
||||
* Change the mode on a directory structure recursively. This includes changing the mode on files as well.
|
||||
*
|
||||
* @param string $path The path to chmod
|
||||
* @param integer $mode octal value 0755
|
||||
* @param boolean $recursive chmod recursively, set to false to only change the current directory.
|
||||
* @param array $exceptions array of files, directories to skip
|
||||
* @return boolean Returns TRUE on success, FALSE on failure
|
||||
* @param string $path The path to chmod.
|
||||
* @param int $mode Octal value, e.g. 0755.
|
||||
* @param bool $recursive Chmod recursively, set to false to only change the current directory.
|
||||
* @param array $exceptions Array of files, directories to skip.
|
||||
* @return bool Success.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::chmod
|
||||
*/
|
||||
public function chmod($path, $mode = false, $recursive = true, $exceptions = array()) {
|
||||
|
|
@ -398,14 +483,14 @@ class Folder {
|
|||
* Returns an array of nested directories and files in each directory
|
||||
*
|
||||
* @param string $path the directory path to build the tree from
|
||||
* @param array|boolean $exceptions Either an array of files/folder to exclude
|
||||
* @param array|bool $exceptions Either an array of files/folder to exclude
|
||||
* or boolean true to not grab dot files/folders
|
||||
* @param string $type either 'file' or 'dir'. null returns both files and directories
|
||||
* @return mixed array of nested directories and files in each directory
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::tree
|
||||
*/
|
||||
public function tree($path = null, $exceptions = false, $type = null) {
|
||||
if ($path == null) {
|
||||
if (!$path) {
|
||||
$path = $this->path;
|
||||
}
|
||||
$files = array();
|
||||
|
|
@ -435,7 +520,7 @@ class Folder {
|
|||
foreach ($iterator as $itemPath => $fsIterator) {
|
||||
if ($skipHidden) {
|
||||
$subPathName = $fsIterator->getSubPathname();
|
||||
if ($subPathName{0} == '.' || strpos($subPathName, DS . '.') !== false) {
|
||||
if ($subPathName{0} === '.' || strpos($subPathName, DS . '.') !== false) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -460,12 +545,15 @@ class Folder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a directory structure recursively. Can be used to create
|
||||
* deep path structures like `/foo/bar/baz/shoe/horn`
|
||||
* Create a directory structure recursively.
|
||||
*
|
||||
* @param string $pathname The directory structure to create
|
||||
* @param integer $mode octal value 0755
|
||||
* @return boolean Returns TRUE on success, FALSE on failure
|
||||
* Can be used to create deep path structures like `/foo/bar/baz/shoe/horn`
|
||||
*
|
||||
* @param string $pathname The directory structure to create. Either an absolute or relative
|
||||
* path. If the path is relative and exists in the process' cwd it will not be created.
|
||||
* Otherwise relative paths will be prefixed with the current pwd().
|
||||
* @param int $mode octal value 0755
|
||||
* @return bool Returns TRUE on success, FALSE on failure
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::create
|
||||
*/
|
||||
public function create($pathname, $mode = false) {
|
||||
|
|
@ -473,6 +561,10 @@ class Folder {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!static::isAbsolute($pathname)) {
|
||||
$pathname = static::addPathElement($this->pwd(), $pathname);
|
||||
}
|
||||
|
||||
if (!$mode) {
|
||||
$mode = $this->mode;
|
||||
}
|
||||
|
|
@ -491,11 +583,10 @@ class Folder {
|
|||
umask($old);
|
||||
$this->_messages[] = __d('cake_dev', '%s created', $pathname);
|
||||
return true;
|
||||
} else {
|
||||
umask($old);
|
||||
$this->_errors[] = __d('cake_dev', '%s NOT created', $pathname);
|
||||
return false;
|
||||
}
|
||||
umask($old);
|
||||
$this->_errors[] = __d('cake_dev', '%s NOT created', $pathname);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
@ -504,7 +595,7 @@ class Folder {
|
|||
/**
|
||||
* Returns the size in bytes of this Folder and its contents.
|
||||
*
|
||||
* @return integer size in bytes of current folder
|
||||
* @return int size in bytes of current folder
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::dirsize
|
||||
*/
|
||||
public function dirsize() {
|
||||
|
|
@ -541,7 +632,7 @@ class Folder {
|
|||
* Recursively Remove directories if the system allows.
|
||||
*
|
||||
* @param string $path Path of directory to delete
|
||||
* @return boolean Success
|
||||
* @return bool Success
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::delete
|
||||
*/
|
||||
public function delete($path = null) {
|
||||
|
|
@ -549,7 +640,7 @@ class Folder {
|
|||
$path = $this->pwd();
|
||||
}
|
||||
if (!$path) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
$path = Folder::slashTerm($path);
|
||||
if (is_dir($path)) {
|
||||
|
|
@ -602,14 +693,15 @@ class Folder {
|
|||
*
|
||||
* - `to` The directory to copy to.
|
||||
* - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
|
||||
* - `mode` The mode to copy the files/directories with.
|
||||
* - `mode` The mode to copy the files/directories with as integer, e.g. 0775.
|
||||
* - `skip` Files/directories to skip.
|
||||
* - `scheme` Folder::MERGE, Folder::OVERWRITE, Folder::SKIP
|
||||
*
|
||||
* @param array|string $options Either an array of options (see above) or a string of the destination directory.
|
||||
* @return boolean Success
|
||||
* @return bool Success.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::copy
|
||||
*/
|
||||
public function copy($options = array()) {
|
||||
public function copy($options) {
|
||||
if (!$this->pwd()) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -618,7 +710,13 @@ class Folder {
|
|||
$to = $options;
|
||||
$options = array();
|
||||
}
|
||||
$options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
|
||||
$options += array(
|
||||
'to' => $to,
|
||||
'from' => $this->path,
|
||||
'mode' => $this->mode,
|
||||
'skip' => array(),
|
||||
'scheme' => Folder::MERGE
|
||||
);
|
||||
|
||||
$fromDir = $options['from'];
|
||||
$toDir = $options['to'];
|
||||
|
|
@ -642,11 +740,11 @@ class Folder {
|
|||
//@codingStandardsIgnoreStart
|
||||
if ($handle = @opendir($fromDir)) {
|
||||
//@codingStandardsIgnoreEnd
|
||||
while (false !== ($item = readdir($handle))) {
|
||||
if (!in_array($item, $exceptions)) {
|
||||
while (($item = readdir($handle)) !== false) {
|
||||
$to = Folder::addPathElement($toDir, $item);
|
||||
if (($options['scheme'] != Folder::SKIP || !is_dir($to)) && !in_array($item, $exceptions)) {
|
||||
$from = Folder::addPathElement($fromDir, $item);
|
||||
$to = Folder::addPathElement($toDir, $item);
|
||||
if (is_file($from)) {
|
||||
if (is_file($from) && (!is_file($to) || $options['scheme'] != Folder::SKIP)) {
|
||||
if (copy($from, $to)) {
|
||||
chmod($to, intval($mode, 8));
|
||||
touch($to, filemtime($from));
|
||||
|
|
@ -656,6 +754,10 @@ class Folder {
|
|||
}
|
||||
}
|
||||
|
||||
if (is_dir($from) && file_exists($to) && $options['scheme'] === Folder::OVERWRITE) {
|
||||
$this->delete($to);
|
||||
}
|
||||
|
||||
if (is_dir($from) && !file_exists($to)) {
|
||||
$old = umask(0);
|
||||
if (mkdir($to, $mode)) {
|
||||
|
|
@ -664,11 +766,14 @@ class Folder {
|
|||
chmod($to, $mode);
|
||||
umask($old);
|
||||
$this->_messages[] = __d('cake_dev', '%s created', $to);
|
||||
$options = array_merge($options, array('to' => $to, 'from' => $from));
|
||||
$options = array('to' => $to, 'from' => $from) + $options;
|
||||
$this->copy($options);
|
||||
} else {
|
||||
$this->_errors[] = __d('cake_dev', '%s not created', $to);
|
||||
}
|
||||
} elseif (is_dir($from) && $options['scheme'] === Folder::MERGE) {
|
||||
$options = array('to' => $to, 'from' => $from) + $options;
|
||||
$this->copy($options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -692,9 +797,10 @@ class Folder {
|
|||
* - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
|
||||
* - `chmod` The mode to copy the files/directories with.
|
||||
* - `skip` Files/directories to skip.
|
||||
* - `scheme` Folder::MERGE, Folder::OVERWRITE, Folder::SKIP
|
||||
*
|
||||
* @param array $options (to, from, chmod, skip)
|
||||
* @return boolean Success
|
||||
* @param array $options (to, from, chmod, skip, scheme)
|
||||
* @return bool Success
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::move
|
||||
*/
|
||||
public function move($options) {
|
||||
|
|
@ -703,10 +809,7 @@ class Folder {
|
|||
$to = $options;
|
||||
$options = (array)$options;
|
||||
}
|
||||
$options = array_merge(
|
||||
array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()),
|
||||
$options
|
||||
);
|
||||
$options += array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array());
|
||||
|
||||
if ($this->copy($options)) {
|
||||
if ($this->delete($options['from'])) {
|
||||
|
|
@ -719,21 +822,31 @@ class Folder {
|
|||
/**
|
||||
* get messages from latest method
|
||||
*
|
||||
* @param bool $reset Reset message stack after reading
|
||||
* @return array
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::messages
|
||||
*/
|
||||
public function messages() {
|
||||
return $this->_messages;
|
||||
public function messages($reset = true) {
|
||||
$messages = $this->_messages;
|
||||
if ($reset) {
|
||||
$this->_messages = array();
|
||||
}
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* get error from latest method
|
||||
*
|
||||
* @param bool $reset Reset error stack after reading
|
||||
* @return array
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::errors
|
||||
*/
|
||||
public function errors() {
|
||||
return $this->_errors;
|
||||
public function errors($reset = true) {
|
||||
$errors = $this->_errors;
|
||||
if ($reset) {
|
||||
$this->_errors = array();
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -744,13 +857,13 @@ class Folder {
|
|||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::realpath
|
||||
*/
|
||||
public function realpath($path) {
|
||||
$path = str_replace('/', DS, trim($path));
|
||||
if (strpos($path, '..') === false) {
|
||||
if (!Folder::isAbsolute($path)) {
|
||||
$path = Folder::addPathElement($this->path, $path);
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
$path = str_replace('/', DS, trim($path));
|
||||
$parts = explode(DS, $path);
|
||||
$newparts = array();
|
||||
$newpath = '';
|
||||
|
|
@ -766,9 +879,8 @@ class Folder {
|
|||
if (!empty($newparts)) {
|
||||
array_pop($newparts);
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$newparts[] = $part;
|
||||
}
|
||||
|
|
@ -781,7 +893,7 @@ class Folder {
|
|||
* Returns true if given $path ends in a slash (i.e. is slash-terminated).
|
||||
*
|
||||
* @param string $path Path to check
|
||||
* @return boolean true if path ends with slash, false otherwise
|
||||
* @return bool true if path ends with slash, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::isSlashTerm
|
||||
*/
|
||||
public static function isSlashTerm($path) {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Utility
|
||||
* @since CakePHP(tm) v 2.2.0
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @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
|
||||
* from arrays or 'sets' of data.
|
||||
*
|
||||
* `Hash` provides an improved interface, more consistent and
|
||||
* predictable set of features over `Set`. While it lacks the spotty
|
||||
* predictable set of features over `Set`. While it lacks the spotty
|
||||
* support for pseudo Xpath, its more fully featured dot notation provides
|
||||
* similar features in a more consistent implementation.
|
||||
*
|
||||
|
|
@ -36,25 +37,35 @@ class Hash {
|
|||
* @param array $data Array of data to operate on.
|
||||
* @param string|array $path The path being searched for. Either a dot
|
||||
* separated string, or an array of path segments.
|
||||
* @param mixed $default The return value when the path does not exist
|
||||
* @throws InvalidArgumentException
|
||||
* @return mixed The value fetched from the array, or null.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::get
|
||||
*/
|
||||
public static function get(array $data, $path) {
|
||||
if (empty($data) || empty($path)) {
|
||||
return null;
|
||||
public static function get(array $data, $path, $default = null) {
|
||||
if (empty($data) || $path === null) {
|
||||
return $default;
|
||||
}
|
||||
if (is_string($path)) {
|
||||
if (is_string($path) || is_numeric($path)) {
|
||||
$parts = explode('.', $path);
|
||||
} else {
|
||||
if (!is_array($path)) {
|
||||
throw new InvalidArgumentException(__d('cake_dev',
|
||||
'Invalid Parameter %s, should be dot separated path or array.',
|
||||
$path
|
||||
));
|
||||
}
|
||||
$parts = $path;
|
||||
}
|
||||
|
||||
foreach ($parts as $key) {
|
||||
if (is_array($data) && isset($data[$key])) {
|
||||
$data =& $data[$key];
|
||||
} else {
|
||||
return null;
|
||||
return $default;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +76,7 @@ class Hash {
|
|||
*
|
||||
* - `{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:
|
||||
|
|
@ -83,8 +95,9 @@ class Hash {
|
|||
*
|
||||
* @param array $data The data to extract from.
|
||||
* @param string $path The path to extract.
|
||||
* @return array An array of the extracted values. Returns an empty array
|
||||
* @return array An array of the extracted values. Returns an empty array
|
||||
* if there are no matches.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::extract
|
||||
*/
|
||||
public static function extract(array $data, $path) {
|
||||
if (empty($path)) {
|
||||
|
|
@ -93,13 +106,13 @@ class Hash {
|
|||
|
||||
// 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__';
|
||||
|
|
@ -109,16 +122,11 @@ class Hash {
|
|||
foreach ($tokens as $token) {
|
||||
$next = array();
|
||||
|
||||
$conditions = false;
|
||||
$position = strpos($token, '[');
|
||||
if ($position !== false) {
|
||||
$conditions = substr($token, $position);
|
||||
$token = substr($token, 0, $position);
|
||||
}
|
||||
list($token, $conditions) = static::_splitConditions($token);
|
||||
|
||||
foreach ($context[$_key] as $item) {
|
||||
foreach ($item as $k => $v) {
|
||||
if (self::_matchToken($k, $token)) {
|
||||
foreach ((array)$item as $k => $v) {
|
||||
if (static::_matchToken($k, $token)) {
|
||||
$next[] = $v;
|
||||
}
|
||||
}
|
||||
|
|
@ -128,7 +136,7 @@ class Hash {
|
|||
if ($conditions) {
|
||||
$filter = array();
|
||||
foreach ($next as $item) {
|
||||
if (self::_matches($item, $conditions)) {
|
||||
if (is_array($item) && static::_matches($item, $conditions)) {
|
||||
$filter[] = $item;
|
||||
}
|
||||
}
|
||||
|
|
@ -139,25 +147,41 @@ class Hash {
|
|||
}
|
||||
return $context[$_key];
|
||||
}
|
||||
/**
|
||||
* Split token conditions
|
||||
*
|
||||
* @param string $token the token being splitted.
|
||||
* @return array array(token, conditions) with token splitted
|
||||
*/
|
||||
protected static function _splitConditions($token) {
|
||||
$conditions = false;
|
||||
$position = strpos($token, '[');
|
||||
if ($position !== false) {
|
||||
$conditions = substr($token, $position);
|
||||
$token = substr($token, 0, $position);
|
||||
}
|
||||
|
||||
return array($token, $conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a key against a token.
|
||||
*
|
||||
* @param string $key The key in the array being searched.
|
||||
* @param string $token The token being matched.
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
protected static function _matchToken($key, $token) {
|
||||
if ($token === '{n}') {
|
||||
return is_numeric($key);
|
||||
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;
|
||||
}
|
||||
if ($token === '{s}') {
|
||||
return is_string($key);
|
||||
}
|
||||
if (is_numeric($token)) {
|
||||
return ($key == $token);
|
||||
}
|
||||
return ($key === $token);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -165,11 +189,11 @@ class Hash {
|
|||
*
|
||||
* @param array $data Array of data to match.
|
||||
* @param string $selector The patterns to match.
|
||||
* @return boolean Fitness of expression.
|
||||
* @return bool Fitness of expression.
|
||||
*/
|
||||
protected static function _matches(array $data, $selector) {
|
||||
preg_match_all(
|
||||
'/(\[ (?<attr>[^=><!]+?) (\s* (?<op>[><!]?[=]|[><]) \s* (?<val>[^\]]+) )? \])/x',
|
||||
'/(\[ (?P<attr>[^=><!]+?) (\s* (?P<op>[><!]?[=]|[><]) \s* (?P<val>(?:\/.*?\/ | [^\]]+)) )? \])/x',
|
||||
$selector,
|
||||
$conditions,
|
||||
PREG_SET_ORDER
|
||||
|
|
@ -190,15 +214,23 @@ class Hash {
|
|||
return false;
|
||||
}
|
||||
|
||||
$prop = isset($data[$attr]) ? $data[$attr] : null;
|
||||
$prop = null;
|
||||
if (isset($data[$attr])) {
|
||||
$prop = $data[$attr];
|
||||
}
|
||||
$isBool = is_bool($prop);
|
||||
if ($isBool && is_numeric($val)) {
|
||||
$prop = $prop ? '1' : '0';
|
||||
} elseif ($isBool) {
|
||||
$prop = $prop ? 'true' : 'false';
|
||||
}
|
||||
|
||||
// Pattern matches and other operators.
|
||||
if ($op === '=' && $val && $val[0] === '/') {
|
||||
if (!preg_match($val, $prop)) {
|
||||
return false;
|
||||
}
|
||||
} elseif (
|
||||
($op === '=' && $prop != $val) ||
|
||||
} elseif (($op === '=' && $prop != $val) ||
|
||||
($op === '!=' && $prop == $val) ||
|
||||
($op === '>' && $prop <= $val) ||
|
||||
($op === '<' && $prop >= $val) ||
|
||||
|
|
@ -218,20 +250,35 @@ class Hash {
|
|||
*
|
||||
* @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
|
||||
*/
|
||||
public static function insert(array $data, $path, $values = null) {
|
||||
$tokens = explode('.', $path);
|
||||
if (strpos($path, '{') === false) {
|
||||
return self::_simpleOp('insert', $data, $tokens, $values);
|
||||
if (strpos($path, '[') === false) {
|
||||
$tokens = explode('.', $path);
|
||||
} else {
|
||||
$tokens = CakeText::tokenize($path, '.', '[', ']');
|
||||
}
|
||||
|
||||
if (strpos($path, '{') === false && strpos($path, '[') === false) {
|
||||
return static::_simpleOp('insert', $data, $tokens, $values);
|
||||
}
|
||||
|
||||
$token = array_shift($tokens);
|
||||
$nextPath = implode('.', $tokens);
|
||||
|
||||
list($token, $conditions) = static::_splitConditions($token);
|
||||
|
||||
foreach ($data as $k => $v) {
|
||||
if (self::_matchToken($k, $token)) {
|
||||
$data[$k] = self::insert($v, $nextPath, $values);
|
||||
if (static::_matchToken($k, $token)) {
|
||||
if ($conditions && static::_matches($v, $conditions)) {
|
||||
$data[$k] = array_merge($v, $values);
|
||||
continue;
|
||||
}
|
||||
if (!$conditions) {
|
||||
$data[$k] = static::insert($v, $nextPath, $values);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
|
|
@ -244,7 +291,7 @@ class Hash {
|
|||
* @param array $data The data to operate on.
|
||||
* @param array $path The path to work on.
|
||||
* @param mixed $values The values to insert when doing inserts.
|
||||
* @return array $data.
|
||||
* @return array data.
|
||||
*/
|
||||
protected static function _simpleOp($op, $data, $path, $values = null) {
|
||||
$_list =& $data;
|
||||
|
|
@ -252,8 +299,8 @@ class Hash {
|
|||
$count = count($path);
|
||||
$last = $count - 1;
|
||||
foreach ($path as $i => $key) {
|
||||
if (is_numeric($key) && intval($key) > 0 || $key === '0') {
|
||||
$key = intval($key);
|
||||
if ((is_numeric($key) && intval($key) > 0 || $key === '0') && strpos($key, '0') !== 0) {
|
||||
$key = (int)$key;
|
||||
}
|
||||
if ($op === 'insert') {
|
||||
if ($i === $last) {
|
||||
|
|
@ -288,20 +335,36 @@ class Hash {
|
|||
* @param array $data The data to operate on
|
||||
* @param string $path A path expression to use to remove.
|
||||
* @return array The modified array.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::remove
|
||||
*/
|
||||
public static function remove(array $data, $path) {
|
||||
$tokens = explode('.', $path);
|
||||
if (strpos($path, '{') === false) {
|
||||
return self::_simpleOp('remove', $data, $tokens);
|
||||
if (strpos($path, '[') === false) {
|
||||
$tokens = explode('.', $path);
|
||||
} else {
|
||||
$tokens = CakeText::tokenize($path, '.', '[', ']');
|
||||
}
|
||||
|
||||
if (strpos($path, '{') === false && strpos($path, '[') === false) {
|
||||
return static::_simpleOp('remove', $data, $tokens);
|
||||
}
|
||||
|
||||
$token = array_shift($tokens);
|
||||
$nextPath = implode('.', $tokens);
|
||||
|
||||
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)) {
|
||||
$data[$k] = self::remove($v, $nextPath);
|
||||
} elseif ($match) {
|
||||
if ($conditions && static::_matches($v, $conditions)) {
|
||||
unset($data[$k]);
|
||||
continue;
|
||||
}
|
||||
$data[$k] = static::remove($v, $nextPath);
|
||||
if (empty($data[$k])) {
|
||||
unset($data[$k]);
|
||||
}
|
||||
} elseif ($match && empty($nextPath)) {
|
||||
unset($data[$k]);
|
||||
}
|
||||
}
|
||||
|
|
@ -320,6 +383,7 @@ class Hash {
|
|||
* @param string $groupPath A dot-separated string.
|
||||
* @return array Combined array
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::combine
|
||||
* @throws CakeException CakeException When keys and values count is unequal.
|
||||
*/
|
||||
public static function combine(array $data, $keyPath, $valuePath = null, $groupPath = null) {
|
||||
if (empty($data)) {
|
||||
|
|
@ -328,9 +392,9 @@ class Hash {
|
|||
|
||||
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();
|
||||
|
|
@ -338,18 +402,23 @@ class Hash {
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
$count = count($keys);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$vals[$i] = isset($vals[$i]) ? $vals[$i] : null;
|
||||
if (count($keys) !== count($vals)) {
|
||||
throw new CakeException(__d(
|
||||
'cake_dev',
|
||||
'Hash::combine() needs an equal number of keys + values.'
|
||||
));
|
||||
}
|
||||
|
||||
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++) {
|
||||
|
|
@ -371,14 +440,14 @@ class Hash {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a formated series of values extracted from `$data`, using
|
||||
* Returns a formatted series of values extracted from `$data`, using
|
||||
* `$format` as the format and `$paths` as the values to extract.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* {{{
|
||||
* ```
|
||||
* $result = Hash::format($users, array('{n}.User.id', '{n}.User.name'), '%s : %s');
|
||||
* }}}
|
||||
* ```
|
||||
*
|
||||
* The `$format` string can use any format options that `vsprintf()` and `sprintf()` do.
|
||||
*
|
||||
|
|
@ -389,17 +458,18 @@ class Hash {
|
|||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::format
|
||||
* @see sprintf()
|
||||
* @see Hash::extract()
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::format
|
||||
*/
|
||||
public static function format(array $data, array $paths, $format) {
|
||||
$extracted = array();
|
||||
$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;
|
||||
|
|
@ -423,7 +493,7 @@ class Hash {
|
|||
*
|
||||
* @param array $data The data to search through.
|
||||
* @param array $needle The values to file in $data
|
||||
* @return boolean true if $data contains $needle, false otherwise
|
||||
* @return bool true if $data contains $needle, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::contains
|
||||
*/
|
||||
public static function contains(array $data, array $needle) {
|
||||
|
|
@ -437,14 +507,14 @@ class Hash {
|
|||
$val = $needle[$key];
|
||||
unset($needle[$key]);
|
||||
|
||||
if (isset($data[$key]) && is_array($val)) {
|
||||
if (array_key_exists($key, $data) && is_array($val)) {
|
||||
$next = $data[$key];
|
||||
unset($data[$key]);
|
||||
|
||||
if (!empty($val)) {
|
||||
$stack[] = array($val, $next);
|
||||
}
|
||||
} elseif (!isset($data[$key]) || $data[$key] != $val) {
|
||||
} elseif (!array_key_exists($key, $data) || $data[$key] != $val) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -464,11 +534,12 @@ class Hash {
|
|||
*
|
||||
* @param array $data The data to check.
|
||||
* @param string $path The path to check for.
|
||||
* @return boolean Existence of path.
|
||||
* @return bool Existence of path.
|
||||
* @see Hash::extract()
|
||||
* @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;
|
||||
}
|
||||
|
|
@ -479,15 +550,15 @@ class Hash {
|
|||
* Recursively filters a data set.
|
||||
*
|
||||
* @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.
|
||||
* @param callable $callback A function to filter the data with. Defaults to
|
||||
* `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);
|
||||
|
|
@ -497,7 +568,7 @@ class Hash {
|
|||
* Callback function for filtering.
|
||||
*
|
||||
* @param array $var Array to filter.
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
protected static function _filter($var) {
|
||||
if ($var === 0 || $var === '0' || !empty($var)) {
|
||||
|
|
@ -547,7 +618,7 @@ class Hash {
|
|||
}
|
||||
|
||||
/**
|
||||
* Expand/unflattens an string to an array
|
||||
* Expands a flat array to a nested array.
|
||||
*
|
||||
* For example, unflattens an array that was collapsed with `Hash::flatten()`
|
||||
* into a multi-dimensional array. So, `array('0.Foo.Bar' => 'Far')` becomes
|
||||
|
|
@ -556,9 +627,13 @@ class Hash {
|
|||
* @param array $data Flattened array
|
||||
* @param string $separator The delimiter used
|
||||
* @return array
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::expand
|
||||
*/
|
||||
public static function expand($data, $separator = '.') {
|
||||
$result = array();
|
||||
|
||||
$stack = array();
|
||||
|
||||
foreach ($data as $flat => $value) {
|
||||
$keys = explode($separator, $flat);
|
||||
$keys = array_reverse($keys);
|
||||
|
|
@ -571,7 +646,24 @@ class Hash {
|
|||
$k => $child
|
||||
);
|
||||
}
|
||||
$result = self::merge($result, $child);
|
||||
|
||||
$stack[] = array($child, &$result);
|
||||
|
||||
while (!empty($stack)) {
|
||||
foreach ($stack as $curKey => &$curMerge) {
|
||||
foreach ($curMerge[0] as $key => &$val) {
|
||||
if (!empty($curMerge[1][$key]) && (array)$curMerge[1][$key] === $curMerge[1][$key] && (array)$val === $val) {
|
||||
$stack[] = array(&$val, &$curMerge[1][$key]);
|
||||
} elseif ((int)$key === $key && isset($curMerge[1][$key])) {
|
||||
$curMerge[1][] = $val;
|
||||
} else {
|
||||
$curMerge[1][$key] = $val;
|
||||
}
|
||||
}
|
||||
unset($stack[$curKey]);
|
||||
}
|
||||
unset($curMerge);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
|
@ -580,7 +672,7 @@ class Hash {
|
|||
* This function can be thought of as a hybrid between PHP's `array_merge` and `array_merge_recursive`.
|
||||
*
|
||||
* The difference between this method and the built-in ones, is that if an array key contains another array, then
|
||||
* Hash::merge() will behave in a recursive fashion (unlike `array_merge`). But it will not act recursively for
|
||||
* Hash::merge() will behave in a recursive fashion (unlike `array_merge`). But it will not act recursively for
|
||||
* keys that contain scalar values (unlike `array_merge_recursive`).
|
||||
*
|
||||
* Note: This function will work with an unlimited amount of arguments and typecasts non-array parameters into arrays.
|
||||
|
|
@ -591,19 +683,28 @@ class Hash {
|
|||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::merge
|
||||
*/
|
||||
public static function merge(array $data, $merge) {
|
||||
$args = func_get_args();
|
||||
$return = current($args);
|
||||
$args = array_slice(func_get_args(), 1);
|
||||
$return = $data;
|
||||
|
||||
while (($arg = next($args)) !== false) {
|
||||
foreach ((array)$arg as $key => $val) {
|
||||
if (!empty($return[$key]) && is_array($return[$key]) && is_array($val)) {
|
||||
$return[$key] = self::merge($return[$key], $val);
|
||||
} elseif (is_int($key)) {
|
||||
$return[] = $val;
|
||||
} else {
|
||||
$return[$key] = $val;
|
||||
foreach ($args as &$curArg) {
|
||||
$stack[] = array((array)$curArg, &$return);
|
||||
}
|
||||
unset($curArg);
|
||||
|
||||
while (!empty($stack)) {
|
||||
foreach ($stack as $curKey => &$curMerge) {
|
||||
foreach ($curMerge[0] as $key => &$val) {
|
||||
if (!empty($curMerge[1][$key]) && (array)$curMerge[1][$key] === $curMerge[1][$key] && (array)$val === $val) {
|
||||
$stack[] = array(&$val, &$curMerge[1][$key]);
|
||||
} elseif ((int)$key === $key && isset($curMerge[1][$key])) {
|
||||
$curMerge[1][] = $val;
|
||||
} else {
|
||||
$curMerge[1][$key] = $val;
|
||||
}
|
||||
}
|
||||
unset($stack[$curKey]);
|
||||
}
|
||||
unset($curMerge);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
|
@ -611,28 +712,26 @@ class Hash {
|
|||
/**
|
||||
* Checks to see if all the values in the array are numeric
|
||||
*
|
||||
* @param array $array The array to check.
|
||||
* @return boolean true if values are numeric, false otherwise
|
||||
* @param array $data The array to check.
|
||||
* @return bool true if values are numeric, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::numeric
|
||||
*/
|
||||
public static function numeric(array $data) {
|
||||
if (empty($data)) {
|
||||
return false;
|
||||
}
|
||||
$values = array_values($data);
|
||||
$str = implode('', $values);
|
||||
return (bool)ctype_digit($str);
|
||||
return $data === array_filter($data, 'is_numeric');
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the dimensions of an array.
|
||||
* Only considers the dimension of the first element in the array.
|
||||
*
|
||||
* If you have an un-even or hetrogenous array, consider using Hash::maxDimensions()
|
||||
* If you have an un-even or heterogenous array, consider using Hash::maxDimensions()
|
||||
* to get the dimensions of the array.
|
||||
*
|
||||
* @param array $array Array to count dimensions on
|
||||
* @return integer The number of dimensions in $data
|
||||
* @param array $data Array to count dimensions on
|
||||
* @return int The number of dimensions in $data
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::dimensions
|
||||
*/
|
||||
public static function dimensions(array $data) {
|
||||
|
|
@ -657,17 +756,17 @@ class Hash {
|
|||
* number of dimensions in a mixed array.
|
||||
*
|
||||
* @param array $data Array to count dimensions on
|
||||
* @return integer The maximum number of dimensions in $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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -678,9 +777,10 @@ class Hash {
|
|||
* @param string $path The path to extract for mapping over.
|
||||
* @param callable $function The function to call on each extracted value.
|
||||
* @return array An array of the modified values.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
|
@ -689,10 +789,12 @@ class Hash {
|
|||
*
|
||||
* @param array $data The data to reduce.
|
||||
* @param string $path The path to extract from $data.
|
||||
* @param callable $function The function to call on each extracted value.
|
||||
* @return mixed The reduced value.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
|
@ -713,15 +815,16 @@ class Hash {
|
|||
*
|
||||
* @param array $data The data to reduce.
|
||||
* @param string $path The path to extract from $data.
|
||||
* @param callable $function The function to call on each extracted value.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an array by any value, determined by a Set-compatible path
|
||||
* Sorts an array by any value, determined by a Hash-compatible path
|
||||
*
|
||||
* ### Sort directions
|
||||
*
|
||||
|
|
@ -730,19 +833,30 @@ class Hash {
|
|||
*
|
||||
* ## Sort types
|
||||
*
|
||||
* - `numeric` Sort by numeric value.
|
||||
* - `regular` Sort by numeric value.
|
||||
* - `string` Sort by numeric value.
|
||||
* - `natural` Sort by natural order. Requires PHP 5.4 or greater.
|
||||
* - `regular` For regular sorting (don't change types)
|
||||
* - `numeric` Compare values numerically
|
||||
* - `string` Compare values as strings
|
||||
* - `locale` Compare items as strings, based on the current locale
|
||||
* - `natural` Compare items as strings using "natural ordering" in a human friendly way.
|
||||
* Will sort foo10 below foo2 as an example. Requires PHP 5.4 or greater or it will fallback to 'regular'
|
||||
*
|
||||
* To do case insensitive sorting, pass the type as an array as follows:
|
||||
*
|
||||
* ```
|
||||
* array('type' => 'regular', 'ignoreCase' => true)
|
||||
* ```
|
||||
*
|
||||
* When using the array form, `type` defaults to 'regular'. The `ignoreCase` option
|
||||
* defaults to `false`.
|
||||
*
|
||||
* @param array $data An array of data to sort
|
||||
* @param string $path A Set-compatible path to the array value
|
||||
* @param string $dir See directions above.
|
||||
* @param string $type See direction types above. Defaults to 'regular'.
|
||||
* @param string $path A Hash-compatible path to the array value
|
||||
* @param string $dir See directions above. Defaults to 'asc'.
|
||||
* @param array|string $type See direction types above. Defaults to 'regular'.
|
||||
* @return array Sorted array of data
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::sort
|
||||
*/
|
||||
public static function sort(array $data, $path, $dir, $type = 'regular') {
|
||||
public static function sort(array $data, $path, $dir = 'asc', $type = 'regular') {
|
||||
if (empty($data)) {
|
||||
return array();
|
||||
}
|
||||
|
|
@ -751,7 +865,7 @@ class Hash {
|
|||
if ($numeric) {
|
||||
$data = array_values($data);
|
||||
}
|
||||
$sortValues = self::extract($data, $path);
|
||||
$sortValues = static::extract($data, $path);
|
||||
$sortCount = count($sortValues);
|
||||
$dataCount = count($data);
|
||||
|
||||
|
|
@ -760,15 +874,26 @@ class Hash {
|
|||
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);
|
||||
if ($type == 'natural' && version_compare(PHP_VERSION, '5.4.0', '<')) {
|
||||
$type == 'regular';
|
||||
$ignoreCase = false;
|
||||
|
||||
// $type can be overloaded for case insensitive sort
|
||||
if (is_array($type)) {
|
||||
$type += array('ignoreCase' => false, 'type' => 'regular');
|
||||
$ignoreCase = $type['ignoreCase'];
|
||||
$type = $type['type'];
|
||||
} else {
|
||||
$type = strtolower($type);
|
||||
}
|
||||
|
||||
if ($type === 'natural' && version_compare(PHP_VERSION, '5.4.0', '<')) {
|
||||
$type = 'regular';
|
||||
}
|
||||
|
||||
if ($dir === 'asc') {
|
||||
$dir = SORT_ASC;
|
||||
} else {
|
||||
|
|
@ -780,10 +905,17 @@ class Hash {
|
|||
$type = SORT_STRING;
|
||||
} elseif ($type === 'natural') {
|
||||
$type = SORT_NATURAL;
|
||||
} elseif ($type === 'locale') {
|
||||
$type = SORT_LOCALE_STRING;
|
||||
} else {
|
||||
$type = SORT_REGULAR;
|
||||
}
|
||||
array_multisort($values, $dir, $type, $keys, $dir, $type);
|
||||
|
||||
if ($ignoreCase) {
|
||||
$values = array_map('mb_strtolower', $values);
|
||||
}
|
||||
array_multisort($values, $dir, $type, $keys, $dir);
|
||||
|
||||
$sorted = array();
|
||||
$keys = array_unique($keys);
|
||||
|
||||
|
|
@ -803,7 +935,7 @@ class Hash {
|
|||
|
||||
/**
|
||||
* Helper method for sort()
|
||||
* Sqaushes an array to a single hash so it can be sorted.
|
||||
* Squashes an array to a single hash so it can be sorted.
|
||||
*
|
||||
* @param array $data The data to squash.
|
||||
* @param string $key The key for the data.
|
||||
|
|
@ -813,11 +945,11 @@ class Hash {
|
|||
$stack = array();
|
||||
foreach ($data as $k => $r) {
|
||||
$id = $k;
|
||||
if (!is_null($key)) {
|
||||
if ($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);
|
||||
}
|
||||
|
|
@ -855,11 +987,12 @@ class Hash {
|
|||
}
|
||||
|
||||
/**
|
||||
* Merges the difference between $data and $push onto $data.
|
||||
* Merges the difference between $data and $compare onto $data.
|
||||
*
|
||||
* @param array $data The data to append onto.
|
||||
* @param array $compare The data to compare and append onto.
|
||||
* @return array The merged array.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::mergeDiff
|
||||
*/
|
||||
public static function mergeDiff(array $data, $compare) {
|
||||
if (empty($data) && !empty($compare)) {
|
||||
|
|
@ -872,7 +1005,7 @@ class Hash {
|
|||
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;
|
||||
|
|
@ -882,7 +1015,7 @@ class Hash {
|
|||
* Normalizes an array, and converts it to a standard format.
|
||||
*
|
||||
* @param array $data List to normalize
|
||||
* @param boolean $assoc If true, $data will be converted to an associative array.
|
||||
* @param bool $assoc If true, $data will be converted to an associative array.
|
||||
* @return array
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::normalize
|
||||
*/
|
||||
|
|
@ -929,6 +1062,8 @@ class Hash {
|
|||
* @param array $options Options are:
|
||||
* @return array of results, nested
|
||||
* @see Hash::extract()
|
||||
* @throws InvalidArgumentException When providing invalid data.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::nest
|
||||
*/
|
||||
public static function nest(array $data, $options = array()) {
|
||||
if (!$data) {
|
||||
|
|
@ -944,7 +1079,7 @@ class Hash {
|
|||
);
|
||||
|
||||
$return = $idMap = array();
|
||||
$ids = self::extract($data, $options['idPath']);
|
||||
$ids = static::extract($data, $options['idPath']);
|
||||
|
||||
$idKeys = explode('.', $options['idPath']);
|
||||
array_shift($idKeys);
|
||||
|
|
@ -955,8 +1090,8 @@ class Hash {
|
|||
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]);
|
||||
|
|
@ -970,15 +1105,21 @@ class Hash {
|
|||
}
|
||||
}
|
||||
|
||||
if (!$return) {
|
||||
throw new InvalidArgumentException(__d('cake_dev',
|
||||
'Invalid data array to nest.'
|
||||
));
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,24 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Utility
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Pluralize and singularize English words.
|
||||
*
|
||||
* Inflector pluralizes and singularizes English nouns.
|
||||
* Used by Cake's naming conventions throughout the framework.
|
||||
* Used by CakePHP's naming conventions throughout the framework.
|
||||
*
|
||||
* @package Cake.Utility
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html
|
||||
|
|
@ -31,7 +32,7 @@ class Inflector {
|
|||
*/
|
||||
protected static $_plural = array(
|
||||
'rules' => array(
|
||||
'/(s)tatus$/i' => '\1\2tatuses',
|
||||
'/(s)tatus$/i' => '\1tatuses',
|
||||
'/(quiz)$/i' => '\1zes',
|
||||
'/^(ox)$/i' => '\1\2en',
|
||||
'/([m|l])ouse$/i' => '\1ice',
|
||||
|
|
@ -39,14 +40,14 @@ class Inflector {
|
|||
'/(x|ch|ss|sh)$/i' => '\1es',
|
||||
'/([^aeiouy]|qu)y$/i' => '\1ies',
|
||||
'/(hive)$/i' => '\1s',
|
||||
'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
|
||||
'/(?:([^f])fe|([lre])f)$/i' => '\1\2ves',
|
||||
'/sis$/i' => 'ses',
|
||||
'/([ti])um$/i' => '\1a',
|
||||
'/(p)erson$/i' => '\1eople',
|
||||
'/(m)an$/i' => '\1en',
|
||||
'/(?<!u)(m)an$/i' => '\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',
|
||||
|
|
@ -55,17 +56,29 @@ class Inflector {
|
|||
'/$/' => 's',
|
||||
),
|
||||
'uninflected' => array(
|
||||
'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people', 'cookie'
|
||||
'.*[nrlm]ese',
|
||||
'.*data',
|
||||
'.*deer',
|
||||
'.*fish',
|
||||
'.*measles',
|
||||
'.*ois',
|
||||
'.*pox',
|
||||
'.*sheep',
|
||||
'people',
|
||||
'feedback',
|
||||
'stadia'
|
||||
),
|
||||
'irregular' => array(
|
||||
'atlas' => 'atlases',
|
||||
'beef' => 'beefs',
|
||||
'brief' => 'briefs',
|
||||
'brother' => 'brothers',
|
||||
'cafe' => 'cafes',
|
||||
'child' => 'children',
|
||||
'cookie' => 'cookies',
|
||||
'corpus' => 'corpuses',
|
||||
'cow' => 'cows',
|
||||
'criterion' => 'criteria',
|
||||
'ganglion' => 'ganglions',
|
||||
'genie' => 'genies',
|
||||
'genus' => 'genera',
|
||||
|
|
@ -89,7 +102,13 @@ class Inflector {
|
|||
'soliloquy' => 'soliloquies',
|
||||
'testis' => 'testes',
|
||||
'trilby' => 'trilbys',
|
||||
'turf' => 'turfs'
|
||||
'turf' => 'turfs',
|
||||
'potato' => 'potatoes',
|
||||
'hero' => 'heroes',
|
||||
'tooth' => 'teeth',
|
||||
'goose' => 'geese',
|
||||
'foot' => 'feet',
|
||||
'sieve' => 'sieves'
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -110,7 +129,7 @@ class Inflector {
|
|||
'/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
|
||||
'/([ftw]ax)es/i' => '\1',
|
||||
'/(cris|ax|test)es$/i' => '\1is',
|
||||
'/(shoe|slave)s$/i' => '\1',
|
||||
'/(shoe)s$/i' => '\1',
|
||||
'/(o)es$/i' => '\1',
|
||||
'/ouses$/' => 'ouse',
|
||||
'/([^a])uses$/' => '\1us',
|
||||
|
|
@ -119,11 +138,11 @@ class Inflector {
|
|||
'/(m)ovies$/i' => '\1\2ovie',
|
||||
'/(s)eries$/i' => '\1\2eries',
|
||||
'/([^aeiouy]|qu)ies$/i' => '\1y',
|
||||
'/([lr])ves$/i' => '\1f',
|
||||
'/(tive)s$/i' => '\1',
|
||||
'/(hive)s$/i' => '\1',
|
||||
'/(drive)s$/i' => '\1',
|
||||
'/([^fo])ves$/i' => '\1fe',
|
||||
'/([le])ves$/i' => '\1f',
|
||||
'/([^rfoa])ves$/i' => '\1fe',
|
||||
'/(^analy)ses$/i' => '\1sis',
|
||||
'/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
|
||||
'/([ti])a$/i' => '\1um',
|
||||
|
|
@ -136,12 +155,11 @@ class Inflector {
|
|||
'/s$/i' => ''
|
||||
),
|
||||
'uninflected' => array(
|
||||
'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss'
|
||||
'.*data',
|
||||
'.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss', 'feedback'
|
||||
),
|
||||
'irregular' => array(
|
||||
'foes' => 'foe',
|
||||
'waves' => 'wave',
|
||||
'curves' => 'curve'
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -159,7 +177,7 @@ class Inflector {
|
|||
'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', '.*?media',
|
||||
'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese',
|
||||
'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese',
|
||||
'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors',
|
||||
'proceedings', 'rabies', 'research', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors',
|
||||
'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'swine', 'testes',
|
||||
'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', 'whiting', 'wildebeest',
|
||||
'Yengeese'
|
||||
|
|
@ -171,56 +189,63 @@ class Inflector {
|
|||
* @var array
|
||||
*/
|
||||
protected static $_transliteration = array(
|
||||
'/ä|æ|ǽ/' => 'ae',
|
||||
'/ö|œ/' => 'oe',
|
||||
'/ü/' => 'ue',
|
||||
'/Ä/' => 'Ae',
|
||||
'/Ü/' => 'Ue',
|
||||
'/Ö/' => 'Oe',
|
||||
'/À|Á|Â|Ã|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A',
|
||||
'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a',
|
||||
'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
|
||||
'/ç|ć|ĉ|ċ|č/' => 'c',
|
||||
'/Ð|Ď|Đ/' => 'D',
|
||||
'/ð|ď|đ/' => 'd',
|
||||
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E',
|
||||
'/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e',
|
||||
'/Ĝ|Ğ|Ġ|Ģ/' => 'G',
|
||||
'/ĝ|ğ|ġ|ģ/' => 'g',
|
||||
'/Ĥ|Ħ/' => 'H',
|
||||
'/ĥ|ħ/' => 'h',
|
||||
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I',
|
||||
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i',
|
||||
'/Ĵ/' => 'J',
|
||||
'/ĵ/' => 'j',
|
||||
'/Ķ/' => 'K',
|
||||
'/ķ/' => 'k',
|
||||
'/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L',
|
||||
'/ĺ|ļ|ľ|ŀ|ł/' => 'l',
|
||||
'/Ñ|Ń|Ņ|Ň/' => 'N',
|
||||
'/ñ|ń|ņ|ň|ʼn/' => 'n',
|
||||
'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O',
|
||||
'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o',
|
||||
'/Ŕ|Ŗ|Ř/' => 'R',
|
||||
'/ŕ|ŗ|ř/' => 'r',
|
||||
'/Ś|Ŝ|Ş|Š/' => 'S',
|
||||
'/ś|ŝ|ş|š|ſ/' => 's',
|
||||
'/Ţ|Ť|Ŧ/' => 'T',
|
||||
'/ţ|ť|ŧ/' => 't',
|
||||
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
|
||||
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
|
||||
'/Ý|Ÿ|Ŷ/' => 'Y',
|
||||
'/ý|ÿ|ŷ/' => 'y',
|
||||
'/Ŵ/' => 'W',
|
||||
'/ŵ/' => 'w',
|
||||
'/Ź|Ż|Ž/' => 'Z',
|
||||
'/ź|ż|ž/' => 'z',
|
||||
'/Æ|Ǽ/' => 'AE',
|
||||
'/ß/' => 'ss',
|
||||
'/Ä/' => 'Ae',
|
||||
'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
|
||||
'/Ð|Ď|Đ/' => 'D',
|
||||
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E',
|
||||
'/Ĝ|Ğ|Ġ|Ģ|Ґ/' => 'G',
|
||||
'/Ĥ|Ħ/' => 'H',
|
||||
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|І/' => 'I',
|
||||
'/IJ/' => 'IJ',
|
||||
'/ij/' => 'ij',
|
||||
'/Ĵ/' => 'J',
|
||||
'/Ķ/' => 'K',
|
||||
'/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L',
|
||||
'/Ñ|Ń|Ņ|Ň/' => 'N',
|
||||
'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O',
|
||||
'/Œ/' => 'OE',
|
||||
'/ƒ/' => 'f'
|
||||
'/Ö/' => 'Oe',
|
||||
'/Ŕ|Ŗ|Ř/' => 'R',
|
||||
'/Ś|Ŝ|Ş|Ș|Š/' => 'S',
|
||||
'/ẞ/' => 'SS',
|
||||
'/Ţ|Ț|Ť|Ŧ/' => 'T',
|
||||
'/Þ/' => 'TH',
|
||||
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
|
||||
'/Ü/' => 'Ue',
|
||||
'/Ŵ/' => 'W',
|
||||
'/Ý|Ÿ|Ŷ/' => 'Y',
|
||||
'/Є/' => 'Ye',
|
||||
'/Ї/' => 'Yi',
|
||||
'/Ź|Ż|Ž/' => 'Z',
|
||||
'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a',
|
||||
'/ä|æ|ǽ/' => 'ae',
|
||||
'/ç|ć|ĉ|ċ|č/' => 'c',
|
||||
'/ð|ď|đ/' => 'd',
|
||||
'/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e',
|
||||
'/ƒ/' => 'f',
|
||||
'/ĝ|ğ|ġ|ģ|ґ/' => 'g',
|
||||
'/ĥ|ħ/' => 'h',
|
||||
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|і/' => 'i',
|
||||
'/ij/' => 'ij',
|
||||
'/ĵ/' => 'j',
|
||||
'/ķ/' => 'k',
|
||||
'/ĺ|ļ|ľ|ŀ|ł/' => 'l',
|
||||
'/ñ|ń|ņ|ň|ʼn/' => 'n',
|
||||
'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o',
|
||||
'/ö|œ/' => 'oe',
|
||||
'/ŕ|ŗ|ř/' => 'r',
|
||||
'/ś|ŝ|ş|ș|š|ſ/' => 's',
|
||||
'/ß/' => 'ss',
|
||||
'/ţ|ț|ť|ŧ/' => 't',
|
||||
'/þ/' => 'th',
|
||||
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
|
||||
'/ü/' => 'ue',
|
||||
'/ŵ/' => 'w',
|
||||
'/ý|ÿ|ŷ/' => 'y',
|
||||
'/є/' => 'ye',
|
||||
'/ї/' => 'yi',
|
||||
'/ź|ż|ž/' => 'z',
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
@ -249,13 +274,13 @@ class Inflector {
|
|||
$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];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -265,13 +290,13 @@ class Inflector {
|
|||
* @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) {
|
||||
if ($key != '_initialState') {
|
||||
self::${$key} = $val;
|
||||
foreach (static::$_initialState as $key => $val) {
|
||||
if ($key !== '_initialState') {
|
||||
static::${$key} = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -281,7 +306,7 @@ class Inflector {
|
|||
*
|
||||
* ### Usage:
|
||||
*
|
||||
* {{{
|
||||
* ```
|
||||
* Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
|
||||
* Inflector::rules('plural', array(
|
||||
* 'rules' => array('/^(inflect)ors$/i' => '\1ables'),
|
||||
|
|
@ -289,11 +314,11 @@ class Inflector {
|
|||
* 'irregular' => array('red' => 'redlings')
|
||||
* ));
|
||||
* Inflector::rules('transliteration', array('/å/' => 'aa'));
|
||||
* }}}
|
||||
* ```
|
||||
*
|
||||
* @param string $type The type of inflection, either 'plural', 'singular' or 'transliteration'
|
||||
* @param array $rules Array of rules to be added.
|
||||
* @param boolean $reset If true, will unset default inflections for all
|
||||
* @param bool $reset If true, will unset default inflections for all
|
||||
* new rules that are being defined in $rules.
|
||||
* @return void
|
||||
*/
|
||||
|
|
@ -303,37 +328,36 @@ class Inflector {
|
|||
switch ($type) {
|
||||
case 'transliteration':
|
||||
if ($reset) {
|
||||
self::$_transliteration = $rules;
|
||||
static::$_transliteration = $rules;
|
||||
} else {
|
||||
self::$_transliteration = $rules + self::$_transliteration;
|
||||
static::$_transliteration = $rules + static::$_transliteration;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
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'];
|
||||
break;
|
||||
static::${$var}['rules'] = $rules + static::${$var}['rules'];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -345,37 +369,39 @@ class Inflector {
|
|||
* @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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -388,46 +414,48 @@ class Inflector {
|
|||
* @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'] = '(?:' . join('|', self::$_singular['merged']['uninflected']) . ')';
|
||||
self::$_singular['cacheIrregular'] = '(?:' . join('|', 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;
|
||||
}
|
||||
|
||||
|
|
@ -439,9 +467,9 @@ class Inflector {
|
|||
* @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;
|
||||
}
|
||||
|
|
@ -454,9 +482,10 @@ class Inflector {
|
|||
* @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;
|
||||
}
|
||||
|
|
@ -470,9 +499,13 @@ class Inflector {
|
|||
* @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;
|
||||
}
|
||||
|
|
@ -485,9 +518,9 @@ class Inflector {
|
|||
* @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;
|
||||
}
|
||||
|
|
@ -500,9 +533,9 @@ class Inflector {
|
|||
* @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;
|
||||
}
|
||||
|
|
@ -510,16 +543,16 @@ class Inflector {
|
|||
/**
|
||||
* Returns camelBacked version of an underscored string.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $string String to convert.
|
||||
* @return string in variable form
|
||||
* @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;
|
||||
}
|
||||
|
|
@ -537,12 +570,12 @@ class Inflector {
|
|||
$quotedReplacement = preg_quote($replacement, '/');
|
||||
|
||||
$merge = array(
|
||||
'/[^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
|
||||
'/\\s+/' => $replacement,
|
||||
'/[^\s\p{Zs}\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
|
||||
'/[\s\p{Zs}]+/mu' => $replacement,
|
||||
sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
|
||||
);
|
||||
|
||||
$map = self::$_transliteration + $merge;
|
||||
$map = static::$_transliteration + $merge;
|
||||
return preg_replace(array_keys($map), array_values($map), $string);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +1,25 @@
|
|||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deals with Collections of objects. Keeping registries of those objects,
|
||||
* Deals with Collections of objects. Keeping registries of those objects,
|
||||
* loading and constructing new objects and triggering callbacks. Each subclass needs
|
||||
* to implement its own load() functionality.
|
||||
*
|
||||
* All core subclasses of ObjectCollection by convention loaded objects are stored
|
||||
* in `$this->_loaded`. Enabled objects are stored in `$this->_enabled`. In addition
|
||||
* the all support an `enabled` option that controls the enabled/disabled state of the object
|
||||
* in `$this->_loaded`. Enabled objects are stored in `$this->_enabled`. In addition,
|
||||
* they all support an `enabled` option that controls the enabled/disabled state of the object
|
||||
* when loaded.
|
||||
*
|
||||
* @package Cake.Utility
|
||||
|
|
@ -55,13 +56,13 @@ abstract class ObjectCollection {
|
|||
*
|
||||
* @param string $name Name of object to load.
|
||||
* @param array $options Array of configuration options for the object to be constructed.
|
||||
* @return object the constructed object
|
||||
* @return CakeObject the constructed object
|
||||
*/
|
||||
abstract public function load($name, $options = array());
|
||||
|
||||
/**
|
||||
* Trigger a callback method on every object in the collection.
|
||||
* Used to trigger methods on objects in the collection. Will fire the methods in the
|
||||
* Used to trigger methods on objects in the collection. Will fire the methods in the
|
||||
* order they were attached.
|
||||
*
|
||||
* ### Options
|
||||
|
|
@ -70,7 +71,7 @@ abstract class ObjectCollection {
|
|||
* Can either be a scalar value, or an array of values to break on. Defaults to `false`.
|
||||
*
|
||||
* - `break` Set to true to enabled breaking. When a trigger is broken, the last returned value
|
||||
* will be returned. If used in combination with `collectReturn` the collected results will be returned.
|
||||
* will be returned. If used in combination with `collectReturn` the collected results will be returned.
|
||||
* Defaults to `false`.
|
||||
*
|
||||
* - `collectReturn` Set to true to collect the return of each object into an array.
|
||||
|
|
@ -81,8 +82,7 @@ abstract class ObjectCollection {
|
|||
* Any non-null value will modify the parameter index indicated.
|
||||
* Defaults to false.
|
||||
*
|
||||
*
|
||||
* @param string $callback|CakeEvent Method to fire on all the objects. Its assumed all the objects implement
|
||||
* @param string|CakeEvent $callback Method to fire on all the objects. Its assumed all the objects implement
|
||||
* the method you are calling. If an instance of CakeEvent is provided, then then Event name will parsed to
|
||||
* get the callback name. This is done by getting the last word after any dot in the event name
|
||||
* (eg. `Model.afterSave` event will trigger the `afterSave` callback)
|
||||
|
|
@ -112,27 +112,24 @@ abstract class ObjectCollection {
|
|||
$parts = explode('.', $event->name());
|
||||
$callback = array_pop($parts);
|
||||
}
|
||||
$options = array_merge(
|
||||
array(
|
||||
'break' => false,
|
||||
'breakOn' => false,
|
||||
'collectReturn' => false,
|
||||
'modParams' => false
|
||||
),
|
||||
$options
|
||||
$options += array(
|
||||
'break' => false,
|
||||
'breakOn' => false,
|
||||
'collectReturn' => false,
|
||||
'modParams' => false
|
||||
);
|
||||
$collected = array();
|
||||
$list = array_keys($this->_enabled);
|
||||
if ($options['modParams'] !== false && !isset($params[$options['modParams']])) {
|
||||
throw new CakeException(__d('cake_dev', 'Cannot use modParams with indexes that do not exist.'));
|
||||
}
|
||||
$result = null;
|
||||
foreach ($list as $name) {
|
||||
$result = call_user_func_array(array($this->_loaded[$name], $callback), compact('subject') + $params);
|
||||
if ($options['collectReturn'] === true) {
|
||||
$collected[] = $result;
|
||||
}
|
||||
if (
|
||||
$options['break'] && ($result === $options['breakOn'] ||
|
||||
if ($options['break'] && ($result === $options['breakOn'] ||
|
||||
(is_array($options['breakOn']) && in_array($result, $options['breakOn'], true)))
|
||||
) {
|
||||
return $result;
|
||||
|
|
@ -163,7 +160,7 @@ abstract class ObjectCollection {
|
|||
* Provide isset access to _loaded
|
||||
*
|
||||
* @param string $name Name of object being checked.
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name) {
|
||||
return isset($this->_loaded[$name]);
|
||||
|
|
@ -173,14 +170,18 @@ abstract class ObjectCollection {
|
|||
* Enables callbacks on an object or array of objects
|
||||
*
|
||||
* @param string|array $name CamelCased name of the object(s) to enable (string or array)
|
||||
* @param boolean Prioritize enabled list after enabling object(s)
|
||||
* @param bool $prioritize Prioritize enabled list after enabling object(s)
|
||||
* @return void
|
||||
*/
|
||||
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 = isset($this->_loaded[$object]->settings['priority']) ? $this->_loaded[$object]->settings['priority'] : $this->defaultPriority;
|
||||
$priority = $this->defaultPriority;
|
||||
if (isset($this->_loaded[$object]->settings['priority'])) {
|
||||
$priority = $this->_loaded[$object]->settings['priority'];
|
||||
}
|
||||
$this->_enabled[$object] = array($priority);
|
||||
$enabled = true;
|
||||
}
|
||||
|
|
@ -211,21 +212,22 @@ abstract class ObjectCollection {
|
|||
* @param string|array $name CamelCased name of the object(s) to enable (string or array)
|
||||
* If string the second param $priority is used else it should be an associative array
|
||||
* with keys as object names and values as priorities to set.
|
||||
* @param integer|null Integer priority to set or null for default
|
||||
* @param int|null $priority Integer priority to set or null for default
|
||||
* @return void
|
||||
*/
|
||||
public function setPriority($name, $priority = null) {
|
||||
if (is_string($name)) {
|
||||
$name = array($name => $priority);
|
||||
}
|
||||
foreach ($name as $obj => $prio) {
|
||||
if (isset($this->_loaded[$obj])) {
|
||||
if (is_null($prio)) {
|
||||
$prio = $this->defaultPriority;
|
||||
foreach ($name as $object => $objectPriority) {
|
||||
list(, $object) = pluginSplit($object);
|
||||
if (isset($this->_loaded[$object])) {
|
||||
if ($objectPriority === null) {
|
||||
$objectPriority = $this->defaultPriority;
|
||||
}
|
||||
$this->_loaded[$obj]->settings['priority'] = $prio;
|
||||
if (isset($this->_enabled[$obj])) {
|
||||
$this->_enabled[$obj] = array($prio);
|
||||
$this->_loaded[$object]->settings['priority'] = $objectPriority;
|
||||
if (isset($this->_enabled[$object])) {
|
||||
$this->_enabled[$object] = array($objectPriority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -233,7 +235,7 @@ abstract class ObjectCollection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Disables callbacks on a object or array of objects. Public object methods are still
|
||||
* Disables callbacks on an object or array of objects. Public object methods are still
|
||||
* callable as normal.
|
||||
*
|
||||
* @param string|array $name CamelCased name of the objects(s) to disable (string or array)
|
||||
|
|
@ -241,6 +243,7 @@ abstract class ObjectCollection {
|
|||
*/
|
||||
public function disable($name) {
|
||||
foreach ((array)$name as $object) {
|
||||
list(, $object) = pluginSplit($object);
|
||||
unset($this->_enabled[$object]);
|
||||
}
|
||||
}
|
||||
|
|
@ -248,13 +251,14 @@ abstract class ObjectCollection {
|
|||
/**
|
||||
* Gets the list of currently-enabled objects, or, the current status of a single objects
|
||||
*
|
||||
* @param string $name Optional. The name of the object to check the status of. If omitted,
|
||||
* @param string $name Optional. The name of the object to check the status of. If omitted,
|
||||
* returns an array of currently-enabled object
|
||||
* @return mixed If $name is specified, returns the boolean status of the corresponding object.
|
||||
* Otherwise, returns an array of all enabled objects.
|
||||
*/
|
||||
public function enabled($name = null) {
|
||||
if (!empty($name)) {
|
||||
list(, $name) = pluginSplit($name);
|
||||
return isset($this->_enabled[$name]);
|
||||
}
|
||||
return array_keys($this->_enabled);
|
||||
|
|
@ -263,13 +267,27 @@ abstract class ObjectCollection {
|
|||
/**
|
||||
* Gets the list of attached objects, or, whether the given object is attached
|
||||
*
|
||||
* @param string $name Optional. The name of the behavior to check the status of. If omitted,
|
||||
* returns an array of currently-attached behaviors
|
||||
* @return mixed If $name is specified, returns the boolean status of the corresponding behavior.
|
||||
* Otherwise, returns an array of all attached behaviors.
|
||||
* @param string $name Optional. The name of the object to check the status of. If omitted,
|
||||
* returns an array of currently-attached objects
|
||||
* @return mixed If $name is specified, returns the boolean status of the corresponding object.
|
||||
* Otherwise, returns an array of all attached objects.
|
||||
* @deprecated 3.0.0 Will be removed in 3.0. Use loaded instead.
|
||||
*/
|
||||
public function attached($name = null) {
|
||||
return $this->loaded($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of loaded objects, or, whether the given object is loaded
|
||||
*
|
||||
* @param string $name Optional. The name of the object to check the status of. If omitted,
|
||||
* returns an array of currently-loaded objects
|
||||
* @return mixed If $name is specified, returns the boolean status of the corresponding object.
|
||||
* Otherwise, returns an array of all loaded objects.
|
||||
*/
|
||||
public function loaded($name = null) {
|
||||
if (!empty($name)) {
|
||||
list(, $name) = pluginSplit($name);
|
||||
return isset($this->_loaded[$name]);
|
||||
}
|
||||
return array_keys($this->_loaded);
|
||||
|
|
@ -282,21 +300,20 @@ abstract class ObjectCollection {
|
|||
* @return void
|
||||
*/
|
||||
public function unload($name) {
|
||||
list($plugin, $name) = pluginSplit($name);
|
||||
unset($this->_loaded[$name]);
|
||||
unset($this->_enabled[$name]);
|
||||
list(, $name) = pluginSplit($name);
|
||||
unset($this->_loaded[$name], $this->_enabled[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or overwrites an instantiated object to the collection
|
||||
*
|
||||
* @param string $name Name of the object
|
||||
* @param Object $object The object to use
|
||||
* @param CakeObject $object The object to use
|
||||
* @return array Loaded objects
|
||||
*/
|
||||
public function set($name = null, $object = null) {
|
||||
if (!empty($name) && !empty($object)) {
|
||||
list($plugin, $name) = pluginSplit($name);
|
||||
list(, $name) = pluginSplit($name);
|
||||
$this->_loaded[$name] = $object;
|
||||
}
|
||||
return $this->_loaded;
|
||||
|
|
@ -317,7 +334,7 @@ abstract class ObjectCollection {
|
|||
$options = (array)$objectName;
|
||||
$objectName = $i;
|
||||
}
|
||||
list($plugin, $name) = pluginSplit($objectName);
|
||||
list(, $name) = pluginSplit($objectName);
|
||||
$normal[$name] = array('class' => $objectName, 'settings' => $options);
|
||||
}
|
||||
return $normal;
|
||||
|
|
|
|||
|
|
@ -4,22 +4,21 @@
|
|||
*
|
||||
* Helpful methods to make unsafe strings usable.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Utility
|
||||
* @since CakePHP(tm) v 0.10.0.1076
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::import('Model', 'ConnectionManager');
|
||||
App::uses('ConnectionManager', 'Model');
|
||||
|
||||
/**
|
||||
* Data Sanitization.
|
||||
|
|
@ -28,6 +27,7 @@ App::import('Model', 'ConnectionManager');
|
|||
* and all of the above on arrays.
|
||||
*
|
||||
* @package Cake.Utility
|
||||
* @deprecated 3.0.0 Deprecated since version 2.4
|
||||
*/
|
||||
class Sanitize {
|
||||
|
||||
|
|
@ -46,14 +46,15 @@ class Sanitize {
|
|||
}
|
||||
}
|
||||
|
||||
if (is_array($string)) {
|
||||
$cleaned = array();
|
||||
foreach ($string as $key => $clean) {
|
||||
$cleaned[$key] = preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $clean);
|
||||
}
|
||||
} else {
|
||||
$cleaned = preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $string);
|
||||
if (!is_array($string)) {
|
||||
return preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $string);
|
||||
}
|
||||
|
||||
$cleaned = array();
|
||||
foreach ($string as $key => $clean) {
|
||||
$cleaned[$key] = preg_replace("/[^{$allow}a-zA-Z0-9]/", '', $clean);
|
||||
}
|
||||
|
||||
return $cleaned;
|
||||
}
|
||||
|
||||
|
|
@ -65,19 +66,17 @@ class Sanitize {
|
|||
* @return string SQL safe string
|
||||
*/
|
||||
public static function escape($string, $connection = 'default') {
|
||||
$db = ConnectionManager::getDataSource($connection);
|
||||
if (is_numeric($string) || $string === null || is_bool($string)) {
|
||||
return $string;
|
||||
}
|
||||
$db = ConnectionManager::getDataSource($connection);
|
||||
$string = $db->value($string, 'string');
|
||||
if ($string[0] === 'N') {
|
||||
$string = substr($string, 2);
|
||||
} else {
|
||||
$string = substr($string, 1);
|
||||
$start = 1;
|
||||
if ($string{0} === 'N') {
|
||||
$start = 2;
|
||||
}
|
||||
|
||||
$string = substr($string, 0, -1);
|
||||
return $string;
|
||||
return substr(substr($string, $start), 0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,7 +90,7 @@ class Sanitize {
|
|||
* - remove (boolean) if true strips all HTML tags before encoding
|
||||
* - charset (string) the charset used to encode the string
|
||||
* - quotes (int) see http://php.net/manual/en/function.htmlentities.php
|
||||
* - double (boolean) doube encode html entities
|
||||
* - double (boolean) double encode html entities
|
||||
*
|
||||
* @param string $string String from where to strip tags
|
||||
* @param array $options Array of options to use.
|
||||
|
|
@ -105,14 +104,14 @@ class Sanitize {
|
|||
$defaultCharset = 'UTF-8';
|
||||
}
|
||||
}
|
||||
$default = array(
|
||||
$defaults = array(
|
||||
'remove' => false,
|
||||
'charset' => $defaultCharset,
|
||||
'quotes' => ENT_QUOTES,
|
||||
'double' => true
|
||||
);
|
||||
|
||||
$options = array_merge($default, $options);
|
||||
$options += $defaults;
|
||||
|
||||
if ($options['remove']) {
|
||||
$string = strip_tags($string);
|
||||
|
|
@ -128,8 +127,7 @@ class Sanitize {
|
|||
* @return string whitespace sanitized string
|
||||
*/
|
||||
public static function stripWhitespace($str) {
|
||||
$r = preg_replace('/[\n\r\t]+/', '', $str);
|
||||
return preg_replace('/\s{2,}/u', ' ', $r);
|
||||
return preg_replace('/\s{2,}/u', ' ', preg_replace('/[\n\r\t]+/', '', $str));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -139,20 +137,29 @@ class Sanitize {
|
|||
* @return string Sting with images stripped.
|
||||
*/
|
||||
public static function stripImages($str) {
|
||||
$str = preg_replace('/(<a[^>]*>)(<img[^>]+alt=")([^"]*)("[^>]*>)(<\/a>)/i', '$1$3$5<br />', $str);
|
||||
$str = preg_replace('/(<img[^>]+alt=")([^"]*)("[^>]*>)/i', '$2<br />', $str);
|
||||
$str = preg_replace('/<img[^>]*>/i', '', $str);
|
||||
return $str;
|
||||
$preg = array(
|
||||
'/(<a[^>]*>)(<img[^>]+alt=")([^"]*)("[^>]*>)(<\/a>)/i' => '$1$3$5<br />',
|
||||
'/(<img[^>]+alt=")([^"]*)("[^>]*>)/i' => '$2<br />',
|
||||
'/<img[^>]*>/i' => ''
|
||||
);
|
||||
|
||||
return preg_replace(array_keys($preg), array_values($preg), $str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips scripts and stylesheets from output
|
||||
*
|
||||
* @param string $str String to sanitize
|
||||
* @return string String with <script>, <style>, <link>, <img> elements removed.
|
||||
* @return string String with <link>, <img>, <script>, <style> elements and html comments removed.
|
||||
*/
|
||||
public static function stripScripts($str) {
|
||||
return preg_replace('/(<link[^>]+rel="[^"]*stylesheet"[^>]*>|<img[^>]*>|style="[^"]*")|<script[^>]*>.*?<\/script>|<style[^>]*>.*?<\/style>|<!--.*?-->/is', '', $str);
|
||||
$regex =
|
||||
'/(<link[^>]+rel="[^"]*stylesheet"[^>]*>|' .
|
||||
'<img[^>]*>|style="[^"]*")|' .
|
||||
'<script[^>]*>.*?<\/script>|' .
|
||||
'<style[^>]*>.*?<\/style>|' .
|
||||
'<!--.*?-->/is';
|
||||
return preg_replace($regex, '', $str);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -162,10 +169,11 @@ class Sanitize {
|
|||
* @return string sanitized string
|
||||
*/
|
||||
public static function stripAll($str) {
|
||||
$str = Sanitize::stripWhitespace($str);
|
||||
$str = Sanitize::stripImages($str);
|
||||
$str = Sanitize::stripScripts($str);
|
||||
return $str;
|
||||
return Sanitize::stripScripts(
|
||||
Sanitize::stripImages(
|
||||
Sanitize::stripWhitespace($str)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -176,7 +184,7 @@ class Sanitize {
|
|||
*
|
||||
* Will remove all `<b>`, `<p>`, and `<div>` tags from the $dirty string.
|
||||
*
|
||||
* @param string $str,... String to sanitize
|
||||
* @param string $str String to sanitize.
|
||||
* @return string sanitized String
|
||||
*/
|
||||
public static function stripTags($str) {
|
||||
|
|
@ -212,13 +220,11 @@ class Sanitize {
|
|||
return $data;
|
||||
}
|
||||
|
||||
if (is_string($options)) {
|
||||
if (!is_array($options)) {
|
||||
$options = array('connection' => $options);
|
||||
} elseif (!is_array($options)) {
|
||||
$options = array();
|
||||
}
|
||||
|
||||
$options = array_merge(array(
|
||||
$options += array(
|
||||
'connection' => 'default',
|
||||
'odd_spaces' => true,
|
||||
'remove_html' => false,
|
||||
|
|
@ -228,37 +234,36 @@ class Sanitize {
|
|||
'unicode' => true,
|
||||
'escape' => true,
|
||||
'backslash' => true
|
||||
), $options);
|
||||
);
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => $val) {
|
||||
$data[$key] = Sanitize::clean($val, $options);
|
||||
}
|
||||
return $data;
|
||||
} else {
|
||||
if ($options['odd_spaces']) {
|
||||
$data = str_replace(chr(0xCA), '', $data);
|
||||
}
|
||||
if ($options['encode']) {
|
||||
$data = Sanitize::html($data, array('remove' => $options['remove_html']));
|
||||
}
|
||||
if ($options['dollar']) {
|
||||
$data = str_replace("\\\$", "$", $data);
|
||||
}
|
||||
if ($options['carriage']) {
|
||||
$data = str_replace("\r", "", $data);
|
||||
}
|
||||
if ($options['unicode']) {
|
||||
$data = preg_replace("/&#([0-9]+);/s", "&#\\1;", $data);
|
||||
}
|
||||
if ($options['escape']) {
|
||||
$data = Sanitize::escape($data, $options['connection']);
|
||||
}
|
||||
if ($options['backslash']) {
|
||||
$data = preg_replace("/\\\(?!&#|\?#)/", "\\", $data);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['odd_spaces']) {
|
||||
$data = str_replace(chr(0xCA), '', $data);
|
||||
}
|
||||
if ($options['encode']) {
|
||||
$data = Sanitize::html($data, array('remove' => $options['remove_html']));
|
||||
}
|
||||
if ($options['dollar']) {
|
||||
$data = str_replace("\\\$", "$", $data);
|
||||
}
|
||||
if ($options['carriage']) {
|
||||
$data = str_replace("\r", "", $data);
|
||||
}
|
||||
if ($options['unicode']) {
|
||||
$data = preg_replace("/&#([0-9]+);/s", "&#\\1;", $data);
|
||||
}
|
||||
if ($options['escape']) {
|
||||
$data = Sanitize::escape($data, $options['connection']);
|
||||
}
|
||||
if ($options['backslash']) {
|
||||
$data = preg_replace("/\\\(?!&#|\?#)/", "\\", $data);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,22 +2,21 @@
|
|||
/**
|
||||
* Core Security
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Utility
|
||||
* @since CakePHP(tm) v .0.10.0.1233
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @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
|
||||
|
|
@ -33,10 +32,18 @@ class Security {
|
|||
*/
|
||||
public static $hashType = null;
|
||||
|
||||
/**
|
||||
* Default cost
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $hashCost = '10';
|
||||
|
||||
/**
|
||||
* Get allowed minutes of inactivity based on security level.
|
||||
*
|
||||
* @return integer Allowed inactivity in minutes
|
||||
* @deprecated 3.0.0 Exists for backwards compatibility only, not used by the core
|
||||
* @return int Allowed inactivity in minutes
|
||||
*/
|
||||
public static function inactiveMins() {
|
||||
switch (Configure::read('Security.level')) {
|
||||
|
|
@ -54,54 +61,74 @@ class Security {
|
|||
* Generate authorization hash.
|
||||
*
|
||||
* @return string Hash
|
||||
* @deprecated 2.8.1 This method was removed in 3.0.0
|
||||
*/
|
||||
public static function generateAuthKey() {
|
||||
return Security::hash(String::uuid());
|
||||
return Security::hash(CakeText::uuid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate authorization hash.
|
||||
*
|
||||
* @param string $authKey Authorization hash
|
||||
* @return boolean Success
|
||||
* @return bool Success
|
||||
* @deprecated 2.8.1 This method was removed in 3.0.0
|
||||
*/
|
||||
public static function validateAuthKey($authKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hash from string using given method.
|
||||
* Fallback on next available method.
|
||||
* Create a hash from string using given method or fallback on next available method.
|
||||
*
|
||||
* #### Using Blowfish
|
||||
*
|
||||
* - Creating Hashes: *Do not supply a salt*. Cake handles salt creation for
|
||||
* you ensuring that each hashed password will have a *unique* salt.
|
||||
* - Comparing Hashes: Simply pass the originally hashed password as the salt.
|
||||
* The salt is prepended to the hash and php handles the parsing automagically.
|
||||
* For convenience the `BlowfishPasswordHasher` class is available for use with
|
||||
* the AuthComponent.
|
||||
* - Do NOT use a constant salt for blowfish!
|
||||
*
|
||||
* Creating a blowfish/bcrypt hash:
|
||||
*
|
||||
* ```
|
||||
* $hash = Security::hash($password, 'blowfish');
|
||||
* ```
|
||||
*
|
||||
* @param string $string String to hash
|
||||
* @param string $type Method to use (sha1/sha256/md5)
|
||||
* @param boolean $salt If true, automatically appends the application's salt
|
||||
* value to $string (Security.salt)
|
||||
* @param string $type Method to use (sha1/sha256/md5/blowfish)
|
||||
* @param mixed $salt If true, automatically prepends the application's salt
|
||||
* value to $string (Security.salt). If you are using blowfish the salt
|
||||
* must be false or a previously generated salt.
|
||||
* @return string Hash
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/security.html#Security::hash
|
||||
*/
|
||||
public static function hash($string, $type = null, $salt = false) {
|
||||
if ($salt) {
|
||||
if (is_string($salt)) {
|
||||
$string = $salt . $string;
|
||||
} else {
|
||||
$string = Configure::read('Security.salt') . $string;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($type)) {
|
||||
$type = self::$hashType;
|
||||
$type = static::$hashType;
|
||||
}
|
||||
$type = strtolower($type);
|
||||
|
||||
if ($type == 'sha1' || $type == null) {
|
||||
if ($type === 'blowfish') {
|
||||
return static::_crypt($string, $salt);
|
||||
}
|
||||
if ($salt) {
|
||||
if (!is_string($salt)) {
|
||||
$salt = Configure::read('Security.salt');
|
||||
}
|
||||
$string = $salt . $string;
|
||||
}
|
||||
|
||||
if (!$type || $type === 'sha1') {
|
||||
if (function_exists('sha1')) {
|
||||
$return = sha1($string);
|
||||
return $return;
|
||||
return sha1($string);
|
||||
}
|
||||
$type = 'sha256';
|
||||
}
|
||||
|
||||
if ($type == 'sha256' && function_exists('mhash')) {
|
||||
if ($type === 'sha256' && function_exists('mhash')) {
|
||||
return bin2hex(mhash(MHASH_SHA256, $string));
|
||||
}
|
||||
|
||||
|
|
@ -112,31 +139,88 @@ class Security {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the default hash method for the Security object. This affects all objects using
|
||||
* Sets the default hash method for the Security object. This affects all objects using
|
||||
* Security::hash().
|
||||
*
|
||||
* @param string $hash Method to use (sha1/sha256/md5)
|
||||
* @param string $hash Method to use (sha1/sha256/md5/blowfish)
|
||||
* @return void
|
||||
* @see Security::hash()
|
||||
*/
|
||||
public static function setHash($hash) {
|
||||
self::$hashType = $hash;
|
||||
static::$hashType = $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts/Decrypts a text using the given key.
|
||||
* Sets the cost for they blowfish hash method.
|
||||
*
|
||||
* @param int $cost Valid values are 4-31
|
||||
* @return void
|
||||
*/
|
||||
public static function setCost($cost) {
|
||||
if ($cost < 4 || $cost > 31) {
|
||||
trigger_error(__d(
|
||||
'cake_dev',
|
||||
'Invalid value, cost must be between %s and %s',
|
||||
array(4, 31)
|
||||
), E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
static::$hashCost = $cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get random bytes from a secure source.
|
||||
*
|
||||
* This method will fall back to an insecure source and trigger a warning,
|
||||
* if it cannot find a secure source of random data.
|
||||
*
|
||||
* @param int $length The number of bytes you want.
|
||||
* @return string Random bytes in binary.
|
||||
*/
|
||||
public static function randomBytes($length) {
|
||||
if (function_exists('random_bytes')) {
|
||||
return random_bytes($length);
|
||||
}
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
return openssl_random_pseudo_bytes($length);
|
||||
}
|
||||
trigger_error(
|
||||
'You do not have a safe source of random data available. ' .
|
||||
'Install either the openssl extension, or paragonie/random_compat. ' .
|
||||
'Falling back to an insecure random source.',
|
||||
E_USER_WARNING
|
||||
);
|
||||
$bytes = '';
|
||||
$byteLength = 0;
|
||||
while ($byteLength < $length) {
|
||||
$bytes .= static::hash(CakeText::uuid() . uniqid(mt_rand(), true), 'sha512', true);
|
||||
$byteLength = strlen($bytes);
|
||||
}
|
||||
return substr($bytes, 0, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs $text through a XOR cipher.
|
||||
*
|
||||
* *Note* This is not a cryptographically strong method and should not be used
|
||||
* for sensitive data. Additionally this method does *not* work in environments
|
||||
* where suhosin is enabled.
|
||||
*
|
||||
* Instead you should use Security::rijndael() when you need strong
|
||||
* encryption.
|
||||
*
|
||||
* @param string $text Encrypted string to decrypt, normal string to encrypt
|
||||
* @param string $key Key to use
|
||||
* @return string Encrypted/Decrypted string
|
||||
* @deprecated 3.0.0 Will be removed in 3.0.
|
||||
*/
|
||||
public static function cipher($text, $key) {
|
||||
if (empty($key)) {
|
||||
trigger_error(__d('cake_dev', 'You cannot use an empty key for Security::cipher()'), E_USER_WARNING);
|
||||
trigger_error(__d('cake_dev', 'You cannot use an empty key for %s', 'Security::cipher()'), E_USER_WARNING);
|
||||
return '';
|
||||
}
|
||||
|
||||
srand(Configure::read('Security.cipherSeed'));
|
||||
srand((int)Configure::read('Security.cipherSeed'));
|
||||
$out = '';
|
||||
$keyLength = strlen($key);
|
||||
for ($i = 0, $textLength = strlen($text); $i < $textLength; $i++) {
|
||||
|
|
@ -154,14 +238,18 @@ class Security {
|
|||
/**
|
||||
* Encrypts/Decrypts a text using the given key using rijndael method.
|
||||
*
|
||||
* Prior to 2.3.1, a fixed initialization vector was used. This was not
|
||||
* secure. This method now uses a random iv, and will silently upgrade values when
|
||||
* they are re-encrypted.
|
||||
*
|
||||
* @param string $text Encrypted string to decrypt, normal string to encrypt
|
||||
* @param string $key Key to use
|
||||
* @param string $key Key to use as the encryption key for encrypted data.
|
||||
* @param string $operation Operation to perform, encrypt or decrypt
|
||||
* @return string Encrypted/Descrypted string
|
||||
* @return string Encrypted/Decrypted string
|
||||
*/
|
||||
public static function rijndael($text, $key, $operation) {
|
||||
if (empty($key)) {
|
||||
trigger_error(__d('cake_dev', 'You cannot use an empty key for Security::rijndael()'), E_USER_WARNING);
|
||||
trigger_error(__d('cake_dev', 'You cannot use an empty key for %s', 'Security::rijndael()'), E_USER_WARNING);
|
||||
return '';
|
||||
}
|
||||
if (empty($operation) || !in_array($operation, array('encrypt', 'decrypt'))) {
|
||||
|
|
@ -172,17 +260,158 @@ class Security {
|
|||
trigger_error(__d('cake_dev', 'You must use a key larger than 32 bytes for Security::rijndael()'), E_USER_WARNING);
|
||||
return '';
|
||||
}
|
||||
$algorithm = 'rijndael-256';
|
||||
$mode = 'cbc';
|
||||
$algorithm = MCRYPT_RIJNDAEL_256;
|
||||
$mode = MCRYPT_MODE_CBC;
|
||||
$ivSize = mcrypt_get_iv_size($algorithm, $mode);
|
||||
|
||||
$cryptKey = substr($key, 0, 32);
|
||||
$iv = substr($key, strlen($key) - 32, 32);
|
||||
$out = '';
|
||||
|
||||
if ($operation === 'encrypt') {
|
||||
$out .= mcrypt_encrypt($algorithm, $cryptKey, $text, $mode, $iv);
|
||||
} elseif ($operation === 'decrypt') {
|
||||
$out .= rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");
|
||||
$iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
|
||||
return $iv . '$$' . mcrypt_encrypt($algorithm, $cryptKey, $text, $mode, $iv);
|
||||
}
|
||||
return $out;
|
||||
// Backwards compatible decrypt with fixed iv
|
||||
if (substr($text, $ivSize, 2) !== '$$') {
|
||||
$iv = substr($key, strlen($key) - 32, 32);
|
||||
return rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");
|
||||
}
|
||||
$iv = substr($text, 0, $ivSize);
|
||||
$text = substr($text, $ivSize + 2);
|
||||
return rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a pseudo random salt suitable for use with php's crypt() function.
|
||||
* The salt length should not exceed 27. The salt will be composed of
|
||||
* [./0-9A-Za-z]{$length}.
|
||||
*
|
||||
* @param int $length The length of the returned salt
|
||||
* @return string The generated salt
|
||||
*/
|
||||
protected static function _salt($length = 22) {
|
||||
$salt = str_replace(
|
||||
array('+', '='),
|
||||
'.',
|
||||
base64_encode(sha1(uniqid(Configure::read('Security.salt'), true), true))
|
||||
);
|
||||
return substr($salt, 0, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* One way encryption using php's crypt() function. To use blowfish hashing see ``Security::hash()``
|
||||
*
|
||||
* @param string $password The string to be encrypted.
|
||||
* @param mixed $salt false to generate a new salt or an existing salt.
|
||||
* @return string The hashed string or an empty string on error.
|
||||
*/
|
||||
protected static function _crypt($password, $salt = false) {
|
||||
if ($salt === false || $salt === null || $salt === '') {
|
||||
$salt = static::_salt(22);
|
||||
$salt = vsprintf('$2a$%02d$%s', array(static::$hashCost, $salt));
|
||||
}
|
||||
|
||||
$invalidCipher = (
|
||||
strpos($salt, '$2y$') !== 0 &&
|
||||
strpos($salt, '$2x$') !== 0 &&
|
||||
strpos($salt, '$2a$') !== 0
|
||||
);
|
||||
if ($salt === true || $invalidCipher || strlen($salt) < 29) {
|
||||
trigger_error(__d(
|
||||
'cake_dev',
|
||||
'Invalid salt: %s for %s Please visit http://www.php.net/crypt and read the appropriate section for building %s salts.',
|
||||
array($salt, 'blowfish', 'blowfish')
|
||||
), E_USER_WARNING);
|
||||
return '';
|
||||
}
|
||||
return crypt($password, $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a value using AES-256.
|
||||
*
|
||||
* *Caveat* You cannot properly encrypt/decrypt data with trailing null bytes.
|
||||
* Any trailing null bytes will be removed on decryption due to how PHP pads messages
|
||||
* with nulls prior to encryption.
|
||||
*
|
||||
* @param string $plain The value to encrypt.
|
||||
* @param string $key The 256 bit/32 byte key to use as a cipher key.
|
||||
* @param string $hmacSalt The salt to use for the HMAC process. Leave null to use Security.salt.
|
||||
* @return string Encrypted data.
|
||||
* @throws CakeException On invalid data or key.
|
||||
*/
|
||||
public static function encrypt($plain, $key, $hmacSalt = null) {
|
||||
static::_checkKey($key, 'encrypt()');
|
||||
|
||||
if ($hmacSalt === null) {
|
||||
$hmacSalt = Configure::read('Security.salt');
|
||||
}
|
||||
|
||||
// Generate the encryption and hmac key.
|
||||
$key = substr(hash('sha256', $key . $hmacSalt), 0, 32);
|
||||
|
||||
$algorithm = MCRYPT_RIJNDAEL_128;
|
||||
$mode = MCRYPT_MODE_CBC;
|
||||
|
||||
$ivSize = mcrypt_get_iv_size($algorithm, $mode);
|
||||
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
|
||||
$ciphertext = $iv . mcrypt_encrypt($algorithm, $key, $plain, $mode, $iv);
|
||||
$hmac = hash_hmac('sha256', $ciphertext, $key);
|
||||
return $hmac . $ciphertext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the encryption key for proper length.
|
||||
*
|
||||
* @param string $key Key to check.
|
||||
* @param string $method The method the key is being checked for.
|
||||
* @return void
|
||||
* @throws CakeException When key length is not 256 bit/32 bytes
|
||||
*/
|
||||
protected static function _checkKey($key, $method) {
|
||||
if (strlen($key) < 32) {
|
||||
throw new CakeException(__d('cake_dev', 'Invalid key for %s, key must be at least 256 bits (32 bytes) long.', $method));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a value using AES-256.
|
||||
*
|
||||
* @param string $cipher The ciphertext to decrypt.
|
||||
* @param string $key The 256 bit/32 byte key to use as a cipher key.
|
||||
* @param string $hmacSalt The salt to use for the HMAC process. Leave null to use Security.salt.
|
||||
* @return string Decrypted data. Any trailing null bytes will be removed.
|
||||
* @throws CakeException On invalid data or key.
|
||||
*/
|
||||
public static function decrypt($cipher, $key, $hmacSalt = null) {
|
||||
static::_checkKey($key, 'decrypt()');
|
||||
if (empty($cipher)) {
|
||||
throw new CakeException(__d('cake_dev', 'The data to decrypt cannot be empty.'));
|
||||
}
|
||||
if ($hmacSalt === null) {
|
||||
$hmacSalt = Configure::read('Security.salt');
|
||||
}
|
||||
|
||||
// Generate the encryption and hmac key.
|
||||
$key = substr(hash('sha256', $key . $hmacSalt), 0, 32);
|
||||
|
||||
// Split out hmac for comparison
|
||||
$macSize = 64;
|
||||
$hmac = substr($cipher, 0, $macSize);
|
||||
$cipher = substr($cipher, $macSize);
|
||||
|
||||
$compareHmac = hash_hmac('sha256', $cipher, $key);
|
||||
if ($hmac !== $compareHmac) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$algorithm = MCRYPT_RIJNDAEL_128;
|
||||
$mode = MCRYPT_MODE_CBC;
|
||||
$ivSize = mcrypt_get_iv_size($algorithm, $mode);
|
||||
|
||||
$iv = substr($cipher, 0, $ivSize);
|
||||
$cipher = substr($cipher, $ivSize);
|
||||
$plain = mcrypt_decrypt($algorithm, $key, $cipher, $mode, $iv);
|
||||
return rtrim($plain, "\0");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,28 +2,28 @@
|
|||
/**
|
||||
* Library of array functions for Cake.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @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
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('String', 'Utility');
|
||||
App::uses('CakeText', 'Utility');
|
||||
App::uses('Hash', 'Utility');
|
||||
|
||||
/**
|
||||
* Class used for manipulation of arrays.
|
||||
*
|
||||
* @package Cake.Utility
|
||||
* @deprecated 3.0.0 Will be removed in 3.0. Use Hash instead.
|
||||
*/
|
||||
class Set {
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ class Set {
|
|||
* to the two is that if an array key contains another array then the function behaves recursive (unlike array_merge)
|
||||
* but does not do if for keys containing strings (unlike array_merge_recursive).
|
||||
*
|
||||
* Since this method emulates `array_merge`, it will re-order numeric keys. When combined with out of
|
||||
* Since this method emulates `array_merge`, it will re-order numeric keys. When combined with out of
|
||||
* order numeric keys containing arrays, results can be lossy.
|
||||
*
|
||||
* Note: This function will work with an unlimited amount of arguments and typecasts non-array
|
||||
|
|
@ -97,7 +97,7 @@ class Set {
|
|||
*
|
||||
* @param string $class A class name of the type of object to map to
|
||||
* @param string $tmp A temporary class name used as $class if $class is an array
|
||||
* @return object Hierarchical object
|
||||
* @return object|null Hierarchical object
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::map
|
||||
*/
|
||||
public static function map($class = 'stdClass', $tmp = 'stdClass') {
|
||||
|
|
@ -120,9 +120,9 @@ class Set {
|
|||
* returned object (recursively). If $key is numeric will maintain array
|
||||
* structure
|
||||
*
|
||||
* @param array $array Array to map
|
||||
* @param array &$array Array to map
|
||||
* @param string $class Class name
|
||||
* @param boolean $primary whether to assign first array key as the _name_
|
||||
* @param bool $primary whether to assign first array key as the _name_
|
||||
* @return mixed Mapped object
|
||||
*/
|
||||
protected static function _map(&$array, $class, $primary = false) {
|
||||
|
|
@ -185,8 +185,8 @@ class Set {
|
|||
/**
|
||||
* Checks to see if all the values in the array are numeric
|
||||
*
|
||||
* @param array $array The array to check. If null, the value of the current Set object
|
||||
* @return boolean true if values are numeric, false otherwise
|
||||
* @param array $array The array to check. If null, the value of the current Set object
|
||||
* @return bool true if values are numeric, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::numeric
|
||||
*/
|
||||
public static function numeric($array = null) {
|
||||
|
|
@ -203,7 +203,7 @@ class Set {
|
|||
*
|
||||
* $list defaults to 0 = no 1 = yes if param is not passed
|
||||
*
|
||||
* @param array $select Key in $list to return
|
||||
* @param string $select Key in $list to return
|
||||
* @param array|string $list can be an array or a comma-separated list.
|
||||
* @return string the value of the array key or null if no match
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::enum
|
||||
|
|
@ -228,7 +228,7 @@ class Set {
|
|||
* @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 @@ class Set {
|
|||
$count = count($keys);
|
||||
|
||||
if (!$count) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
|
|
@ -308,7 +308,7 @@ class Set {
|
|||
* @param string $path An absolute XPath 2.0 path
|
||||
* @param array $data An array of data to extract from
|
||||
* @param array $options Currently only supports 'flatten' which can be disabled for higher XPath-ness
|
||||
* @return array An array of matched items
|
||||
* @return mixed An array of matched items or the content of a single selected item or null in any of these cases: $path or $data are null, no items found.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::extract
|
||||
*/
|
||||
public static function extract($path, $data = null, $options = array()) {
|
||||
|
|
@ -327,7 +327,7 @@ class Set {
|
|||
return $data;
|
||||
}
|
||||
$contexts = $data;
|
||||
$options = array_merge(array('flatten' => true), $options);
|
||||
$options += array('flatten' => true);
|
||||
if (!isset($contexts[0])) {
|
||||
$current = current($data);
|
||||
if ((is_array($current) && count($data) < 1) || !is_array($current) || !Set::numeric(array_keys($data))) {
|
||||
|
|
@ -349,7 +349,7 @@ class Set {
|
|||
$context = array('trace' => array(null), 'item' => $context, 'key' => $key);
|
||||
}
|
||||
if ($token === '..') {
|
||||
if (count($context['trace']) == 1) {
|
||||
if (count($context['trace']) === 1) {
|
||||
$context['trace'][] = $context['key'];
|
||||
}
|
||||
$parent = implode('/', $context['trace']) . '/.';
|
||||
|
|
@ -373,7 +373,7 @@ class Set {
|
|||
);
|
||||
} elseif (is_array($context['item'])
|
||||
&& array_key_exists($token, $context['item'])
|
||||
&& !(strval($key) === strval($token) && count($tokens) == 1 && $tokens[0] === '.')) {
|
||||
&& !(strval($key) === strval($token) && count($tokens) === 1 && $tokens[0] === '.')) {
|
||||
$items = $context['item'][$token];
|
||||
if (!is_array($items)) {
|
||||
$items = array($items);
|
||||
|
|
@ -456,10 +456,10 @@ class Set {
|
|||
* This function can be used to see if a single item or a given xpath match certain conditions.
|
||||
*
|
||||
* @param string|array $conditions An array of condition strings or an XPath expression
|
||||
* @param array $data An array of data to execute the match on
|
||||
* @param integer $i Optional: The 'nth'-number of the item being matched.
|
||||
* @param integer $length
|
||||
* @return boolean
|
||||
* @param array $data An array of data to execute the match on
|
||||
* @param int $i Optional: The 'nth'-number of the item being matched.
|
||||
* @param int $length Length.
|
||||
* @return bool
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::matches
|
||||
*/
|
||||
public static function matches($conditions, $data = array(), $i = null, $length = null) {
|
||||
|
|
@ -467,7 +467,7 @@ class Set {
|
|||
return true;
|
||||
}
|
||||
if (is_string($conditions)) {
|
||||
return !!Set::extract($conditions, $data);
|
||||
return (bool)Set::extract($conditions, $data);
|
||||
}
|
||||
foreach ($conditions as $condition) {
|
||||
if ($condition === ':last') {
|
||||
|
|
@ -533,7 +533,7 @@ class Set {
|
|||
*
|
||||
* @param array $data Array from where to extract
|
||||
* @param string|array $path As an array, or as a dot-separated string.
|
||||
* @return array|null Extracted data or null when $data or $path are empty.
|
||||
* @return mixed An array of matched items or the content of a single selected item or null in any of these cases: $path or $data are null, no items found.
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::classicExtract
|
||||
*/
|
||||
public static function classicExtract($data, $path = null) {
|
||||
|
|
@ -549,7 +549,7 @@ class Set {
|
|||
return null;
|
||||
}
|
||||
if (is_string($path) && strpos($path, '{') !== false) {
|
||||
$path = String::tokenize($path, '.', '{', '}');
|
||||
$path = CakeText::tokenize($path, '.', '{', '}');
|
||||
} elseif (is_string($path)) {
|
||||
$path = explode('.', $path);
|
||||
}
|
||||
|
|
@ -560,7 +560,7 @@ class Set {
|
|||
}
|
||||
|
||||
foreach ($path as $i => $key) {
|
||||
if (is_numeric($key) && intval($key) > 0 || $key === '0') {
|
||||
if (is_numeric($key) && (int)$key > 0 || $key === '0') {
|
||||
if (isset($data[$key])) {
|
||||
$data = $data[$key];
|
||||
} else {
|
||||
|
|
@ -590,7 +590,7 @@ class Set {
|
|||
}
|
||||
}
|
||||
return $tmp;
|
||||
} elseif (false !== strpos($key, '{') && false !== strpos($key, '}')) {
|
||||
} elseif (strpos($key, '{') !== false && strpos($key, '}') !== false) {
|
||||
$pattern = substr($key, 1, -1);
|
||||
|
||||
foreach ($data as $j => $val) {
|
||||
|
|
@ -645,7 +645,7 @@ class Set {
|
|||
*
|
||||
* @param string|array $data Data to check on
|
||||
* @param string|array $path A dot-separated string.
|
||||
* @return boolean true if path is found, false otherwise
|
||||
* @return bool true if path is found, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::check
|
||||
*/
|
||||
public static function check($data, $path = null) {
|
||||
|
|
@ -657,8 +657,8 @@ class Set {
|
|||
}
|
||||
|
||||
foreach ($path as $i => $key) {
|
||||
if (is_numeric($key) && intval($key) > 0 || $key === '0') {
|
||||
$key = intval($key);
|
||||
if (is_numeric($key) && (int)$key > 0 || $key === '0') {
|
||||
$key = (int)$key;
|
||||
}
|
||||
if ($i === count($path) - 1) {
|
||||
return (is_array($data) && array_key_exists($key, $data));
|
||||
|
|
@ -705,7 +705,7 @@ class Set {
|
|||
*
|
||||
* @param array $val1 First value
|
||||
* @param array $val2 Second value
|
||||
* @return boolean true if $val1 contains $val2, false otherwise
|
||||
* @return bool true if $val1 contains $val2, false otherwise
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::contains
|
||||
*/
|
||||
public static function contains($val1, $val2 = null) {
|
||||
|
|
@ -730,12 +730,12 @@ class Set {
|
|||
* only consider the dimension of the first element in the array.
|
||||
*
|
||||
* @param array $array Array to count dimensions on
|
||||
* @param boolean $all Set to true to count the dimension considering all elements in array
|
||||
* @param integer $count Start the dimension count at this number
|
||||
* @return integer The number of dimensions in $array
|
||||
* @param bool $all Set to true to count the dimension considering all elements in array
|
||||
* @param int $count Start the dimension count at this number
|
||||
* @return int The number of dimensions in $array
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::countDim
|
||||
*/
|
||||
public static function countDim($array = null, $all = false, $count = 0) {
|
||||
public static function countDim($array, $all = false, $count = 0) {
|
||||
if ($all) {
|
||||
$depth = array($count);
|
||||
if (is_array($array) && reset($array) !== false) {
|
||||
|
|
@ -758,9 +758,9 @@ class Set {
|
|||
* Normalizes a string or array list.
|
||||
*
|
||||
* @param mixed $list List to normalize
|
||||
* @param boolean $assoc If true, $list will be converted to an associative array
|
||||
* @param bool $assoc If true, $list will be converted to an associative array
|
||||
* @param string $sep If $list is a string, it will be split into an array with $sep
|
||||
* @param boolean $trim If true, separated strings will be trimmed
|
||||
* @param bool $trim If true, separated strings will be trimmed
|
||||
* @return array
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::normalize
|
||||
*/
|
||||
|
|
@ -827,7 +827,7 @@ class Set {
|
|||
}
|
||||
}
|
||||
|
||||
if ($groupPath != null) {
|
||||
if ($groupPath) {
|
||||
$group = Set::extract($data, $groupPath);
|
||||
if (!empty($group)) {
|
||||
$c = count($keys);
|
||||
|
|
@ -851,6 +851,7 @@ class Set {
|
|||
|
||||
/**
|
||||
* Converts an object into an array.
|
||||
*
|
||||
* @param object $object Object to reverse
|
||||
* @return array Array representation of given object
|
||||
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::reverse
|
||||
|
|
@ -909,7 +910,7 @@ class Set {
|
|||
}
|
||||
|
||||
/**
|
||||
* Expand/unflattens an string to an array
|
||||
* Expand/unflattens a string to an array
|
||||
*
|
||||
* For example, unflattens an array that was collapsed with `Set::flatten()`
|
||||
* into a multi-dimensional array. So, `array('0.Foo.Bar' => 'Far')` becomes
|
||||
|
|
@ -926,15 +927,15 @@ class Set {
|
|||
/**
|
||||
* Flattens an array for sorting
|
||||
*
|
||||
* @param array $results
|
||||
* @param string $key
|
||||
* @param array $results Array to flatten.
|
||||
* @param string $key Key.
|
||||
* @return array
|
||||
*/
|
||||
protected static function _flatten($results, $key = null) {
|
||||
$stack = array();
|
||||
foreach ($results as $k => $r) {
|
||||
$id = $k;
|
||||
if (!is_null($key)) {
|
||||
if ($key !== null) {
|
||||
$id = $key;
|
||||
}
|
||||
if (is_array($r) && !empty($r)) {
|
||||
|
|
@ -1010,7 +1011,7 @@ class Set {
|
|||
*/
|
||||
public static function apply($path, $data, $callback, $options = array()) {
|
||||
$defaults = array('type' => 'pass');
|
||||
$options = array_merge($defaults, $options);
|
||||
$options += $defaults;
|
||||
$extracted = Set::extract($path, $data);
|
||||
|
||||
if ($options['type'] === 'map') {
|
||||
|
|
@ -1026,7 +1027,7 @@ class Set {
|
|||
/**
|
||||
* Takes in a flat array and returns a nested array
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param mixed $data Data
|
||||
* @param array $options Options are:
|
||||
* children - the key name to use in the resultset for children
|
||||
* idPath - the path to a key that identifies each entry
|
||||
|
|
|
|||
|
|
@ -2,604 +2,26 @@
|
|||
/**
|
||||
* String handling methods.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @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 MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
App::uses('CakeText', 'Utility');
|
||||
|
||||
/**
|
||||
* String handling methods.
|
||||
*
|
||||
*
|
||||
* @package Cake.Utility
|
||||
* @package Cake.Utility
|
||||
* @deprecated 3.0.0 Deprecated since version 2.7, use CakeText class instead.
|
||||
*/
|
||||
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());
|
||||
$uuid = 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
|
||||
);
|
||||
|
||||
return $uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 array Array of tokens in $data.
|
||||
*/
|
||||
public static function tokenize($data, $separator = ',', $leftBound = '(', $rightBound = ')') {
|
||||
if (empty($data) || is_array($data)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$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 ($data{$tmpOffset} == $separator && $depth == 0) {
|
||||
$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--;
|
||||
$open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
$offset = ++$tmpOffset;
|
||||
} else {
|
||||
$results[] = $buffer . substr($data, $offset);
|
||||
$offset = $length + 1;
|
||||
}
|
||||
}
|
||||
if (empty($results) && !empty($buffer)) {
|
||||
$results[] = $buffer;
|
||||
}
|
||||
|
||||
if (!empty($results)) {
|
||||
$data = array_map('trim', $results);
|
||||
} else {
|
||||
$data = array();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 string $data A key => val array where each key stands for a placeholder variable name
|
||||
* to be replaced with val
|
||||
* @param string $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;
|
||||
} else {
|
||||
asort($data);
|
||||
|
||||
$hashKeys = array();
|
||||
foreach ($data as $key => $value) {
|
||||
$hashKeys[] = crc32($key);
|
||||
}
|
||||
|
||||
$tempData = array_combine(array_keys($data), array_values($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
|
||||
* @param string $options
|
||||
* @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 Text the text to format.
|
||||
* @param array|integer $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 = 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ued to match words, default is '|$tag|iu'
|
||||
*
|
||||
* @param string $text Text to search the phrase in
|
||||
* @param string $phrase The phrase 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;
|
||||
}
|
||||
|
||||
$default = array(
|
||||
'format' => '<span class="highlight">\1</span>',
|
||||
'html' => false,
|
||||
'regex' => "|%s|iu"
|
||||
);
|
||||
$options = array_merge($default, $options);
|
||||
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);
|
||||
} else {
|
||||
$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.
|
||||
*
|
||||
* Cuts a string to the length of $length and replaces the last characters
|
||||
* with the ending if the text is longer than length.
|
||||
*
|
||||
* ### Options:
|
||||
*
|
||||
* - `ending` Will be used as Ending and appended to the trimmed string
|
||||
* - `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 integer $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()) {
|
||||
$default = array(
|
||||
'ending' => '...', 'exact' => true, 'html' => false
|
||||
);
|
||||
$options = array_merge($default, $options);
|
||||
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($ending));
|
||||
$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;
|
||||
} else {
|
||||
$truncate = mb_substr($text, 0, $length - mb_strlen($ending));
|
||||
}
|
||||
}
|
||||
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 .= $ending;
|
||||
|
||||
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 integer $radius The amount of characters that will be returned on each side of the founded phrase
|
||||
* @param string $ending 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, $ending = '...') {
|
||||
if (empty($text) || empty($phrase)) {
|
||||
return self::truncate($text, $radius * 2, array('ending' => $ending));
|
||||
}
|
||||
|
||||
$append = $prepend = $ending;
|
||||
|
||||
$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) . $ending;
|
||||
}
|
||||
|
||||
$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 English
|
||||
*
|
||||
* @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 = 'and', $separator = ', ') {
|
||||
if (count($list) > 1) {
|
||||
return implode($separator, array_slice($list, null, -1)) . ' ' . $and . ' ' . array_pop($list);
|
||||
} else {
|
||||
return array_pop($list);
|
||||
}
|
||||
}
|
||||
|
||||
class String extends CakeText {
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,24 +4,24 @@
|
|||
*
|
||||
* The methods in these classes enable the datasources that use XML to work.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.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 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Utility
|
||||
* @since CakePHP v .0.10.3.1400
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
App::uses('HttpSocket', 'Network/Http');
|
||||
|
||||
/**
|
||||
* XML handling for Cake.
|
||||
* XML handling for CakePHP.
|
||||
*
|
||||
* The methods in these classes enable the datasources that use XML to work.
|
||||
*
|
||||
|
|
@ -52,7 +52,7 @@ class Xml {
|
|||
*
|
||||
* Building from an array:
|
||||
*
|
||||
* {{{
|
||||
* ```
|
||||
* $value = array(
|
||||
* 'tags' => array(
|
||||
* 'tag' => array(
|
||||
|
|
@ -68,18 +68,23 @@ class Xml {
|
|||
* )
|
||||
* );
|
||||
* $xml = Xml::build($value);
|
||||
* }}}
|
||||
* ```
|
||||
*
|
||||
* When building XML from an array ensure that there is only one top level element.
|
||||
*
|
||||
* ### Options
|
||||
*
|
||||
* - `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 `<!ENTITY` definitions. This
|
||||
* - `loadEntities` Defaults to false. Set to true to enable loading of `<!ENTITY` definitions. This
|
||||
* is disabled by default for security reasons.
|
||||
* - If using array as input, you can pass `options` from Xml::fromArray.
|
||||
* - `readFile` Set to false to disable file reading. This is important to disable when
|
||||
* putting user data into Xml::build(). If enabled local & remote files will be read if they exist.
|
||||
* Defaults to true for backwards compatibility reasons.
|
||||
* - `parseHuge` Enable the `LIBXML_PARSEHUGE`
|
||||
*
|
||||
* @param string|array $input XML string, a path to a file, an URL or an array
|
||||
* If using array as input, you can pass `options` from Xml::fromArray.
|
||||
*
|
||||
* @param string|array $input XML string, a path to a file, a URL or an array
|
||||
* @param array $options The options to use
|
||||
* @return SimpleXMLElement|DOMDocument SimpleXMLElement or DOMDocument
|
||||
* @throws XmlException
|
||||
|
|
@ -91,22 +96,28 @@ class Xml {
|
|||
$defaults = array(
|
||||
'return' => 'simplexml',
|
||||
'loadEntities' => false,
|
||||
'readFile' => true,
|
||||
'parseHuge' => true
|
||||
);
|
||||
$options = array_merge($defaults, $options);
|
||||
$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) {
|
||||
$socket = new HttpSocket();
|
||||
$response = $socket->get($input);
|
||||
if (!$response->isOk()) {
|
||||
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 static::_loadXml($response->body, $options);
|
||||
} catch (SocketException $e) {
|
||||
throw new XmlException(__d('cake_dev', 'XML cannot be read.'));
|
||||
}
|
||||
return self::_loadXml($response->body, $options);
|
||||
} elseif (!is_string($input)) {
|
||||
throw new XmlException(__d('cake_dev', 'Invalid input.'));
|
||||
}
|
||||
|
|
@ -117,8 +128,9 @@ class Xml {
|
|||
* Parse the input data and create either a SimpleXmlElement object or a DOMDocument.
|
||||
*
|
||||
* @param string $input The input to load.
|
||||
* @param array $options The options to use. See Xml::build()
|
||||
* @return SimpleXmlElement|DOMDocument.
|
||||
* @param array $options The options to use. See Xml::build()
|
||||
* @return SimpleXmlElement|DOMDocument
|
||||
* @throws XmlException
|
||||
*/
|
||||
protected static function _loadXml($input, $options) {
|
||||
$hasDisable = function_exists('libxml_disable_entity_loader');
|
||||
|
|
@ -126,16 +138,27 @@ class Xml {
|
|||
if ($hasDisable && !$options['loadEntities']) {
|
||||
libxml_disable_entity_loader(true);
|
||||
}
|
||||
if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
|
||||
$xml = new SimpleXMLElement($input, LIBXML_NOCDATA);
|
||||
} else {
|
||||
$xml = new DOMDocument();
|
||||
$xml->loadXML($input);
|
||||
$flags = LIBXML_NOCDATA;
|
||||
if (!empty($options['parseHuge'])) {
|
||||
$flags |= LIBXML_PARSEHUGE;
|
||||
}
|
||||
try {
|
||||
if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
|
||||
$xml = new SimpleXMLElement($input, LIBXML_NOCDATA);
|
||||
} else {
|
||||
$xml = new DOMDocument();
|
||||
$xml->loadXML($input);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$xml = null;
|
||||
}
|
||||
if ($hasDisable && !$options['loadEntities']) {
|
||||
libxml_disable_entity_loader(false);
|
||||
}
|
||||
libxml_use_internal_errors($internalErrors);
|
||||
if ($xml === null) {
|
||||
throw new XmlException(__d('cake_dev', 'Xml cannot be read.'));
|
||||
}
|
||||
return $xml;
|
||||
}
|
||||
|
||||
|
|
@ -144,14 +167,15 @@ class Xml {
|
|||
*
|
||||
* ### 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.
|
||||
* - `return` If return object of SimpleXMLElement ('simplexml') or DOMDocument ('domdocument'). Default is SimpleXMLElement.
|
||||
*
|
||||
* Using the following data:
|
||||
*
|
||||
* {{{
|
||||
* ```
|
||||
* $value = array(
|
||||
* 'root' => array(
|
||||
* 'tag' => array(
|
||||
|
|
@ -161,13 +185,13 @@ class Xml {
|
|||
* )
|
||||
* )
|
||||
* );
|
||||
* }}}
|
||||
* ```
|
||||
*
|
||||
* Calling `Xml::fromArray($value, 'tags');` Will generate:
|
||||
*
|
||||
* `<root><tag><id>1</id><value>defect</value>description</tag></root>`
|
||||
*
|
||||
* And calling `Xml::fromArray($value, 'attribute');` Will generate:
|
||||
* And calling `Xml::fromArray($value, 'attributes');` Will generate:
|
||||
*
|
||||
* `<root><tag id="1" value="defect">description</tag></root>`
|
||||
*
|
||||
|
|
@ -181,7 +205,7 @@ class Xml {
|
|||
throw new XmlException(__d('cake_dev', 'Invalid input.'));
|
||||
}
|
||||
$key = key($input);
|
||||
if (is_integer($key)) {
|
||||
if (is_int($key)) {
|
||||
throw new XmlException(__d('cake_dev', 'The key of input must be alphanumeric'));
|
||||
}
|
||||
|
||||
|
|
@ -192,12 +216,16 @@ class Xml {
|
|||
'format' => 'tags',
|
||||
'version' => '1.0',
|
||||
'encoding' => Configure::read('App.encoding'),
|
||||
'return' => 'simplexml'
|
||||
'return' => 'simplexml',
|
||||
'pretty' => false
|
||||
);
|
||||
$options = array_merge($defaults, $options);
|
||||
$options += $defaults;
|
||||
|
||||
$dom = new DOMDocument($options['version'], $options['encoding']);
|
||||
self::_fromArray($dom, $dom, $input, $options['format']);
|
||||
if ($options['pretty']) {
|
||||
$dom->formatOutput = true;
|
||||
}
|
||||
static::_fromArray($dom, $dom, $input, $options['format']);
|
||||
|
||||
$options['return'] = strtolower($options['return']);
|
||||
if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
|
||||
|
|
@ -211,8 +239,8 @@ class Xml {
|
|||
*
|
||||
* @param DOMDocument $dom Handler to DOMDocument
|
||||
* @param DOMElement $node Handler to DOMElement (child)
|
||||
* @param array $data Array of data to append to the $node.
|
||||
* @param string $format Either 'attribute' or 'tags'. This determines where nested keys go.
|
||||
* @param array &$data Array of data to append to the $node.
|
||||
* @param string $format Either 'attributes' or 'tags'. This determines where nested keys go.
|
||||
* @return void
|
||||
* @throws XmlException
|
||||
*/
|
||||
|
|
@ -261,10 +289,10 @@ class Xml {
|
|||
foreach ($value as $item) {
|
||||
$itemData = compact('dom', 'node', 'key', 'format');
|
||||
$itemData['value'] = $item;
|
||||
self::_createChild($itemData);
|
||||
static::_createChild($itemData);
|
||||
}
|
||||
} else { // Struct
|
||||
self::_createChild(compact('dom', 'node', 'key', 'value', 'format'));
|
||||
static::_createChild(compact('dom', 'node', 'key', 'value', 'format'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -291,25 +319,24 @@ class Xml {
|
|||
$childNS = $value['xmlns:'];
|
||||
unset($value['xmlns:']);
|
||||
}
|
||||
} elseif (!empty($value) || $value === 0) {
|
||||
} elseif (!empty($value) || $value === 0 || $value === '0') {
|
||||
$childValue = (string)$value;
|
||||
}
|
||||
|
||||
if ($childValue) {
|
||||
$child = $dom->createElement($key, $childValue);
|
||||
} else {
|
||||
$child = $dom->createElement($key);
|
||||
$child = $dom->createElement($key);
|
||||
if ($childValue !== null) {
|
||||
$child->appendChild($dom->createTextNode($childValue));
|
||||
}
|
||||
if ($childNS) {
|
||||
$child->setAttribute('xmlns', $childNS);
|
||||
}
|
||||
|
||||
self::_fromArray($dom, $child, $value, $format);
|
||||
static::_fromArray($dom, $child, $value, $format);
|
||||
$node->appendChild($child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this XML structure as a array.
|
||||
* Returns this XML structure as an array.
|
||||
*
|
||||
* @param SimpleXMLElement|DOMDocument|DOMNode $obj SimpleXMLElement, DOMDocument or DOMNode instance
|
||||
* @return array Array representation of the XML structure.
|
||||
|
|
@ -324,7 +351,7 @@ class Xml {
|
|||
}
|
||||
$result = array();
|
||||
$namespaces = array_merge(array('' => ''), $obj->getNamespaces(true));
|
||||
self::_toArray($obj, $result, '', array_keys($namespaces));
|
||||
static::_toArray($obj, $result, '', array_keys($namespaces));
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
@ -332,7 +359,7 @@ class Xml {
|
|||
* Recursive method to toArray
|
||||
*
|
||||
* @param SimpleXMLElement $xml SimpleXMLElement object
|
||||
* @param array $parentData Parent array with data
|
||||
* @param array &$parentData Parent array with data
|
||||
* @param string $ns Namespace of current child
|
||||
* @param array $namespaces List of namespaces in XML
|
||||
* @return void
|
||||
|
|
@ -349,14 +376,14 @@ class Xml {
|
|||
}
|
||||
|
||||
foreach ($xml->children($namespace, true) as $child) {
|
||||
self::_toArray($child, $data, $namespace, $namespaces);
|
||||
static::_toArray($child, $data, $namespace, $namespaces);
|
||||
}
|
||||
}
|
||||
|
||||
$asString = trim((string)$xml);
|
||||
if (empty($data)) {
|
||||
$data = $asString;
|
||||
} elseif (!empty($asString)) {
|
||||
} elseif (strlen($asString) > 0) {
|
||||
$data['@'] = $asString;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue