summaryrefslogtreecommitdiff
path: root/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility
diff options
context:
space:
mode:
authorLudovic Pouzenc <ludovic@pouzenc.fr>2012-08-02 11:09:40 +0000
committerLudovic Pouzenc <ludovic@pouzenc.fr>2012-08-02 11:09:40 +0000
commit6dfd5d507d9863f987b30b0a5ab4268aea2ed875 (patch)
tree9c3de66b4aaa1e6bfa3c6442d807ec70bc479d11 /poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility
parentf4792ba1a7785220fef5adb1cb3e7ce8ac40152f (diff)
download2012-php-weave-6dfd5d507d9863f987b30b0a5ab4268aea2ed875.tar.gz
2012-php-weave-6dfd5d507d9863f987b30b0a5ab4268aea2ed875.tar.bz2
2012-php-weave-6dfd5d507d9863f987b30b0a5ab4268aea2ed875.zip
J'étais parti sur un download pourri de Cake. Les gars on abusé sur GitHub.
git-svn-id: file:///var/svn/2012-php-weave/trunk@7 d972a294-176a-4cf9-8ea1-fcd5b0c30f5c
Diffstat (limited to 'poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility')
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/CakeNumber.php296
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/CakeTime.php1057
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/ClassRegistry.php368
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Debugger.php817
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/File.php568
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Folder.php780
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Hash.php974
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Inflector.php551
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/ObjectCollection.php326
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Sanitize.php264
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Security.php192
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Set.php1108
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/String.php605
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Validation.php937
-rw-r--r--poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Xml.php370
15 files changed, 9213 insertions, 0 deletions
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/CakeNumber.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/CakeNumber.php
new file mode 100644
index 0000000..3b21cbc
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/CakeNumber.php
@@ -0,0 +1,296 @@
+<?php
+/**
+ * CakeNumber Utility.
+ *
+ * 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)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+/**
+ * Number helper library.
+ *
+ * Methods to make numbers more readable.
+ *
+ * @package Cake.Utility
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html
+ */
+class CakeNumber {
+
+/**
+ * Currencies supported by the helper. You can add additional currency formats
+ * with CakeNumber::addFormat
+ *
+ * @var array
+ */
+ protected static $_currencies = array(
+ 'USD' => array(
+ 'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after',
+ 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true
+ ),
+ 'GBP' => array(
+ 'wholeSymbol' => '&#163;', 'wholePosition' => 'before', 'fractionSymbol' => 'p', 'fractionPosition' => 'after',
+ 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()','escape' => false
+ ),
+ 'EUR' => array(
+ 'wholeSymbol' => '&#8364;', 'wholePosition' => 'before', 'fractionSymbol' => false, 'fractionPosition' => 'after',
+ 'zero' => 0, 'places' => 2, 'thousands' => '.', 'decimals' => ',', 'negative' => '()', 'escape' => false
+ )
+ );
+
+/**
+ * Default options for currency formats
+ *
+ * @var array
+ */
+ protected static $_currencyDefaults = array(
+ 'wholeSymbol' => '', 'wholePosition' => 'before', 'fractionSymbol' => '', 'fractionPosition' => 'after',
+ 'zero' => '0', 'places' => 2, 'thousands' => ',', 'decimals' => '.','negative' => '()', 'escape' => true,
+ );
+
+/**
+ * If native number_format() should be used. If >= PHP5.4
+ *
+ * @var boolean
+ */
+ 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.
+ * @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);
+ }
+
+/**
+ * Returns a formatted-for-humans file size.
+ *
+ * @param integer $size Size in bytes
+ * @return string Human readable size
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::toReadableSize
+ */
+ public static function toReadableSize($size) {
+ switch (true) {
+ 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));
+ case round($size / 1024 / 1024, 2) < 1024:
+ return __d('cake', '%.2f MB', self::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));
+ default:
+ return __d('cake', '%.2f TB', self::precision($size / 1024 / 1024 / 1024 / 1024, 2));
+ }
+ }
+
+/**
+ * Formats a number into a percentage string.
+ *
+ * @param float $number A floating point number
+ * @param integer $precision The precision of the returned number
+ * @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) . '%';
+ }
+
+/**
+ * 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
+ * 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) {
+ $places = 0;
+ if (is_int($options)) {
+ $places = $options;
+ }
+
+ $separators = array(',', '.', '-', ':');
+
+ $before = $after = null;
+ if (is_string($options) && !in_array($options, $separators)) {
+ $before = $options;
+ }
+ $thousands = ',';
+ if (!is_array($options) && in_array($options, $separators)) {
+ $thousands = $options;
+ }
+ $decimals = '.';
+ if (!is_array($options) && in_array($options, $separators)) {
+ $decimals = $options;
+ }
+
+ $escape = true;
+ if (is_array($options)) {
+ $options = array_merge(array('before' => '$', 'places' => 2, 'thousands' => ',', 'decimals' => '.'), $options);
+ extract($options);
+ }
+
+ $out = $before . self::_numberFormat($number, $places, $decimals, $thousands) . $after;
+
+ if ($escape) {
+ return h($out);
+ }
+ return $out;
+ }
+
+/**
+ * Alternative number_format() to accommodate multibyte decimals and thousands < PHP 5.4
+ *
+ * @param float $number
+ * @param integer $places
+ * @param string $decimals
+ * @param string $thousands
+ * @return string
+ */
+ protected static function _numberFormat($number, $places = 0, $decimals = '.', $thousands = ',') {
+ if (!isset(self::$_numberFormatSupport)) {
+ self::$_numberFormatSupport = version_compare(PHP_VERSION, '5.4.0', '>=');
+ }
+ if (self::$_numberFormatSupport) {
+ return number_format($number, $places, $decimals, $thousands);
+ }
+ $number = number_format($number, $places, '.', '');
+ $after = '';
+ $foundDecimal = strpos($number, '.');
+ if ($foundDecimal !== false) {
+ $after = substr($number, $foundDecimal);
+ $number = substr($number, 0, $foundDecimal);
+ }
+ while (($foundThousand = preg_replace('/(\d+)(\d\d\d)/', '\1 \2', $number)) != $number) {
+ $number = $foundThousand;
+ }
+ $number .= $after;
+ return strtr($number, array(' ' => $thousands, '.' => $decimals));
+ }
+
+/**
+ * Formats a number into a currency format.
+ *
+ * ### Options
+ *
+ * - `wholeSymbol` - The currency symbol to use for whole numbers,
+ * greater than 1, or less than -1.
+ * - `wholePosition` - The position the whole symbol should be placed
+ * valid options are 'before' & 'after'.
+ * - `fractionSymbol` - The currency symbol to use for fractional numbers.
+ * - `fractionPosition` - The position the fraction symbol should be placed
+ * valid options are 'before' & 'after'.
+ * - `before` - The currency symbol to place before whole numbers
+ * 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`
+ * - `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
+ * - `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 htmlentity escaped? Defaults to true
+ *
+ * @param float $number
+ * @param string $currency Shortcut to default options. Valid values are
+ * 'USD', 'EUR', 'GBP', otherwise set at least 'before' and 'after' options.
+ * @param array $options
+ * @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;
+
+ if (isset(self::$_currencies[$currency])) {
+ $default = self::$_currencies[$currency];
+ } elseif (is_string($currency)) {
+ $options['before'] = $currency;
+ }
+
+ $options = array_merge($default, $options);
+
+ if (isset($options['before']) && $options['before'] !== '') {
+ $options['wholeSymbol'] = $options['before'];
+ }
+ if (isset($options['after']) && !$options['after'] !== '') {
+ $options['fractionSymbol'] = $options['after'];
+ }
+
+ $result = $options['before'] = $options['after'] = null;
+
+ $symbolKey = 'whole';
+ if ($number == 0 ) {
+ if ($options['zero'] !== 0 ) {
+ return $options['zero'];
+ }
+ } elseif ($number < 1 && $number > -1 ) {
+ if ($options['fractionSymbol'] !== false) {
+ $multiply = intval('1' . str_pad('', $options['places'], '0'));
+ $number = $number * $multiply;
+ $options['places'] = null;
+ $symbolKey = 'fraction';
+ }
+ }
+
+ $position = $options[$symbolKey . 'Position'] != 'after' ? 'before' : 'after';
+ $options[$position] = $options[$symbolKey . 'Symbol'];
+
+ $abs = abs($number);
+ $result = self::format($abs, $options);
+
+ if ($number < 0 ) {
+ if ($options['negative'] == '()') {
+ $result = '(' . $result . ')';
+ } else {
+ $result = $options['negative'] . $result;
+ }
+ }
+ return $result;
+ }
+
+/**
+ * Add a currency format to the Number helper. Makes reusing
+ * currency formats easier.
+ *
+ * {{{ $number->addFormat('NOK', array('before' => 'Kr. ')); }}}
+ *
+ * You can now use `NOK` as a shortform when formatting currency amounts.
+ *
+ * {{{ $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.
+ *
+ * @param string $formatName The format name to be used in the future.
+ * @param array $options The array of options for this format.
+ * @return void
+ * @see NumberHelper::currency()
+ * @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;
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/CakeTime.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/CakeTime.php
new file mode 100644
index 0000000..4788d53
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/CakeTime.php
@@ -0,0 +1,1057 @@
+<?php
+/**
+ * CakeTime utility class file.
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+App::uses('Multibyte', 'I18n');
+
+/**
+ * Time Helper class for easy use of time data.
+ *
+ * Manipulation of time data.
+ *
+ * @package Cake.Utility
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html
+ */
+class CakeTime {
+
+/**
+ * The format to use when formatting a time using `CakeTime::nice()`
+ *
+ * The format should use the locale strings as defined in the PHP docs under
+ * `strftime` (http://php.net/manual/en/function.strftime.php)
+ *
+ * @var string
+ * @see CakeTime::format()
+ */
+ public static $niceFormat = '%a, %b %eS %Y, %H:%M';
+
+/**
+ * The format to use when formatting a time using `CakeTime::timeAgoInWords()`
+ * and the difference is more than `CakeTime::$wordEnd`
+ *
+ * @var string
+ * @see CakeTime::timeAgoInWords()
+ */
+ public static $wordFormat = 'j/n/y';
+
+/**
+ * The format to use when formatting a time using `CakeTime::niceShort()`
+ * and the difference is between 3 and 7 days
+ *
+ * @var string
+ * @see CakeTime::niceShort()
+ */
+ public static $niceShortFormat = '%d/%m, %H:%M';
+
+/**
+ * The format to use when formatting a time using `CakeTime::timeAgoInWords()`
+ * and the difference is less than `CakeTime::$wordEnd`
+ *
+ * @var array
+ * @see CakeTime::timeAgoInWords()
+ */
+ public static $wordAccuracy = array(
+ 'year' => "day",
+ 'month' => "day",
+ 'week' => "day",
+ 'day' => "hour",
+ 'hour' => "minute",
+ 'minute' => "minute",
+ 'second' => "second",
+ );
+
+/**
+ * The end of relative time telling
+ *
+ * @var string
+ * @see CakeTime::timeAgoInWords()
+ */
+ public static $wordEnd = '+1 month';
+
+/**
+ * Temporary variable containing timestamp value, used internally convertSpecifiers()
+ */
+ protected static $_time = null;
+
+/**
+ * Magic set method for backward compatibility.
+ *
+ * Used by TimeHelper to modify static variables in CakeTime
+ */
+ public function __set($name, $value) {
+ switch ($name) {
+ case 'niceFormat':
+ self::${$name} = $value;
+ break;
+ default:
+ break;
+ }
+ }
+
+/**
+ * Magic set method for backward compatibility.
+ *
+ * Used by TimeHelper to get static variables in CakeTime
+ */
+ public function __get($name) {
+ switch ($name) {
+ case 'niceFormat':
+ return self::${$name};
+ break;
+ default:
+ return null;
+ break;
+ }
+ }
+
+/**
+ * Converts a string representing the format for the function strftime and returns a
+ * windows safe and i18n aware format.
+ *
+ * @param string $format Format with specifiers for strftime function.
+ * Accepts the special specifier %S which mimics the modifier S for date()
+ * @param string $time UNIX timestamp
+ * @return string windows safe and date() function compatible format for strftime
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function convertSpecifiers($format, $time = null) {
+ if (!$time) {
+ $time = time();
+ }
+ self::$_time = $time;
+ return preg_replace_callback('/\%(\w+)/', array('CakeTime', '_translateSpecifier'), $format);
+ }
+
+/**
+ * Auxiliary function to translate a matched specifier element from a regular expression into
+ * a windows safe and i18n aware specifier
+ *
+ * @param array $specifier match from regular expression
+ * @return string converted element
+ */
+ protected static function _translateSpecifier($specifier) {
+ switch ($specifier[1]) {
+ case 'a':
+ $abday = __dc('cake', 'abday', 5);
+ if (is_array($abday)) {
+ return $abday[date('w', self::$_time)];
+ }
+ break;
+ case 'A':
+ $day = __dc('cake', 'day', 5);
+ if (is_array($day)) {
+ return $day[date('w', self::$_time)];
+ }
+ break;
+ case 'c':
+ $format = __dc('cake', 'd_t_fmt', 5);
+ if ($format != 'd_t_fmt') {
+ return self::convertSpecifiers($format, self::$_time);
+ }
+ break;
+ case 'C':
+ return sprintf("%02d", date('Y', self::$_time) / 100);
+ case 'D':
+ return '%m/%d/%y';
+ case 'e':
+ if (DS === '/') {
+ return '%e';
+ }
+ $day = date('j', self::$_time);
+ if ($day < 10) {
+ $day = ' ' . $day;
+ }
+ return $day;
+ case 'eS' :
+ return date('jS', self::$_time);
+ case 'b':
+ case 'h':
+ $months = __dc('cake', 'abmon', 5);
+ if (is_array($months)) {
+ return $months[date('n', self::$_time) - 1];
+ }
+ return '%b';
+ case 'B':
+ $months = __dc('cake', 'mon', 5);
+ if (is_array($months)) {
+ return $months[date('n', self::$_time) - 1];
+ }
+ break;
+ case 'n':
+ return "\n";
+ case 'p':
+ case 'P':
+ $default = array('am' => 0, 'pm' => 1);
+ $meridiem = $default[date('a', self::$_time)];
+ $format = __dc('cake', 'am_pm', 5);
+ if (is_array($format)) {
+ $meridiem = $format[$meridiem];
+ return ($specifier[1] == 'P') ? strtolower($meridiem) : strtoupper($meridiem);
+ }
+ break;
+ case 'r':
+ $complete = __dc('cake', 't_fmt_ampm', 5);
+ if ($complete != 't_fmt_ampm') {
+ return str_replace('%p', self::_translateSpecifier(array('%p', 'p')), $complete);
+ }
+ break;
+ case 'R':
+ return date('H:i', self::$_time);
+ case 't':
+ return "\t";
+ case 'T':
+ return '%H:%M:%S';
+ case 'u':
+ return ($weekDay = date('w', self::$_time)) ? $weekDay : 7;
+ case 'x':
+ $format = __dc('cake', 'd_fmt', 5);
+ if ($format != 'd_fmt') {
+ return self::convertSpecifiers($format, self::$_time);
+ }
+ break;
+ case 'X':
+ $format = __dc('cake', 't_fmt', 5);
+ if ($format != 't_fmt') {
+ return self::convertSpecifiers($format, self::$_time);
+ }
+ break;
+ }
+ return $specifier[0];
+ }
+
+/**
+ * Converts given time (in server's time zone) to user's local time, given his/her timezone.
+ *
+ * @param string $serverTime UNIX timestamp
+ * @param string|DateTimeZone $timezone User's timezone string or DateTimeZone object
+ * @return integer UNIX timestamp
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function convert($serverTime, $timezone) {
+ static $serverTimezone = null;
+ if (is_null($serverTimezone) || (date_default_timezone_get() !== $serverTimezone->getName())) {
+ $serverTimezone = new DateTimeZone(date_default_timezone_get());
+ }
+ $serverOffset = $serverTimezone->getOffset(new DateTime('@' . $serverTime));
+ $gmtTime = $serverTime - $serverOffset;
+ if (is_numeric($timezone)) {
+ $userOffset = $timezone * (60 * 60);
+ } else {
+ $timezone = self::timezone($timezone);
+ $userOffset = $timezone->getOffset(new DateTime('@' . $gmtTime));
+ }
+ $userTime = $gmtTime + $userOffset;
+ return (int)$userTime;
+ }
+
+/**
+ * Returns a timezone object from a string or the user's timezone object
+ *
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * If null it tries to get timezone from 'Config.timezone' config var
+ * @return DateTimeZone Timezone object
+ */
+ public static function timezone($timezone = null) {
+ static $tz = null;
+
+ if (is_object($timezone)) {
+ if ($tz === null || $tz->getName() !== $timezone->getName()) {
+ $tz = $timezone;
+ }
+ } else {
+ if ($timezone === null) {
+ $timezone = Configure::read('Config.timezone');
+ if ($timezone === null) {
+ $timezone = date_default_timezone_get();
+ }
+ }
+
+ if ($tz === null || $tz->getName() !== $timezone) {
+ $tz = new DateTimeZone($timezone);
+ }
+ }
+
+ return $tz;
+ }
+
+/**
+ * Returns server's offset from GMT in seconds.
+ *
+ * @return integer Offset
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function serverOffset() {
+ return date('Z', time());
+ }
+
+/**
+ * Returns a UNIX timestamp, given either a UNIX timestamp or a valid strtotime() date string.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return string Parsed timestamp
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function fromString($dateString, $timezone = null) {
+ if (empty($dateString)) {
+ return false;
+ }
+
+ if (is_integer($dateString) || is_numeric($dateString)) {
+ $date = intval($dateString);
+ } elseif (is_object($dateString) && $dateString instanceof DateTime) {
+ $clone = clone $dateString;
+ $clone->setTimezone(new DateTimeZone(date_default_timezone_get()));
+ $date = (int)$clone->format('U') + $clone->getOffset();
+ } else {
+ $date = strtotime($dateString);
+ }
+
+ if ($date === -1 || empty($date)) {
+ return false;
+ }
+
+ if ($timezone === null) {
+ $timezone = Configure::read('Config.timezone');
+ }
+
+ if ($timezone !== null) {
+ return self::convert($date, $timezone);
+ }
+ return $date;
+ }
+
+/**
+ * Returns a nicely formatted date string for given Datetime string.
+ *
+ * See http://php.net/manual/en/function.strftime.php for information on formatting
+ * using locale strings.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @param string $format The format to use. If null, `TimeHelper::$niceFormat` is used
+ * @return string Formatted date string
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function nice($dateString = null, $timezone = null, $format = null) {
+ if (!$dateString) {
+ $dateString = time();
+ }
+ $date = self::fromString($dateString, $timezone);
+
+ if (!$format) {
+ $format = self::$niceFormat;
+ }
+ $format = self::convertSpecifiers($format, $date);
+ return self::_strftime($format, $date);
+ }
+
+/**
+ * Returns a formatted descriptive date string for given datetime string.
+ *
+ * If the given date is today, the returned string could be "Today, 16:54".
+ * If the given date is tomorrow, the returned string could be "Tomorrow, 16:54".
+ * If the given date was yesterday, the returned string could be "Yesterday, 16:54".
+ * If the given date is within next or last week, the returned string could be "On Thursday, 16:54".
+ * If $dateString's year is the current year, the returned string does not
+ * include mention of the year.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return string Described, relative date string
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function niceShort($dateString = null, $timezone = null) {
+ if (!$dateString) {
+ $dateString = time();
+ }
+ $date = self::fromString($dateString, $timezone);
+
+ $y = self::isThisYear($date) ? '' : ' %Y';
+
+ $d = self::_strftime("%w", $date);
+ $day = array(
+ __d('cake', 'Sunday'),
+ __d('cake', 'Monday'),
+ __d('cake', 'Tuesday'),
+ __d('cake', 'Wednesday'),
+ __d('cake', 'Thursday'),
+ __d('cake', 'Friday'),
+ __d('cake', 'Saturday')
+ );
+
+ if (self::isToday($dateString, $timezone)) {
+ $ret = __d('cake', 'Today, %s', self::_strftime("%H:%M", $date));
+ } elseif (self::wasYesterday($dateString, $timezone)) {
+ $ret = __d('cake', 'Yesterday, %s', self::_strftime("%H:%M", $date));
+ } elseif (self::isTomorrow($dateString, $timezone)) {
+ $ret = __d('cake', 'Tomorrow, %s', self::_strftime("%H:%M", $date));
+ } elseif (self::wasWithinLast('7 days', $dateString, $timezone)) {
+ $ret = sprintf('%s %s', $day[$d], self::_strftime(self::$niceShortFormat, $date));
+ } elseif (self::isWithinNext('7 days', $dateString, $timezone)) {
+ $ret = __d('cake', 'On %s %s', $day[$d], self::_strftime(self::$niceShortFormat, $date));
+ } else {
+ $format = self::convertSpecifiers("%b %eS{$y}, %H:%M", $date);
+ $ret = self::_strftime($format, $date);
+ }
+ return $ret;
+ }
+
+/**
+ * Returns a partial SQL string to search for all records between two dates.
+ *
+ * @param integer|string|DateTime $begin UNIX timestamp, strtotime() valid string or DateTime object
+ * @param integer|string|DateTime $end UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string $fieldName Name of database field to compare with
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return string Partial SQL string.
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function daysAsSql($begin, $end, $fieldName, $timezone = null) {
+ $begin = self::fromString($begin, $timezone);
+ $end = self::fromString($end, $timezone);
+ $begin = date('Y-m-d', $begin) . ' 00:00:00';
+ $end = date('Y-m-d', $end) . ' 23:59:59';
+
+ return "($fieldName >= '$begin') AND ($fieldName <= '$end')";
+ }
+
+/**
+ * Returns a partial SQL string to search for all records between two times
+ * occurring on the same day.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string $fieldName Name of database field to compare with
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return string Partial SQL string.
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function dayAsSql($dateString, $fieldName, $timezone = null) {
+ return self::daysAsSql($dateString, $dateString, $fieldName);
+ }
+
+/**
+ * Returns true if given datetime string is today.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return boolean True if datetime string is today
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#testing-time
+ */
+ public static function isToday($dateString, $timezone = null) {
+ $date = self::fromString($dateString, $timezone);
+ return date('Y-m-d', $date) == date('Y-m-d', time());
+ }
+
+/**
+ * Returns true if given datetime string is within this week.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return boolean True if datetime string is within current week
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#testing-time
+ */
+ public static function isThisWeek($dateString, $timezone = null) {
+ $date = self::fromString($dateString, $timezone);
+ return date('W o', $date) == date('W o', time());
+ }
+
+/**
+ * Returns true if given datetime string is within this month
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return boolean True if datetime string is within current month
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#testing-time
+ */
+ public static function isThisMonth($dateString, $timezone = null) {
+ $date = self::fromString($dateString);
+ return date('m Y', $date) == date('m Y', time());
+ }
+
+/**
+ * Returns true if given datetime string is within current year.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return boolean True if datetime string is within current year
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#testing-time
+ */
+ public static function isThisYear($dateString, $timezone = null) {
+ $date = self::fromString($dateString, $timezone);
+ return date('Y', $date) == date('Y', time());
+ }
+
+/**
+ * Returns true if given datetime string was yesterday.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return boolean True if datetime string was yesterday
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#testing-time
+ *
+ */
+ public static function wasYesterday($dateString, $timezone = null) {
+ $date = self::fromString($dateString, $timezone);
+ return date('Y-m-d', $date) == date('Y-m-d', strtotime('yesterday'));
+ }
+
+/**
+ * Returns true if given datetime string is tomorrow.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return boolean True if datetime string was yesterday
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#testing-time
+ */
+ public static function isTomorrow($dateString, $timezone = null) {
+ $date = self::fromString($dateString, $timezone);
+ return date('Y-m-d', $date) == date('Y-m-d', strtotime('tomorrow'));
+ }
+
+/**
+ * Returns the quarter
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param boolean $range if true returns a range in Y-m-d format
+ * @return mixed 1, 2, 3, or 4 quarter of year or array if $range true
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function toQuarter($dateString, $range = false) {
+ $time = self::fromString($dateString);
+ $date = ceil(date('m', $time) / 3);
+
+ if ($range === true) {
+ $range = 'Y-m-d';
+ }
+
+ if ($range !== false) {
+ $year = date('Y', $time);
+
+ switch ($date) {
+ case 1:
+ $date = array($year . '-01-01', $year . '-03-31');
+ break;
+ case 2:
+ $date = array($year . '-04-01', $year . '-06-30');
+ break;
+ case 3:
+ $date = array($year . '-07-01', $year . '-09-30');
+ break;
+ case 4:
+ $date = array($year . '-10-01', $year . '-12-31');
+ break;
+ }
+ }
+ return $date;
+ }
+
+/**
+ * Returns a UNIX timestamp from a textual datetime description. Wrapper for PHP function strtotime().
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return integer Unix timestamp
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function toUnix($dateString, $timezone = null) {
+ return self::fromString($dateString, $timezone);
+ }
+
+/**
+ * Returns a formatted date in server's timezone.
+ *
+ * If a DateTime object is given or the dateString has a timezone
+ * segment, the timezone parameter will be ignored.
+ *
+ * If no timezone parameter is given and no DateTime object, the passed $dateString will be
+ * considered to be in the UTC timezone.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @param string $format date format string
+ * @return mixed Formatted date
+ */
+ public static function toServer($dateString, $timezone = null, $format = 'Y-m-d H:i:s') {
+ if ($timezone === null) {
+ $timezone = new DateTimeZone('UTC');
+ } elseif (is_string($timezone)) {
+ $timezone = new DateTimeZone($timezone);
+ } elseif (!($timezone instanceof DateTimeZone)) {
+ return false;
+ }
+
+ if ($dateString instanceof DateTime) {
+ $date = $dateString;
+ } elseif (is_integer($dateString) || is_numeric($dateString)) {
+ $dateString = (int)$dateString;
+
+ $date = new DateTime('@' . $dateString);
+ $date->setTimezone($timezone);
+ } else {
+ $date = new DateTime($dateString, $timezone);
+ }
+
+ $date->setTimezone(new DateTimeZone(date_default_timezone_get()));
+ return $date->format($format);
+ }
+
+/**
+ * Returns a date formatted for Atom RSS feeds.
+ *
+ * @param string $dateString Datetime string or Unix timestamp
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return string Formatted date string
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function toAtom($dateString, $timezone = null) {
+ $date = self::fromString($dateString, $timezone);
+ return date('Y-m-d\TH:i:s\Z', $date);
+ }
+
+/**
+ * Formats date for RSS feeds
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return string Formatted date string
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function toRSS($dateString, $timezone = null) {
+ $date = self::fromString($dateString, $timezone);
+
+ if (!is_null($timezone)) {
+ if (is_numeric($timezone)) {
+ $userOffset = $timezone;
+ } else {
+ if (!is_object($timezone)) {
+ $timezone = new DateTimeZone($timezone);
+ }
+ $currentDate = new DateTime('@' . $date);
+ $currentDate->setTimezone($timezone);
+ $userOffset = $timezone->getOffset($currentDate) / 60 / 60;
+ }
+ if ($userOffset == 0) {
+ $timezone = '+0000';
+ } else {
+ $hours = (int)floor(abs($userOffset));
+ $minutes = (int)(fmod(abs($userOffset), $hours) * 60);
+ $timezone = ($userOffset < 0 ? '-' : '+') . str_pad($hours, 2, '0', STR_PAD_LEFT) . str_pad($minutes, 2, '0', STR_PAD_LEFT);
+ }
+ return date('D, d M Y H:i:s', $date) . ' ' . $timezone;
+ }
+ return date("r", $date);
+ }
+
+/**
+ * Returns either a relative date or a formatted date depending
+ * on the difference between the current time and given datetime.
+ * $datetime should be in a *strtotime* - parsable format, like MySQL's datetime datatype.
+ *
+ * ### Options:
+ *
+ * - `format` => a fall back format if the relative time is longer than the duration specified by end
+ * - `accuracy` => Specifies how accurate the date should be described (array)
+ * - year => The format if years > 0 (default "day")
+ * - month => The format if months > 0 (default "day")
+ * - week => The format if weeks > 0 (default "day")
+ * - day => The format if weeks > 0 (default "hour")
+ * - hour => The format if hours > 0 (default "minute")
+ * - minute => The format if minutes > 0 (default "minute")
+ * - second => The format if seconds > 0 (default "second")
+ * - `end` => The end of relative time telling
+ * - `userOffset` => Users offset from GMT (in hours) *Deprecated* use timezone intead.
+ * - `timezone` => The user timezone the timestamp should be formatted in.
+ *
+ * Relative dates look something like this:
+ *
+ * - 3 weeks, 4 days ago
+ * - 15 seconds ago
+ *
+ * Default date formatting is d/m/yy e.g: on 18/2/09
+ *
+ * The returned string includes 'ago' or 'on' and assumes you'll properly add a word
+ * like 'Posted ' before the function output.
+ *
+ * NOTE: If the difference is one week or more, the lowest level of accuracy is day
+ *
+ * @param integer|string|DateTime $dateTime Datetime UNIX timestamp, strtotime() valid string or DateTime object
+ * @param array $options Default format if timestamp is used in $dateString
+ * @return string Relative time string.
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function timeAgoInWords($dateTime, $options = array()) {
+ $timezone = null;
+ $format = self::$wordFormat;
+ $end = self::$wordEnd;
+ $accuracy = self::$wordAccuracy;
+
+ if (is_array($options)) {
+ if (isset($options['timezone'])) {
+ $timezone = $options['timezone'];
+ } elseif (isset($options['userOffset'])) {
+ $timezone = $options['userOffset'];
+ }
+
+ if (isset($options['accuracy'])) {
+ if (is_array($options['accuracy'])) {
+ $accuracy = array_merge($accuracy, $options['accuracy']);
+ } else {
+ foreach ($accuracy as $key => $level) {
+ $accuracy[$key] = $options['accuracy'];
+ }
+ }
+ }
+
+ if (isset($options['format'])) {
+ $format = $options['format'];
+ }
+ if (isset($options['end'])) {
+ $end = $options['end'];
+ }
+ unset($options['end'], $options['format']);
+ } else {
+ $format = $options;
+ }
+
+ $now = self::fromString(time(), $timezone);
+ $inSeconds = self::fromString($dateTime, $timezone);
+ $backwards = ($inSeconds > $now);
+
+ if ($backwards) {
+ $futureTime = $inSeconds;
+ $pastTime = $now;
+ } else {
+ $futureTime = $now;
+ $pastTime = $inSeconds;
+ }
+ $diff = $futureTime - $pastTime;
+
+ // If more than a week, then take into account the length of months
+ if ($diff >= 604800) {
+ list($future['H'], $future['i'], $future['s'], $future['d'], $future['m'], $future['Y']) = explode('/', date('H/i/s/d/m/Y', $futureTime));
+
+ list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $pastTime));
+ $years = $months = $weeks = $days = $hours = $minutes = $seconds = 0;
+
+ $years = $future['Y'] - $past['Y'];
+ $months = $future['m'] + ((12 * $years) - $past['m']);
+
+ if ($months >= 12) {
+ $years = floor($months / 12);
+ $months = $months - ($years * 12);
+ }
+ if ($future['m'] < $past['m'] && $future['Y'] - $past['Y'] == 1) {
+ $years--;
+ }
+
+ if ($future['d'] >= $past['d']) {
+ $days = $future['d'] - $past['d'];
+ } else {
+ $daysInPastMonth = date('t', $pastTime);
+ $daysInFutureMonth = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y']));
+
+ if (!$backwards) {
+ $days = ($daysInPastMonth - $past['d']) + $future['d'];
+ } else {
+ $days = ($daysInFutureMonth - $past['d']) + $future['d'];
+ }
+
+ if ($future['m'] != $past['m']) {
+ $months--;
+ }
+ }
+
+ if ($months == 0 && $years >= 1 && $diff < ($years * 31536000)) {
+ $months = 11;
+ $years--;
+ }
+
+ if ($months >= 12) {
+ $years = $years + 1;
+ $months = $months - 12;
+ }
+
+ if ($days >= 7) {
+ $weeks = floor($days / 7);
+ $days = $days - ($weeks * 7);
+ }
+ } else {
+ $years = $months = $weeks = 0;
+ $days = floor($diff / 86400);
+
+ $diff = $diff - ($days * 86400);
+
+ $hours = floor($diff / 3600);
+ $diff = $diff - ($hours * 3600);
+
+ $minutes = floor($diff / 60);
+ $diff = $diff - ($minutes * 60);
+ $seconds = $diff;
+ }
+ $relativeDate = '';
+ $diff = $futureTime - $pastTime;
+
+ if ($diff > abs($now - self::fromString($end))) {
+ $relativeDate = __d('cake', 'on %s', date($format, $inSeconds));
+ } else {
+ if ($years > 0) {
+ $f = $accuracy['year'];
+ } elseif (abs($months) > 0) {
+ $f = $accuracy['month'];
+ } elseif (abs($weeks) > 0) {
+ $f = $accuracy['week'];
+ } elseif (abs($days) > 0) {
+ $f = $accuracy['day'];
+ } elseif (abs($hours) > 0) {
+ $f = $accuracy['hour'];
+ } elseif (abs($minutes) > 0) {
+ $f = $accuracy['minute'];
+ } else {
+ $f = $accuracy['second'];
+ }
+
+ $f = str_replace(array('year', 'month', 'week', 'day', 'hour', 'minute', 'second'), array(1, 2, 3, 4, 5, 6, 7), $f);
+
+ $relativeDate .= $f >= 1 && $years > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d year', '%d years', $years, $years) : '';
+ $relativeDate .= $f >= 2 && $months > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d month', '%d months', $months, $months) : '';
+ $relativeDate .= $f >= 3 && $weeks > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d week', '%d weeks', $weeks, $weeks) : '';
+ $relativeDate .= $f >= 4 && $days > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days) : '';
+ $relativeDate .= $f >= 5 && $hours > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d hour', '%d hours', $hours, $hours) : '';
+ $relativeDate .= $f >= 6 && $minutes > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d minute', '%d minutes', $minutes, $minutes) : '';
+ $relativeDate .= $f >= 7 && $seconds > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d second', '%d seconds', $seconds, $seconds) : '';
+
+ if (!$backwards) {
+ $relativeDate = __d('cake', '%s ago', $relativeDate);
+ }
+ }
+
+ // If now
+ if ($diff == 0) {
+ $relativeDate = __d('cake', 'just now', 'just now');
+ }
+ return $relativeDate;
+ }
+
+/**
+ * Returns true if specified datetime was within the interval specified, else false.
+ *
+ * @param string|integer $timeInterval the numeric value with space then time type.
+ * Example of valid types: 6 hours, 2 days, 1 minute.
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return boolean
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#testing-time
+ */
+ public static function wasWithinLast($timeInterval, $dateString, $timezone = null) {
+ $tmp = str_replace(' ', '', $timeInterval);
+ if (is_numeric($tmp)) {
+ $timeInterval = $tmp . ' ' . __d('cake', 'days');
+ }
+
+ $date = self::fromString($dateString, $timezone);
+ $interval = self::fromString('-' . $timeInterval);
+
+ if ($date >= $interval && $date <= time()) {
+ return true;
+ }
+ return false;
+ }
+
+/**
+ * Returns true if specified datetime is within the interval specified, else false.
+ *
+ * @param string|integer $timeInterval the numeric value with space then time type.
+ * Example of valid types: 6 hours, 2 days, 1 minute.
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return boolean
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#testing-time
+ */
+ public static function isWithinNext($timeInterval, $dateString, $timezone = null) {
+ $tmp = str_replace(' ', '', $timeInterval);
+ if (is_numeric($tmp)) {
+ $timeInterval = $tmp . ' ' . __d('cake', 'days');
+ }
+
+ $date = self::fromString($dateString, $timezone);
+ $interval = self::fromString('+' . $timeInterval);
+
+ if ($date <= $interval && $date >= time()) {
+ return true;
+ }
+ return false;
+ }
+
+/**
+ * Returns gmt as a UNIX timestamp.
+ *
+ * @param integer|string|DateTime $dateString UNIX timestamp, strtotime() valid string or DateTime object
+ * @return integer UNIX timestamp
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function gmt($dateString = null) {
+ if ($dateString != null) {
+ $time = self::fromString($dateString);
+ } else {
+ $time = time();
+ }
+ $hour = intval(date("G", $time));
+ $minute = intval(date("i", $time));
+ $second = intval(date("s", $time));
+ $month = intval(date("n", $time));
+ $day = intval(date("j", $time));
+ $year = intval(date("Y", $time));
+ return gmmktime($hour, $minute, $second, $month, $day, $year);
+ }
+
+/**
+ * Returns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string.
+ * This function also accepts a time string and a format string as first and second parameters.
+ * In that case this function behaves as a wrapper for TimeHelper::i18nFormat()
+ *
+ * ## Examples:
+ * {{{
+ * CakeTime::format('2012-02-15', '%m-%d-%Y'); // returns 02-15-2012
+ * CakeTime::format('2012-02-15 23:01:01', '%c'); // returns preferred date and time based on configured locale
+ * CakeTime::format('0000-00-00', '%d-%m-%Y', 'N/A'); // return N/A becuase an invalid date was passed
+ * CakeTime::format('2012-02-15 23:01:01', '%c', 'N/A', 'America/New_York'); // converts passed date to timezone
+ * }}}
+ *
+ * @param integer|string|DateTime $date UNIX timestamp, strtotime() valid string or DateTime object (or a date format string)
+ * @param integer|string|DateTime $format date format string (or UNIX timestamp, strtotime() valid string or DateTime object)
+ * @param boolean|string $default if an invalid date is passed it will output supplied default value. Pass false if you want raw conversion value
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return string Formatted date string
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function format($date, $format = null, $default = false, $timezone = null) {
+ //Backwards compatible params order
+ $time = self::fromString($format, $timezone);
+ $_time = is_numeric($time) ? false : self::fromString($date, $timezone);
+
+ if (is_numeric($_time) && $time === false) {
+ return self::i18nFormat($_time, $format, $default, $timezone);
+ }
+ if ($time === false && $default !== false) {
+ return $default;
+ }
+ return date($date, $time);
+ }
+
+/**
+ * Returns a formatted date string, given either a UNIX timestamp or a valid strtotime() date string.
+ * It take in account the default date format for the current language if a LC_TIME file is used.
+ *
+ * @param integer|string|DateTime $date UNIX timestamp, strtotime() valid string or DateTime object
+ * @param string $format strftime format string.
+ * @param boolean|string $default if an invalid date is passed it will output supplied default value. Pass false if you want raw conversion value
+ * @param string|DateTimeZone $timezone Timezone string or DateTimeZone object
+ * @return string Formatted and translated date string
+ * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/time.html#formatting
+ */
+ public static function i18nFormat($date, $format = null, $default = false, $timezone = null) {
+ $date = self::fromString($date, $timezone);
+ if ($date === false && $default !== false) {
+ return $default;
+ }
+ if (empty($format)) {
+ $format = '%x';
+ }
+ $format = self::convertSpecifiers($format, $date);
+ return self::_strftime($format, $date);
+ }
+
+/**
+ * Get list of timezone identifiers
+ *
+ * @param integer|string $filter A regex to filter identifer
+ * Or one of DateTimeZone class constants (PHP 5.3 and above)
+ * @param string $country A two-letter ISO 3166-1 compatible country code.
+ * This option is only used when $filter is set to DateTimeZone::PER_COUNTRY (available only in PHP 5.3 and above)
+ * @param boolean $group If true (default value) groups the identifiers list by primary region
+ * @return array List of timezone identifiers
+ * @since 2.2
+ */
+ public static function listTimezones($filter = null, $country = null, $group = true) {
+ $regex = null;
+ if (is_string($filter)) {
+ $regex = $filter;
+ $filter = null;
+ }
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ if ($regex === null) {
+ $regex = '#^((Africa|America|Antartica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)/|UTC)#';
+ }
+ $identifiers = DateTimeZone::listIdentifiers();
+ } else {
+ if ($filter === null) {
+ $filter = DateTimeZone::ALL;
+ }
+ $identifiers = DateTimeZone::listIdentifiers($filter, $country);
+ }
+
+ if ($regex) {
+ foreach ($identifiers as $key => $tz) {
+ if (!preg_match($regex, $tz)) {
+ unset($identifiers[$key]);
+ }
+ }
+ }
+
+ if ($group) {
+ $return = array();
+ foreach ($identifiers as $key => $tz) {
+ $item = explode('/', $tz, 2);
+ if (isset($item[1])) {
+ $return[$item[0]][$tz] = $item[1];
+ } else {
+ $return[$item[0]] = array($tz => $item[0]);
+ }
+ }
+ return $return;
+ } else {
+ return array_combine($identifiers, $identifiers);
+ }
+ }
+
+/**
+ * Multibyte wrapper for strftime.
+ *
+ * Handles utf8_encoding the result of strftime when necessary.
+ *
+ * @param string $format Format string.
+ * @param integer $date Timestamp to format.
+ * @return string formatted string with correct encoding.
+ */
+ protected static function _strftime($format, $date) {
+ $format = strftime($format, $date);
+ $encoding = Configure::read('App.encoding');
+
+ if (!empty($encoding) && $encoding === 'UTF-8') {
+ if (function_exists('mb_check_encoding')) {
+ $valid = mb_check_encoding($format, $encoding);
+ } else {
+ $valid = !Multibyte::checkMultibyte($format);
+ }
+ if (!$valid) {
+ $format = utf8_encode($format);
+ }
+ }
+ return $format;
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/ClassRegistry.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/ClassRegistry.php
new file mode 100644
index 0000000..da7b125
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/ClassRegistry.php
@@ -0,0 +1,368 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+/**
+ * Included libraries.
+ */
+App::uses('Model', 'Model');
+App::uses('AppModel', 'Model');
+App::uses('ConnectionManager', 'Model');
+
+/**
+ * Class Collections.
+ *
+ * A repository for class objects, each registered with a key.
+ * If you try to add an object with the same key twice, nothing will come of it.
+ * If you need a second instance of an object, give it another key.
+ *
+ * @package Cake.Utility
+ */
+class ClassRegistry {
+
+/**
+ * Names of classes with their objects.
+ *
+ * @var array
+ */
+ protected $_objects = array();
+
+/**
+ * Names of class names mapped to the object in the registry.
+ *
+ * @var array
+ */
+ protected $_map = array();
+
+/**
+ * Default constructor parameter settings, indexed by type
+ *
+ * @var array
+ */
+ protected $_config = array();
+
+/**
+ * Return a singleton instance of the ClassRegistry.
+ *
+ * @return ClassRegistry instance
+ */
+ public static function &getInstance() {
+ static $instance = array();
+ if (!$instance) {
+ $instance[0] = new ClassRegistry();
+ }
+ return $instance[0];
+ }
+
+/**
+ * Loads a class, registers the object in the registry and returns instance of the object. ClassRegistry::init()
+ * is used as a factory for models, and handle correct injecting of settings, that assist in testing.
+ *
+ * Examples
+ * Simple Use: Get a Post model instance ```ClassRegistry::init('Post');```
+ *
+ * Expanded: ```array('class' => 'ClassName', 'alias' => 'AliasNameStoredInTheRegistry', 'type' => 'Model');```
+ *
+ * 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
+ * of trying to create an AppModel
+ * @return object 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;
+ if (!isset($class[0])) {
+ $objects = array($class);
+ }
+ } else {
+ $objects = array(array('class' => $class));
+ }
+ $defaults = isset($_this->_config['Model']) ? $_this->_config['Model'] : array();
+ $count = count($objects);
+ $availableDs = array_keys(ConnectionManager::enumConnectionObjects());
+
+ foreach ($objects as $key => $settings) {
+ if (is_array($settings)) {
+ $pluginPath = null;
+ $settings = array_merge($defaults, $settings);
+ $class = $settings['class'];
+
+ list($plugin, $class) = pluginSplit($class);
+ if ($plugin) {
+ $pluginPath = $plugin . '.';
+ }
+
+ if (empty($settings['alias'])) {
+ $settings['alias'] = $class;
+ }
+ $alias = $settings['alias'];
+
+ if ($model = $_this->_duplicate($alias, $class)) {
+ $_this->map($alias, $class);
+ return $model;
+ }
+
+ App::uses($plugin . 'AppModel', $pluginPath . 'Model');
+ App::uses($class, $pluginPath . 'Model');
+
+ if (class_exists($class) || interface_exists($class)) {
+ $reflection = new ReflectionClass($class);
+ if ($reflection->isAbstract() || $reflection->isInterface()) {
+ throw new CakeException(__d('cake_dev', 'Cannot create instance of %s, as it is abstract or is an interface', $class));
+ }
+ $testing = isset($settings['testing']) ? $settings['testing'] : false;
+ if ($testing) {
+ $settings['ds'] = 'test';
+ $defaultProperties = $reflection->getDefaultProperties();
+ if (isset($defaultProperties['useDbConfig'])) {
+ $useDbConfig = $defaultProperties['useDbConfig'];
+ if (in_array('test_' . $useDbConfig, $availableDs)) {
+ $useDbConfig = 'test_' . $useDbConfig;
+ }
+ if (strpos($useDbConfig, 'test') === 0) {
+ $settings['ds'] = $useDbConfig;
+ }
+ }
+ }
+ if ($reflection->getConstructor()) {
+ $instance = $reflection->newInstance($settings);
+ } else {
+ $instance = $reflection->newInstance();
+ }
+ if ($strict) {
+ $instance = ($instance instanceof Model) ? $instance : null;
+ }
+ }
+ if (!isset($instance)) {
+ 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 %1$s class %2$s ', $class, $type), E_USER_WARNING);
+ return $false;
+ }
+ }
+ $_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 $instance;
+ }
+
+/**
+ * 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
+ */
+ public static function addObject($key, $object) {
+ $_this = ClassRegistry::getInstance();
+ $key = Inflector::underscore($key);
+ if (!isset($_this->_objects[$key])) {
+ $_this->_objects[$key] = $object;
+ return true;
+ }
+ return false;
+ }
+
+/**
+ * Remove object which corresponds to given key.
+ *
+ * @param string $key Key of object to remove from registry
+ * @return void
+ */
+ public static function removeObject($key) {
+ $_this = ClassRegistry::getInstance();
+ $key = Inflector::underscore($key);
+ if (isset($_this->_objects[$key])) {
+ unset($_this->_objects[$key]);
+ }
+ }
+
+/**
+ * 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
+ */
+ 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;
+ }
+
+/**
+ * Get all keys from the registry.
+ *
+ * @return array Set of keys stored in registry
+ */
+ public static function keys() {
+ $_this = ClassRegistry::getInstance();
+ return array_keys($_this->_objects);
+ }
+
+/**
+ * Return object which corresponds to given key.
+ *
+ * @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) {
+ $_this = ClassRegistry::getInstance();
+ $key = Inflector::underscore($key);
+ $return = false;
+ if (isset($_this->_objects[$key])) {
+ $return = $_this->_objects[$key];
+ } else {
+ $key = $_this->_getMap($key);
+ if (isset($_this->_objects[$key])) {
+ $return = $_this->_objects[$key];
+ }
+ }
+ return $return;
+ }
+
+/**
+ * Sets the default constructor parameter for an object type
+ *
+ * @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
+ * the previously-set value of $param, or null if not set.
+ */
+ public static function config($type, $param = array()) {
+ $_this = ClassRegistry::getInstance();
+
+ if (empty($param) && is_array($type)) {
+ $param = $type;
+ $type = 'Model';
+ } elseif (is_null($param)) {
+ unset($_this->_config[$type]);
+ } elseif (empty($param) && is_string($type)) {
+ return isset($_this->_config[$type]) ? $_this->_config[$type] : null;
+ }
+ if (isset($_this->_config[$type]['testing'])) {
+ $param['testing'] = true;
+ }
+ $_this->_config[$type] = $param;
+ }
+
+/**
+ * Checks to see if $alias is a duplicate $class Object
+ *
+ * @param string $alias
+ * @param string $class
+ * @return boolean
+ */
+ 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)) {
+ $duplicate = $model;
+ }
+ unset($model);
+ }
+ return $duplicate;
+ }
+
+/**
+ * Add a key name pair to the registry to map name to class in the registry.
+ *
+ * @param string $key Key to include in map
+ * @param string $name Key that is being mapped
+ * @return void
+ */
+ public static function map($key, $name) {
+ $_this = ClassRegistry::getInstance();
+ $key = Inflector::underscore($key);
+ $name = Inflector::underscore($name);
+ if (!isset($_this->_map[$key])) {
+ $_this->_map[$key] = $name;
+ }
+ }
+
+/**
+ * Get all keys from the map in the registry.
+ *
+ * @return array Keys of registry's map
+ */
+ public static function mapKeys() {
+ $_this = ClassRegistry::getInstance();
+ return array_keys($_this->_map);
+ }
+
+/**
+ * Return the name of a class in the registry.
+ *
+ * @param string $key Key to find in map
+ * @return string Mapped value
+ */
+ protected function _getMap($key) {
+ if (isset($this->_map[$key])) {
+ return $this->_map[$key];
+ }
+ }
+
+/**
+ * Flushes all objects from the ClassRegistry.
+ *
+ * @return void
+ */
+ public static function flush() {
+ $_this = ClassRegistry::getInstance();
+ $_this->_objects = array();
+ $_this->_map = array();
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Debugger.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Debugger.php
new file mode 100644
index 0000000..1d7410a
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Debugger.php
@@ -0,0 +1,817 @@
+<?php
+/**
+ * Framework debugging and PHP error-handling class
+ *
+ * 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)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+App::uses('CakeLog', 'Log');
+App::uses('String', 'Utility');
+
+/**
+ * Provide custom logging and error handling.
+ *
+ * Debugger overrides PHP's default error handling to provide stack traces and enhanced logging
+ *
+ * @package Cake.Utility
+ * @link http://book.cakephp.org/2.0/en/development/debugging.html#debugger-class
+ */
+class Debugger {
+
+/**
+ * A list of errors generated by the application.
+ *
+ * @var array
+ */
+ public $errors = array();
+
+/**
+ * The current output format.
+ *
+ * @var string
+ */
+ protected $_outputFormat = 'js';
+
+/**
+ * Templates used when generating trace or error strings. Can be global or indexed by the format
+ * value used in $_outputFormat.
+ *
+ * @var string
+ */
+ protected $_templates = array(
+ 'log' => array(
+ 'trace' => '{:reference} - {:path}, line {:line}',
+ 'error' => "{:error} ({:code}): {:description} in [{:file}, line {:line}]"
+ ),
+ 'js' => array(
+ 'error' => '',
+ 'info' => '',
+ 'trace' => '<pre class="stack-trace">{:trace}</pre>',
+ 'code' => '',
+ 'context' => '',
+ 'links' => array(),
+ 'escapeContext' => true,
+ ),
+ 'html' => array(
+ 'trace' => '<pre class="cake-error trace"><b>Trace</b> <p>{:trace}</p></pre>',
+ 'context' => '<pre class="cake-error context"><b>Context</b> <p>{:context}</p></pre>',
+ 'escapeContext' => true,
+ ),
+ 'txt' => array(
+ 'error' => "{:error}: {:code} :: {:description} on line {:line} of {:path}\n{:info}",
+ 'code' => '',
+ 'info' => ''
+ ),
+ 'base' => array(
+ 'traceLine' => '{:reference} - {:path}, line {:line}',
+ 'trace' => "Trace:\n{:trace}\n",
+ 'context' => "Context:\n{:context}\n",
+ ),
+ 'log' => array(),
+ );
+
+/**
+ * Holds current output data when outputFormat is false.
+ *
+ * @var string
+ */
+ protected $_data = array();
+
+/**
+ * Constructor.
+ *
+ */
+ public function __construct() {
+ $docRef = ini_get('docref_root');
+
+ if (empty($docRef) && function_exists('ini_set')) {
+ ini_set('docref_root', 'http://php.net/');
+ }
+ if (!defined('E_RECOVERABLE_ERROR')) {
+ define('E_RECOVERABLE_ERROR', 4096);
+ }
+
+ $e = '<pre class="cake-error">';
+ $e .= '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-trace\')';
+ $e .= '.style.display = (document.getElementById(\'{:id}-trace\').style.display == ';
+ $e .= '\'none\' ? \'\' : \'none\');"><b>{:error}</b> ({:code})</a>: {:description} ';
+ $e .= '[<b>{:path}</b>, line <b>{:line}</b>]';
+
+ $e .= '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">';
+ $e .= '{:links}{:info}</div>';
+ $e .= '</pre>';
+ $this->_templates['js']['error'] = $e;
+
+ $t = '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">';
+ $t .= '{:context}{:code}{:trace}</div>';
+ $this->_templates['js']['info'] = $t;
+
+ $links = array();
+ $link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-code\')';
+ $link .= '.style.display = (document.getElementById(\'{:id}-code\').style.display == ';
+ $link .= '\'none\' ? \'\' : \'none\')">Code</a>';
+ $links['code'] = $link;
+
+ $link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-context\')';
+ $link .= '.style.display = (document.getElementById(\'{:id}-context\').style.display == ';
+ $link .= '\'none\' ? \'\' : \'none\')">Context</a>';
+ $links['context'] = $link;
+
+ $this->_templates['js']['links'] = $links;
+
+ $this->_templates['js']['context'] = '<pre id="{:id}-context" class="cake-context" ';
+ $this->_templates['js']['context'] .= 'style="display: none;">{:context}</pre>';
+
+ $this->_templates['js']['code'] = '<pre id="{:id}-code" class="cake-code-dump" ';
+ $this->_templates['js']['code'] .= 'style="display: none;">{:code}</pre>';
+
+ $e = '<pre class="cake-error"><b>{:error}</b> ({:code}) : {:description} ';
+ $e .= '[<b>{:path}</b>, line <b>{:line}]</b></pre>';
+ $this->_templates['html']['error'] = $e;
+
+ $this->_templates['html']['context'] = '<pre class="cake-context"><b>Context</b> ';
+ $this->_templates['html']['context'] .= '<p>{:context}</p></pre>';
+ }
+
+/**
+ * Returns a reference to the Debugger singleton object instance.
+ *
+ * @param string $class
+ * @return object
+ */
+ public static function &getInstance($class = null) {
+ static $instance = array();
+ if (!empty($class)) {
+ if (!$instance || strtolower($class) != strtolower(get_class($instance[0]))) {
+ $instance[0] = new $class();
+ }
+ }
+ if (!$instance) {
+ $instance[0] = new Debugger();
+ }
+ return $instance[0];
+ }
+
+/**
+ * Recursively formats and outputs the contents of the supplied variable.
+ *
+ *
+ * @param mixed $var the variable to dump
+ * @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));
+ }
+
+/**
+ * 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
+ * @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));
+ }
+
+/**
+ * Overrides PHP's default error handling.
+ *
+ * @param integer $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 array $context Context
+ * @return boolean true if error was handled
+ * @deprecated This function is superseded by Debugger::outputError()
+ */
+ public static function showError($code, $description, $file = null, $line = null, $context = null) {
+ $self = Debugger::getInstance();
+
+ if (empty($file)) {
+ $file = '[internal]';
+ }
+ if (empty($line)) {
+ $line = '??';
+ }
+ $path = self::trimPath($file);
+
+ $info = compact('code', 'description', 'file', 'line');
+ if (!in_array($info, $self->errors)) {
+ $self->errors[] = $info;
+ } else {
+ return;
+ }
+
+ switch ($code) {
+ case E_PARSE:
+ case E_ERROR:
+ case E_CORE_ERROR:
+ case E_COMPILE_ERROR:
+ case E_USER_ERROR:
+ $error = 'Fatal Error';
+ $level = LOG_ERR;
+ break;
+ case E_WARNING:
+ case E_USER_WARNING:
+ case E_COMPILE_WARNING:
+ case E_RECOVERABLE_ERROR:
+ $error = 'Warning';
+ $level = LOG_WARNING;
+ break;
+ case E_NOTICE:
+ case E_USER_NOTICE:
+ $error = 'Notice';
+ $level = LOG_NOTICE;
+ break;
+ case E_DEPRECATED:
+ case E_USER_DEPRECATED:
+ $error = 'Deprecated';
+ $level = LOG_NOTICE;
+ break;
+ default:
+ return;
+ break;
+ }
+
+ $data = compact(
+ 'level', 'error', 'code', 'description', 'file', 'path', 'line', 'context'
+ );
+ echo $self->outputError($data);
+
+ if ($error == 'Fatal Error') {
+ exit();
+ }
+ return true;
+ }
+
+/**
+ * Outputs a stack trace based on the supplied options.
+ *
+ * ### 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 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
+ *
+ * @param array $options Format for outputting stack trace
+ * @return mixed Formatted stack trace
+ * @link http://book.cakephp.org/2.0/en/development/debugging.html#Debugger::trace
+ */
+ public static function trace($options = array()) {
+ $self = Debugger::getInstance();
+ $defaults = array(
+ 'depth' => 999,
+ 'format' => $self->_outputFormat,
+ 'args' => false,
+ 'start' => 0,
+ 'scope' => null,
+ 'exclude' => array('call_user_func_array', 'trigger_error')
+ );
+ $options = Hash::merge($defaults, $options);
+
+ $backtrace = debug_backtrace();
+ $count = count($backtrace);
+ $back = array();
+
+ $_trace = array(
+ 'line' => '??',
+ 'file' => '[internal]',
+ 'class' => null,
+ 'function' => '[main]'
+ );
+
+ for ($i = $options['start']; $i < $count && $i < $options['depth']; $i++) {
+ $trace = array_merge(array('file' => '[internal]', 'line' => '??'), $backtrace[$i]);
+ $signature = $reference = '[main]';
+
+ if (isset($backtrace[$i + 1])) {
+ $next = array_merge($_trace, $backtrace[$i + 1]);
+ $signature = $reference = $next['function'];
+
+ if (!empty($next['class'])) {
+ $signature = $next['class'] . '::' . $next['function'];
+ $reference = $signature . '(';
+ if ($options['args'] && isset($next['args'])) {
+ $args = array();
+ foreach ($next['args'] as $arg) {
+ $args[] = Debugger::exportVar($arg);
+ }
+ $reference .= join(', ', $args);
+ }
+ $reference .= ')';
+ }
+ }
+ if (in_array($signature, $options['exclude'])) {
+ continue;
+ }
+ if ($options['format'] == 'points' && $trace['file'] != '[internal]') {
+ $back[] = array('file' => $trace['file'], 'line' => $trace['line']);
+ } elseif ($options['format'] == 'array') {
+ $back[] = $trace;
+ } else {
+ if (isset($self->_templates[$options['format']]['traceLine'])) {
+ $tpl = $self->_templates[$options['format']]['traceLine'];
+ } else {
+ $tpl = $self->_templates['base']['traceLine'];
+ }
+ $trace['path'] = self::trimPath($trace['file']);
+ $trace['reference'] = $reference;
+ unset($trace['object'], $trace['args']);
+ $back[] = String::insert($tpl, $trace, array('before' => '{:', 'after' => '}'));
+ }
+ }
+
+ if ($options['format'] == 'array' || $options['format'] == 'points') {
+ return $back;
+ }
+ return implode("\n", $back);
+ }
+
+/**
+ * Shortens file paths by replacing the application base path with 'APP', and the CakePHP core
+ * path with 'CORE'.
+ *
+ * @param string $path Path to shorten
+ * @return string Normalized path
+ */
+ public static function trimPath($path) {
+ if (!defined('CAKE_CORE_INCLUDE_PATH') || !defined('APP')) {
+ return $path;
+ }
+
+ if (strpos($path, APP) === 0) {
+ return str_replace(APP, 'APP' . DS, $path);
+ } elseif (strpos($path, CAKE_CORE_INCLUDE_PATH) === 0) {
+ return str_replace(CAKE_CORE_INCLUDE_PATH, 'CORE', $path);
+ } elseif (strpos($path, ROOT) === 0) {
+ return str_replace(ROOT, 'ROOT', $path);
+ }
+
+ if (strpos($path, CAKE) === 0) {
+ return str_replace($corePath, 'CORE' . DS, $path);
+ }
+ return $path;
+ }
+
+/**
+ * Grabs an excerpt from a file and highlights a given line of code.
+ *
+ * Usage:
+ *
+ * `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
+ * 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
+ * @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
+ */
+ public static function excerpt($file, $line, $context = 2) {
+ $lines = array();
+ if (!file_exists($file)) {
+ return array();
+ }
+ $data = @explode("\n", file_get_contents($file));
+
+ if (empty($data) || !isset($data[$line])) {
+ return;
+ }
+ for ($i = $line - ($context + 1); $i < $line + $context; $i++) {
+ if (!isset($data[$i])) {
+ continue;
+ }
+ $string = str_replace(array("\r\n", "\n"), "", self::_highlight($data[$i]));
+ if ($i == $line) {
+ $lines[] = '<span class="code-highlight">' . $string . '</span>';
+ } else {
+ $lines[] = $string;
+ }
+ }
+ return $lines;
+ }
+
+/**
+ * Wraps the highlight_string funciton 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
+ * @return string
+ */
+ protected static function _highlight($str) {
+ static $supportHighlight = null;
+ if (!$supportHighlight && function_exists('hphp_log')) {
+ $supportHighlight = false;
+ return htmlentities($str);
+ }
+ $supportHighlight = true;
+ return highlight_string($str, true);
+ }
+
+/**
+ * Converts a variable to a string for debug output.
+ *
+ * *Note:* The following keys will have their contents
+ * replaced with `*****`:
+ *
+ * - password
+ * - login
+ * - 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.
+ * @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);
+ }
+
+/**
+ * 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.
+ * @return string The dumped variable.
+ */
+ protected static function _export($var, $depth, $indent) {
+ switch (self::getType($var)) {
+ case 'boolean':
+ return ($var) ? 'true' : 'false';
+ break;
+ case 'integer':
+ return '(int) ' . $var;
+ case 'float':
+ return '(float) ' . $var;
+ break;
+ case 'string':
+ if (trim($var) == '') {
+ return "''";
+ }
+ return "'" . $var . "'";
+ break;
+ case 'array':
+ return self::_array($var, $depth - 1, $indent + 1);
+ break;
+ case 'resource':
+ return strtolower(gettype($var));
+ break;
+ case 'null':
+ return 'null';
+ default:
+ return self::_object($var, $depth - 1, $indent + 1);
+ break;
+ }
+ }
+
+/**
+ * Export an array type object. Filters out keys used in datasource configuration.
+ *
+ * The following keys are replaced with ***'s
+ *
+ * - password
+ * - login
+ * - 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.
+ * @return string Exported array.
+ */
+ protected static function _array(array $var, $depth, $indent) {
+ $secrets = array(
+ 'password' => '*****',
+ 'login' => '*****',
+ 'host' => '*****',
+ 'database' => '*****',
+ 'port' => '*****',
+ 'prefix' => '*****',
+ 'schema' => '*****'
+ );
+ $replace = array_intersect_key($secrets, $var);
+ $var = $replace + $var;
+
+ $out = "array(";
+ $n = $break = $end = null;
+ if (!empty($var)) {
+ $n = "\n";
+ $break = "\n" . str_repeat("\t", $indent);
+ $end = "\n" . str_repeat("\t", $indent - 1);
+ }
+ $vars = array();
+
+ if ($depth >= 0) {
+ foreach ($var as $key => $val) {
+ $vars[] = $break . self::exportVar($key) .
+ ' => ' .
+ self::_export($val, $depth, $indent);
+ }
+ } else {
+ $vars[] = $break . '[maximum depth reached]';
+ }
+ return $out . implode(',', $vars) . $end . ')';
+ }
+
+/**
+ * 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.
+ * @return string
+ * @see Debugger::exportVar()
+ */
+ protected static function _object($var, $depth, $indent) {
+ $out = '';
+ $props = array();
+
+ $className = get_class($var);
+ $out .= 'object(' . $className . ') {';
+
+ if ($depth > 0) {
+ $end = "\n" . str_repeat("\t", $indent - 1);
+ $break = "\n" . str_repeat("\t", $indent);
+ $objectVars = get_object_vars($var);
+ foreach ($objectVars as $key => $value) {
+ $value = self::_export($value, $depth - 1, $indent);
+ $props[] = "$key => " . $value;
+ }
+ $out .= $break . implode($break, $props) . $end;
+ }
+ $out .= '}';
+ return $out;
+ }
+
+/**
+ * Get/Set the output format for Debugger error rendering.
+ *
+ * @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.
+ * @throws CakeException when choosing a format that doesn't exist.
+ */
+ public static function outputAs($format = null) {
+ $self = Debugger::getInstance();
+ if ($format === null) {
+ return $self->_outputFormat;
+ }
+ if ($format !== false && !isset($self->_templates[$format])) {
+ throw new CakeException(__d('cake_dev', 'Invalid Debugger output format.'));
+ }
+ $self->_outputFormat = $format;
+ }
+
+/**
+ * Add an output format or update a format in 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.
+ * An error formatter can have the following keys:
+ *
+ * - 'error' - Used for the container for the error message. Gets the following template
+ * variables: `id`, `error`, `code`, `description`, `path`, `line`, `links`, `info`
+ * - 'info' - A combination of `code`, `context` and `trace`. Will be set with
+ * the contents of the other template keys.
+ * - 'trace' - The container for a stack trace. Gets the following template
+ * variables: `trace`
+ * - 'context' - The container element for the context variables.
+ * 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
+ * example.
+ * - 'traceLine' - Used for creating lines in the stacktrace. Gets the following
+ * template variables: `reference`, `path`, `line`
+ *
+ * Alternatively if you want to use a custom callback to do all the formatting, you can use
+ * the callback key, and provide a callable:
+ *
+ * `Debugger::addFormat('custom', array('callback' => array($foo, 'outputError'));`
+ *
+ * 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`
+ * 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
+ * straight HTML output, or 'txt' for unformatted text.
+ * @param array $strings Template strings, or a callback to be used for the output format.
+ * @return The resulting format string set.
+ */
+ public static function addFormat($format, array $strings) {
+ $self = Debugger::getInstance();
+ if (isset($self->_templates[$format])) {
+ if (isset($strings['links'])) {
+ $self->_templates[$format]['links'] = array_merge(
+ $self->_templates[$format]['links'],
+ $strings['links']
+ );
+ unset($strings['links']);
+ }
+ $self->_templates[$format] = array_merge($self->_templates[$format], $strings);
+ } else {
+ $self->_templates[$format] = $strings;
+ }
+ return $self->_templates[$format];
+ }
+
+/**
+ * Switches output format, updates format strings.
+ * Can be used to switch the active output format:
+ *
+ * @param string $format Format to use, including 'js' for JavaScript-enhanced HTML, 'html' for
+ * 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
+ * in 3.0
+ */
+ public function output($format = null, $strings = array()) {
+ $self = Debugger::getInstance();
+ $data = null;
+
+ if (is_null($format)) {
+ return Debugger::outputAs();
+ }
+
+ if (!empty($strings)) {
+ return Debugger::addFormat($format, $strings);
+ }
+
+ if ($format === true && !empty($self->_data)) {
+ $data = $self->_data;
+ $self->_data = array();
+ $format = false;
+ }
+ Debugger::outputAs($format);
+ return $data;
+ }
+
+/**
+ * Takes a processed array of data from an error and displays it in the chosen format.
+ *
+ * @param string $data
+ * @return void
+ */
+ public function outputError($data) {
+ $defaults = array(
+ 'level' => 0,
+ 'error' => 0,
+ 'code' => 0,
+ 'description' => '',
+ 'file' => '',
+ 'line' => 0,
+ 'context' => array(),
+ 'start' => 2,
+ );
+ $data += $defaults;
+
+ $files = $this->trace(array('start' => $data['start'], 'format' => 'points'));
+ $code = '';
+ if (isset($files[1]['file'])) {
+ $code = $this->excerpt($files[1]['file'], $files[1]['line'] - 1, 1);
+ }
+ $trace = $this->trace(array('start' => $data['start'], 'depth' => '20'));
+ $insertOpts = array('before' => '{:', 'after' => '}');
+ $context = array();
+ $links = array();
+ $info = '';
+
+ foreach ((array)$data['context'] as $var => $value) {
+ $context[] = "\${$var} = " . $this->exportVar($value, 1);
+ }
+
+ switch ($this->_outputFormat) {
+ case false:
+ $this->_data[] = compact('context', 'trace') + $data;
+ return;
+ case 'log':
+ $this->log(compact('context', 'trace') + $data);
+ return;
+ }
+
+ $data['trace'] = $trace;
+ $data['id'] = 'cakeErr' . uniqid();
+ $tpl = array_merge($this->_templates['base'], $this->_templates[$this->_outputFormat]);
+
+ if (isset($tpl['links'])) {
+ foreach ($tpl['links'] as $key => $val) {
+ $links[$key] = String::insert($val, $data, $insertOpts);
+ }
+ }
+
+ if (!empty($tpl['escapeContext'])) {
+ $context = h($context);
+ }
+
+ $infoData = compact('code', 'context', 'trace');
+ foreach ($infoData as $key => $value) {
+ if (empty($value) || !isset($tpl[$key])) {
+ continue;
+ }
+ if (is_array($value)) {
+ $value = join("\n", $value);
+ }
+ $info .= String::insert($tpl[$key], array($key => $value) + $data, $insertOpts);
+ }
+ $links = join(' ', $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);
+ }
+
+/**
+ * Get the type of the given variable. Will return the classname
+ * for objects.
+ *
+ * @param mixed $var The variable to get the type of
+ * @return string The type of variable.
+ */
+ public static function getType($var) {
+ if (is_object($var)) {
+ return get_class($var);
+ }
+ if (is_null($var)) {
+ return 'null';
+ }
+ if (is_string($var)) {
+ return 'string';
+ }
+ if (is_array($var)) {
+ return 'array';
+ }
+ if (is_int($var)) {
+ return 'integer';
+ }
+ if (is_bool($var)) {
+ return 'boolean';
+ }
+ if (is_float($var)) {
+ return 'float';
+ }
+ if (is_resource($var)) {
+ return 'resource';
+ }
+ return 'unknown';
+ }
+
+/**
+ * Verifies that the application's salt and cipher seed value has been changed from the default value.
+ *
+ * @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.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);
+ }
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/File.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/File.php
new file mode 100644
index 0000000..6fe9399
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/File.php
@@ -0,0 +1,568 @@
+<?php
+/**
+ * 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)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+App::uses('Folder', 'Utility');
+
+/**
+ * Convenience class for reading, writing and appending to files.
+ *
+ * @package Cake.Utility
+ */
+class File {
+
+/**
+ * Folder object of the File
+ *
+ * @var Folder
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$Folder
+ */
+ public $Folder = null;
+
+/**
+ * Filename
+ *
+ * @var string
+ * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$name
+ */
+ public $name = null;
+
+/**
+ * File info
+ *
+ * @var array
+ * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$info
+ */
+ public $info = array();
+
+/**
+ * Holds the file handler resource if the file is opened
+ *
+ * @var resource
+ * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$handle
+ */
+ public $handle = null;
+
+/**
+ * Enable locking for file reading and writing
+ *
+ * @var boolean
+ * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$lock
+ */
+ public $lock = null;
+
+/**
+ * Path property
+ *
+ * Current file's absolute path
+ *
+ * @var mixed null
+ * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$path
+ */
+ public $path = null;
+
+/**
+ * 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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File
+ */
+ public function __construct($path, $create = false, $mode = 0755) {
+ $this->Folder = new Folder(dirname($path), $create, $mode);
+ if (!is_dir($path)) {
+ $this->name = basename($path);
+ }
+ $this->pwd();
+ $create && !$this->exists() && $this->safe($path) && $this->create();
+ }
+
+/**
+ * Closes the current file if it is opened
+ *
+ */
+ public function __destruct() {
+ $this->close();
+ }
+
+/**
+ * Creates the File.
+ *
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::create
+ */
+ public function create() {
+ $dir = $this->Folder->pwd();
+ if (is_dir($dir) && is_writable($dir) && !$this->exists()) {
+ if (touch($this->path)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+/**
+ * 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
+ * @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;
+ }
+ }
+
+ $this->handle = fopen($this->path, $mode);
+ if (is_resource($this->handle)) {
+ return true;
+ }
+ return false;
+ }
+
+/**
+ * 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
+ * @return mixed string on success, false on failure
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::read
+ */
+ public function read($bytes = false, $mode = 'rb', $force = false) {
+ if ($bytes === false && $this->lock === null) {
+ return file_get_contents($this->path);
+ }
+ if ($this->open($mode, $force) === false) {
+ return false;
+ }
+ if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
+ return false;
+ }
+ if (is_int($bytes)) {
+ return fread($this->handle, $bytes);
+ }
+
+ $data = '';
+ while (!feof($this->handle)) {
+ $data .= fgets($this->handle, 4096);
+ }
+
+ if ($this->lock !== null) {
+ flock($this->handle, LOCK_UN);
+ }
+ if ($bytes === false) {
+ $this->close();
+ }
+ return trim($data);
+ }
+
+/**
+ * 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
+ * @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
+ */
+ public function offset($offset = false, $seek = SEEK_SET) {
+ if ($offset === false) {
+ if (is_resource($this->handle)) {
+ return ftell($this->handle);
+ }
+ } elseif ($this->open() === true) {
+ return fseek($this->handle, $offset, $seek) === 0;
+ }
+ return false;
+ }
+
+/**
+ * Prepares a 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
+ * @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) {
+ $lineBreak = "\r\n";
+ }
+ return strtr($data, array("\r\n" => $lineBreak, "\n" => $lineBreak, "\r" => $lineBreak));
+ }
+
+/**
+ * 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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::write
+ */
+ public function write($data, $mode = 'w', $force = false) {
+ $success = false;
+ if ($this->open($mode, $force) === true) {
+ if ($this->lock !== null) {
+ if (flock($this->handle, LOCK_EX) === false) {
+ return false;
+ }
+ }
+
+ if (fwrite($this->handle, $data) !== false) {
+ $success = true;
+ }
+ if ($this->lock !== null) {
+ flock($this->handle, LOCK_UN);
+ }
+ }
+ return $success;
+ }
+
+/**
+ * Append given data string to this File.
+ *
+ * @param string $data Data to write
+ * @param string $force force the file to open
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::append
+ */
+ public function append($data, $force = false) {
+ return $this->write($data, 'a', $force);
+ }
+
+/**
+ * Closes the current file if it is opened.
+ *
+ * @return boolean 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() {
+ if (!is_resource($this->handle)) {
+ return true;
+ }
+ return fclose($this->handle);
+ }
+
+/**
+ * Deletes the File.
+ *
+ * @return boolean 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;
+ }
+ if ($this->exists()) {
+ return unlink($this->path);
+ }
+ return false;
+ }
+
+/**
+ * Returns the File info as an array with the following keys:
+ *
+ * - dirname
+ * - basename
+ * - extension
+ * - filename
+ * - filesize
+ * - mime
+ *
+ * @return array File information.
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::info
+ */
+ public function info() {
+ if ($this->info == null) {
+ $this->info = pathinfo($this->path);
+ }
+ if (!isset($this->info['filename'])) {
+ $this->info['filename'] = $this->name();
+ }
+ if (!isset($this->info['filesize'])) {
+ $this->info['filesize'] = $this->size();
+ }
+ if (!isset($this->info['mime'])) {
+ $this->info['mime'] = $this->mime();
+ }
+ return $this->info;
+ }
+
+/**
+ * Returns 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) {
+ $this->info();
+ }
+ if (isset($this->info['extension'])) {
+ return $this->info['extension'];
+ }
+ return false;
+ }
+
+/**
+ * Returns 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) {
+ $this->info();
+ }
+ if (isset($this->info['extension'])) {
+ return basename($this->name, '.' . $this->info['extension']);
+ } elseif ($this->name) {
+ return $this->name;
+ }
+ return false;
+ }
+
+/**
+ * makes filename 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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::safe
+ */
+ public function safe($name = null, $ext = null) {
+ if (!$name) {
+ $name = $this->name;
+ }
+ if (!$ext) {
+ $ext = $this->ext();
+ }
+ return preg_replace("/(?:[^\w\.-]+)/", "_", basename($name, $ext));
+ }
+
+/**
+ * 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()}
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::md5
+ */
+ public function md5($maxsize = 5) {
+ if ($maxsize === true) {
+ return md5_file($this->path);
+ }
+
+ $size = $this->size();
+ if ($size && $size < ($maxsize * 1024) * 1024) {
+ return md5_file($this->path);
+ }
+
+ return false;
+ }
+
+/**
+ * Returns the full path of the File.
+ *
+ * @return string Full path to 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;
+ }
+ return $this->path;
+ }
+
+/**
+ * Returns true if the File exists.
+ *
+ * @return boolean 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() {
+ return (file_exists($this->path) && is_file($this->path));
+ }
+
+/**
+ * Returns the "chmod" (permissions) of the File.
+ *
+ * @return string Permissions for the file
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::perms
+ */
+ public function perms() {
+ if ($this->exists()) {
+ return substr(sprintf('%o', fileperms($this->path)), -4);
+ }
+ return false;
+ }
+
+/**
+ * Returns the Filesize
+ *
+ * @return integer 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() {
+ if ($this->exists()) {
+ return filesize($this->path);
+ }
+ return false;
+ }
+
+/**
+ * Returns true if the File is writable.
+ *
+ * @return boolean true if its writable, false otherwise
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::writable
+ */
+ public function writable() {
+ return is_writable($this->path);
+ }
+
+/**
+ * Returns true if the File is executable.
+ *
+ * @return boolean true if its executable, false otherwise
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::executable
+ */
+ public function executable() {
+ return is_executable($this->path);
+ }
+
+/**
+ * Returns true if the File is readable.
+ *
+ * @return boolean 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() {
+ return is_readable($this->path);
+ }
+
+/**
+ * Returns the File's owner.
+ *
+ * @return integer the Fileowner
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::owner
+ */
+ public function owner() {
+ if ($this->exists()) {
+ return fileowner($this->path);
+ }
+ return false;
+ }
+
+/**
+ * Returns the File's group.
+ *
+ * @return integer the Filegroup
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::group
+ */
+ public function group() {
+ if ($this->exists()) {
+ return filegroup($this->path);
+ }
+ return false;
+ }
+
+/**
+ * Returns last access time.
+ *
+ * @return integer timestamp Timestamp of last access time
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::lastAccess
+ */
+ public function lastAccess() {
+ if ($this->exists()) {
+ return fileatime($this->path);
+ }
+ return false;
+ }
+
+/**
+ * Returns last modified time.
+ *
+ * @return integer timestamp Timestamp of last modification
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::lastChange
+ */
+ public function lastChange() {
+ if ($this->exists()) {
+ return filemtime($this->path);
+ }
+ return false;
+ }
+
+/**
+ * Returns the current folder.
+ *
+ * @return Folder Current folder
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::copy
+ */
+ public function copy($dest, $overwrite = true) {
+ if (!$this->exists() || is_file($dest) && !$overwrite) {
+ return false;
+ }
+ return copy($this->path, $dest);
+ }
+
+/**
+ * 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.
+ */
+ public function mime() {
+ if (!$this->exists()) {
+ return false;
+ }
+ if (function_exists('finfo_open')) {
+ $finfo = finfo_open(FILEINFO_MIME);
+ list($type, $charset) = explode(';', finfo_file($finfo, $this->pwd()));
+ return $type;
+ } elseif (function_exists('mime_content_type')) {
+ return mime_content_type($this->pwd());
+ }
+ return false;
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Folder.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Folder.php
new file mode 100644
index 0000000..08895bf
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Folder.php
@@ -0,0 +1,780 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+/**
+ * Folder structure browser, lists folders and files.
+ * Provides an Object interface for Common directory related tasks.
+ *
+ * @package Cake.Utility
+ */
+class Folder {
+
+/**
+ * Path to Folder.
+ *
+ * @var string
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::$path
+ */
+ public $path = null;
+
+/**
+ * Sortedness. Whether or not list results
+ * should be sorted by name.
+ *
+ * @var boolean
+ * @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.
+ *
+ * @var integer
+ * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::$mode
+ */
+ public $mode = 0755;
+
+/**
+ * Holds messages from last method.
+ *
+ * @var array
+ */
+ protected $_messages = array();
+
+/**
+ * Holds errors from last method.
+ *
+ * @var array
+ */
+ protected $_errors = array();
+
+/**
+ * Holds array of complete directory paths.
+ *
+ * @var array
+ */
+ protected $_directories;
+
+/**
+ * Holds array of complete file paths.
+ *
+ * @var array
+ */
+ protected $_files;
+
+/**
+ * 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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder
+ */
+ public function __construct($path = false, $create = false, $mode = false) {
+ if (empty($path)) {
+ $path = TMP;
+ }
+ if ($mode) {
+ $this->mode = $mode;
+ }
+
+ if (!file_exists($path) && $create === true) {
+ $this->create($path, $this->mode);
+ }
+ if (!Folder::isAbsolute($path)) {
+ $path = realpath($path);
+ }
+ if (!empty($path)) {
+ $this->cd($path);
+ }
+ }
+
+/**
+ * Return current path.
+ *
+ * @return string Current path
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::pwd
+ */
+ public function pwd() {
+ return $this->path;
+ }
+
+/**
+ * Change directory to $path.
+ *
+ * @param string $path Path to the directory to change to
+ * @return string The new path. Returns false on failure
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::cd
+ */
+ public function cd($path) {
+ $path = $this->realpath($path);
+ if (is_dir($path)) {
+ return $this->path = $path;
+ }
+ return false;
+ }
+
+/**
+ * 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
+ * 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
+ * @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) {
+ $dirs = $files = array();
+
+ if (!$this->pwd()) {
+ return array($dirs, $files);
+ }
+ if (is_array($exceptions)) {
+ $exceptions = array_flip($exceptions);
+ }
+ $skipHidden = isset($exceptions['.']) || $exceptions === true;
+
+ try {
+ $iterator = new DirectoryIterator($this->path);
+ } catch (Exception $e) {
+ return array($dirs, $files);
+ }
+
+ foreach ($iterator as $item) {
+ if ($item->isDot()) {
+ continue;
+ }
+ $name = $item->getFileName();
+ if ($skipHidden && $name[0] === '.' || isset($exceptions[$name])) {
+ continue;
+ }
+ if ($fullPath) {
+ $name = $item->getPathName();
+ }
+ if ($item->isDir()) {
+ $dirs[] = $name;
+ } else {
+ $files[] = $name;
+ }
+ }
+ if ($sort || $this->sort) {
+ sort($dirs);
+ sort($files);
+ }
+ return array($dirs, $files);
+ }
+
+/**
+ * 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.
+ * @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);
+ return array_values(preg_grep('/^' . $regexpPattern . '$/i', $files));
+ }
+
+/**
+ * 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.
+ * @return array Files matching $pattern
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::findRecursive
+ */
+ public function findRecursive($pattern = '.*', $sort = false) {
+ if (!$this->pwd()) {
+ return array();
+ }
+ $startsOn = $this->path;
+ $out = $this->_findRecursive($pattern, $sort);
+ $this->cd($startsOn);
+ return $out;
+ }
+
+/**
+ * Private helper function for findRecursive.
+ *
+ * @param string $pattern Pattern to match against
+ * @param boolean $sort Whether results should be sorted.
+ * @return array Files matching pattern
+ */
+ protected function _findRecursive($pattern, $sort = false) {
+ list($dirs, $files) = $this->read($sort);
+ $found = array();
+
+ foreach ($files as $file) {
+ if (preg_match('/^' . $pattern . '$/i', $file)) {
+ $found[] = Folder::addPathElement($this->path, $file);
+ }
+ }
+ $start = $this->path;
+
+ foreach ($dirs as $dir) {
+ $this->cd(Folder::addPathElement($start, $dir));
+ $found = array_merge($found, $this->findRecursive($pattern, $sort));
+ }
+ return $found;
+ }
+
+/**
+ * Returns true if given $path is a Windows path.
+ *
+ * @param string $path Path to check
+ * @return boolean 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) == '\\\\');
+ }
+
+/**
+ * Returns true if given $path is an absolute path.
+ *
+ * @param string $path Path to check
+ * @return boolean 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) == '\\\\');
+ }
+
+/**
+ * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
+ *
+ * @param string $path Path to check
+ * @return string Set of slashes ("\\" or "/")
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::normalizePath
+ */
+ public static function normalizePath($path) {
+ return Folder::correctSlashFor($path);
+ }
+
+/**
+ * Returns a correct set of slashes for given $path. (\\ for Windows paths and / for other paths.)
+ *
+ * @param string $path Path to check
+ * @return string Set of slashes ("\\" or "/")
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::correctSlashFor
+ */
+ public static function correctSlashFor($path) {
+ return (Folder::isWindowsPath($path)) ? '\\' : '/';
+ }
+
+/**
+ * Returns $path with added terminating slash (corrected for Windows or other OS).
+ *
+ * @param string $path Path to check
+ * @return string Path with ending slash
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::slashTerm
+ */
+ public static function slashTerm($path) {
+ if (Folder::isSlashTerm($path)) {
+ return $path;
+ }
+ return $path . Folder::correctSlashFor($path);
+ }
+
+/**
+ * Returns $path with $element added, with correct slash in-between.
+ *
+ * @param string $path Path
+ * @param string $element Element to and 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;
+ }
+
+/**
+ * Returns true if the File is in a given CakePath.
+ *
+ * @param string $path The path to check.
+ * @return boolean
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::inCakePath
+ */
+ public function inCakePath($path = '') {
+ $dir = substr(Folder::slashTerm(ROOT), 0, -1);
+ $newdir = $dir . $path;
+
+ return $this->inPath($newdir);
+ }
+
+/**
+ * Returns true if the File is in 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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::inPath
+ */
+ public function inPath($path = '', $reverse = false) {
+ $dir = Folder::slashTerm($path);
+ $current = Folder::slashTerm($this->pwd());
+
+ if (!$reverse) {
+ $return = preg_match('/^(.*)' . preg_quote($dir, '/') . '(.*)/', $current);
+ } else {
+ $return = preg_match('/^(.*)' . preg_quote($current, '/') . '(.*)/', $dir);
+ }
+ return (bool)$return;
+ }
+
+/**
+ * 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
+ * @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()) {
+ if (!$mode) {
+ $mode = $this->mode;
+ }
+
+ if ($recursive === false && is_dir($path)) {
+ if (@chmod($path, intval($mode, 8))) {
+ $this->_messages[] = __d('cake_dev', '%s changed to %s', $path, $mode);
+ return true;
+ }
+
+ $this->_errors[] = __d('cake_dev', '%s NOT changed to %s', $path, $mode);
+ return false;
+ }
+
+ if (is_dir($path)) {
+ $paths = $this->tree($path);
+
+ foreach ($paths as $type) {
+ foreach ($type as $key => $fullpath) {
+ $check = explode(DS, $fullpath);
+ $count = count($check);
+
+ if (in_array($check[$count - 1], $exceptions)) {
+ continue;
+ }
+
+ if (@chmod($fullpath, intval($mode, 8))) {
+ $this->_messages[] = __d('cake_dev', '%s changed to %s', $fullpath, $mode);
+ } else {
+ $this->_errors[] = __d('cake_dev', '%s NOT changed to %s', $fullpath, $mode);
+ }
+ }
+ }
+
+ if (empty($this->_errors)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+/**
+ * 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
+ * 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) {
+ $path = $this->path;
+ }
+ $files = array();
+ $directories = array($path);
+
+ if (is_array($exceptions)) {
+ $exceptions = array_flip($exceptions);
+ }
+ $skipHidden = false;
+ if ($exceptions === true) {
+ $skipHidden = true;
+ } elseif (isset($exceptions['.'])) {
+ $skipHidden = true;
+ unset($exceptions['.']);
+ }
+
+ try {
+ $directory = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::KEY_AS_PATHNAME | RecursiveDirectoryIterator::CURRENT_AS_SELF);
+ $iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::SELF_FIRST);
+ } catch (Exception $e) {
+ if ($type === null) {
+ return array(array(), array());
+ }
+ return array();
+ }
+
+ foreach ($iterator as $itemPath => $fsIterator) {
+ if ($skipHidden) {
+ $subPathName = $fsIterator->getSubPathname();
+ if ($subPathName{0} == '.' || strpos($subPathName, DS . '.') !== false) {
+ continue;
+ }
+ }
+ $item = $fsIterator->current();
+ if (!empty($exceptions) && isset($exceptions[$item->getFilename()])) {
+ continue;
+ }
+
+ if ($item->isFile()) {
+ $files[] = $itemPath;
+ } elseif ($item->isDir() && !$item->isDot()) {
+ $directories[] = $itemPath;
+ }
+ }
+ if ($type === null) {
+ return array($directories, $files);
+ }
+ if ($type === 'dir') {
+ return $directories;
+ }
+ return $files;
+ }
+
+/**
+ * Create a directory structure recursively. Can be used to create
+ * deep path structures like `/foo/bar/baz/shoe/horn`
+ *
+ * @param string $pathname The directory structure to create
+ * @param integer $mode octal value 0755
+ * @return boolean 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) {
+ if (is_dir($pathname) || empty($pathname)) {
+ return true;
+ }
+
+ if (!$mode) {
+ $mode = $this->mode;
+ }
+
+ if (is_file($pathname)) {
+ $this->_errors[] = __d('cake_dev', '%s is a file', $pathname);
+ return false;
+ }
+ $pathname = rtrim($pathname, DS);
+ $nextPathname = substr($pathname, 0, strrpos($pathname, DS));
+
+ if ($this->create($nextPathname, $mode)) {
+ if (!file_exists($pathname)) {
+ $old = umask(0);
+ if (mkdir($pathname, $mode)) {
+ 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;
+ }
+ }
+ }
+ return false;
+ }
+
+/**
+ * Returns the size in bytes of this Folder and its contents.
+ *
+ * @return integer 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() {
+ $size = 0;
+ $directory = Folder::slashTerm($this->path);
+ $stack = array($directory);
+ $count = count($stack);
+ for ($i = 0, $j = $count; $i < $j; ++$i) {
+ if (is_file($stack[$i])) {
+ $size += filesize($stack[$i]);
+ } elseif (is_dir($stack[$i])) {
+ $dir = dir($stack[$i]);
+ if ($dir) {
+ while (false !== ($entry = $dir->read())) {
+ if ($entry === '.' || $entry === '..') {
+ continue;
+ }
+ $add = $stack[$i] . $entry;
+
+ if (is_dir($stack[$i] . $entry)) {
+ $add = Folder::slashTerm($add);
+ }
+ $stack[] = $add;
+ }
+ $dir->close();
+ }
+ }
+ $j = count($stack);
+ }
+ return $size;
+ }
+
+/**
+ * Recursively Remove directories if the system allows.
+ *
+ * @param string $path Path of directory to delete
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::delete
+ */
+ public function delete($path = null) {
+ if (!$path) {
+ $path = $this->pwd();
+ }
+ if (!$path) {
+ return null;
+ }
+ $path = Folder::slashTerm($path);
+ if (is_dir($path)) {
+ try {
+ $directory = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::CURRENT_AS_SELF);
+ $iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::CHILD_FIRST);
+ } catch (Exception $e) {
+ return false;
+ }
+
+ foreach ($iterator as $item) {
+ $filePath = $item->getPathname();
+ if ($item->isFile() || $item->isLink()) {
+ if (@unlink($filePath)) {
+ $this->_messages[] = __d('cake_dev', '%s removed', $filePath);
+ } else {
+ $this->_errors[] = __d('cake_dev', '%s NOT removed', $filePath);
+ }
+ } elseif ($item->isDir() && !$item->isDot()) {
+ if (@rmdir($filePath)) {
+ $this->_messages[] = __d('cake_dev', '%s removed', $filePath);
+ } else {
+ $this->_errors[] = __d('cake_dev', '%s NOT removed', $filePath);
+ return false;
+ }
+ }
+ }
+
+ $path = rtrim($path, DS);
+ if (@rmdir($path)) {
+ $this->_messages[] = __d('cake_dev', '%s removed', $path);
+ } else {
+ $this->_errors[] = __d('cake_dev', '%s NOT removed', $path);
+ return false;
+ }
+ }
+ return true;
+ }
+
+/**
+ * Recursive directory copy.
+ *
+ * ### Options
+ *
+ * - `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.
+ * - `skip` Files/directories to skip.
+ *
+ * @param array|string $options Either an array of options (see above) or a string of the destination directory.
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::copy
+ */
+ public function copy($options = array()) {
+ if (!$this->pwd()) {
+ return false;
+ }
+ $to = null;
+ if (is_string($options)) {
+ $to = $options;
+ $options = array();
+ }
+ $options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
+
+ $fromDir = $options['from'];
+ $toDir = $options['to'];
+ $mode = $options['mode'];
+
+ if (!$this->cd($fromDir)) {
+ $this->_errors[] = __d('cake_dev', '%s not found', $fromDir);
+ return false;
+ }
+
+ if (!is_dir($toDir)) {
+ $this->create($toDir, $mode);
+ }
+
+ if (!is_writable($toDir)) {
+ $this->_errors[] = __d('cake_dev', '%s not writable', $toDir);
+ return false;
+ }
+
+ $exceptions = array_merge(array('.', '..', '.svn'), $options['skip']);
+ if ($handle = @opendir($fromDir)) {
+ while (false !== ($item = readdir($handle))) {
+ if (!in_array($item, $exceptions)) {
+ $from = Folder::addPathElement($fromDir, $item);
+ $to = Folder::addPathElement($toDir, $item);
+ if (is_file($from)) {
+ if (copy($from, $to)) {
+ chmod($to, intval($mode, 8));
+ touch($to, filemtime($from));
+ $this->_messages[] = __d('cake_dev', '%s copied to %s', $from, $to);
+ } else {
+ $this->_errors[] = __d('cake_dev', '%s NOT copied to %s', $from, $to);
+ }
+ }
+
+ if (is_dir($from) && !file_exists($to)) {
+ $old = umask(0);
+ if (mkdir($to, $mode)) {
+ umask($old);
+ $old = umask(0);
+ chmod($to, $mode);
+ umask($old);
+ $this->_messages[] = __d('cake_dev', '%s created', $to);
+ $options = array_merge($options, array('to' => $to, 'from' => $from));
+ $this->copy($options);
+ } else {
+ $this->_errors[] = __d('cake_dev', '%s not created', $to);
+ }
+ }
+ }
+ }
+ closedir($handle);
+ } else {
+ return false;
+ }
+
+ if (!empty($this->_errors)) {
+ return false;
+ }
+ return true;
+ }
+
+/**
+ * Recursive directory move.
+ *
+ * ### Options
+ *
+ * - `to` The directory to copy to.
+ * - `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.
+ *
+ * @param array $options (to, from, chmod, skip)
+ * @return boolean Success
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::move
+ */
+ public function move($options) {
+ $to = null;
+ if (is_string($options)) {
+ $to = $options;
+ $options = (array)$options;
+ }
+ $options = array_merge(
+ array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()),
+ $options
+ );
+
+ if ($this->copy($options)) {
+ if ($this->delete($options['from'])) {
+ return (bool)$this->cd($options['to']);
+ }
+ }
+ return false;
+ }
+
+/**
+ * get messages from latest method
+ *
+ * @return array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::messages
+ */
+ public function messages() {
+ return $this->_messages;
+ }
+
+/**
+ * get error from latest method
+ *
+ * @return array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::errors
+ */
+ public function errors() {
+ return $this->_errors;
+ }
+
+/**
+ * Get the real path (taking ".." and such into account)
+ *
+ * @param string $path Path to resolve
+ * @return string The resolved path
+ * @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;
+ }
+ $parts = explode(DS, $path);
+ $newparts = array();
+ $newpath = '';
+ if ($path[0] === DS) {
+ $newpath = DS;
+ }
+
+ while (($part = array_shift($parts)) !== null) {
+ if ($part === '.' || $part === '') {
+ continue;
+ }
+ if ($part === '..') {
+ if (!empty($newparts)) {
+ array_pop($newparts);
+ continue;
+ } else {
+ return false;
+ }
+ }
+ $newparts[] = $part;
+ }
+ $newpath .= implode(DS, $newparts);
+
+ return Folder::slashTerm($newpath);
+ }
+
+/**
+ * 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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::isSlashTerm
+ */
+ public static function isSlashTerm($path) {
+ $lastChar = $path[strlen($path) - 1];
+ return $lastChar === '/' || $lastChar === '\\';
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Hash.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Hash.php
new file mode 100644
index 0000000..9514ccc
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Hash.php
@@ -0,0 +1,974 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2011, 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)
+ */
+
+App::uses('String', '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
+ * support for pseudo Xpath, its more fully featured dot notation provides
+ * similar features in a more consistent implementation.
+ *
+ * @package Cake.Utility
+ */
+class Hash {
+
+/**
+ * Get a single value specified by $path out of $data.
+ * Does not support the full dot notation feature set,
+ * but is faster for simple read operations.
+ *
+ * @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.
+ * @return mixed The value fetched from the array, or null.
+ */
+ public static function get(array $data, $path) {
+ if (empty($data) || empty($path)) {
+ return null;
+ }
+ if (is_string($path)) {
+ $parts = explode('.', $path);
+ } else {
+ $parts = $path;
+ }
+ foreach ($parts as $key) {
+ if (is_array($data) && isset($data[$key])) {
+ $data =& $data[$key];
+ } else {
+ return null;
+ }
+
+ }
+ return $data;
+ }
+
+/**
+ * Gets the values from an array matching the $path expression.
+ * The path expression is a dot separated expression, that can contain a set
+ * of patterns and expressions:
+ *
+ * - `{n}` Matches any numeric key, or integer.
+ * - `{s}` Matches any string key.
+ * - `Foo` Matches any key with the exact same value.
+ *
+ * There are a number of attribute operators:
+ *
+ * - `=`, `!=` Equality.
+ * - `>`, `<`, `>=`, `<=` Value comparison.
+ * - `=/.../` Regular expression pattern match.
+ *
+ * Given a set of User array data, from a `$User->find('all')` call:
+ *
+ * - `1.User.name` Get the name of the user at index 1.
+ * - `{n}.User.name` Get the name of every user in the set of users.
+ * - `{n}.User[id]` Get the name of every user with an id key.
+ * - `{n}.User[id>=2]` Get the name of every user with an id key greater than or equal to 2.
+ * - `{n}.User[username=/^paul/]` Get User elements with username matching `^paul`.
+ *
+ * @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
+ * if there are no matches.
+ */
+ public static function extract(array $data, $path) {
+ if (empty($path)) {
+ return $data;
+ }
+
+ // Simple paths.
+ if (!preg_match('/[{\[]/', $path)) {
+ return (array)self::get($data, $path);
+ }
+
+ if (strpos('[', $path) === false) {
+ $tokens = explode('.', $path);
+ } else {
+ $tokens = String::tokenize($path, '.', '[', ']');
+ }
+
+ $_key = '__set_item__';
+
+ $context = array($_key => array($data));
+
+ foreach ($tokens as $token) {
+ $next = array();
+
+ $conditions = false;
+ $position = strpos($token, '[');
+ if ($position !== false) {
+ $conditions = substr($token, $position);
+ $token = substr($token, 0, $position);
+ }
+
+ foreach ($context[$_key] as $item) {
+ foreach ($item as $k => $v) {
+ if (self::_matchToken($k, $token)) {
+ $next[] = $v;
+ }
+ }
+ }
+
+ // Filter for attributes.
+ if ($conditions) {
+ $filter = array();
+ foreach ($next as $item) {
+ if (self::_matches($item, $conditions)) {
+ $filter[] = $item;
+ }
+ }
+ $next = $filter;
+ }
+ $context = array($_key => $next);
+
+ }
+ return $context[$_key];
+ }
+
+/**
+ * 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
+ */
+ protected static function _matchToken($key, $token) {
+ if ($token === '{n}') {
+ return is_numeric($key);
+ }
+ if ($token === '{s}') {
+ return is_string($key);
+ }
+ if (is_numeric($token)) {
+ return ($key == $token);
+ }
+ return ($key === $token);
+ }
+
+/**
+ * Checks whether or not $data matches the attribute patterns
+ *
+ * @param array $data Array of data to match.
+ * @param string $selector The patterns to match.
+ * @return boolean Fitness of expression.
+ */
+ protected static function _matches(array $data, $selector) {
+ preg_match_all(
+ '/(\[ (?<attr>[^=><!]+?) (\s* (?<op>[><!]?[=]|[><]) \s* (?<val>[^\]]+) )? \])/x',
+ $selector,
+ $conditions,
+ PREG_SET_ORDER
+ );
+
+ foreach ($conditions as $cond) {
+ $attr = $cond['attr'];
+ $op = isset($cond['op']) ? $cond['op'] : null;
+ $val = isset($cond['val']) ? $cond['val'] : null;
+
+ // Presence test.
+ if (empty($op) && empty($val) && !isset($data[$attr])) {
+ return false;
+ }
+
+ // Empty attribute = fail.
+ if (!(isset($data[$attr]) || array_key_exists($attr, $data))) {
+ return false;
+ }
+
+ $prop = isset($data[$attr]) ? $data[$attr] : null;
+
+ // Pattern matches and other operators.
+ if ($op === '=' && $val && $val[0] === '/') {
+ if (!preg_match($val, $prop)) {
+ return false;
+ }
+ } elseif (
+ ($op === '=' && $prop != $val) ||
+ ($op === '!=' && $prop == $val) ||
+ ($op === '>' && $prop <= $val) ||
+ ($op === '<' && $prop >= $val) ||
+ ($op === '>=' && $prop < $val) ||
+ ($op === '<=' && $prop > $val)
+ ) {
+ return false;
+ }
+
+ }
+ return true;
+ }
+
+/**
+ * Insert $values into an array with the given $path. You can use
+ * `{n}` and `{s}` elements to insert $data multiple times.
+ *
+ * @param array $data The data to insert into.
+ * @param string $path The path to insert at.
+ * @param array $values The values to insert.
+ * @return array The data with $values inserted.
+ */
+ public static function insert(array $data, $path, $values = null) {
+ $tokens = explode('.', $path);
+ if (strpos($path, '{') === false) {
+ return self::_simpleOp('insert', $data, $tokens, $values);
+ }
+
+ $token = array_shift($tokens);
+ $nextPath = implode('.', $tokens);
+ foreach ($data as $k => $v) {
+ if (self::_matchToken($k, $token)) {
+ $data[$k] = self::insert($v, $nextPath, $values);
+ }
+ }
+ return $data;
+ }
+
+/**
+ * Perform a simple insert/remove operation.
+ *
+ * @param string $op The operation to do.
+ * @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.
+ */
+ protected static function _simpleOp($op, $data, $path, $values = null) {
+ $_list =& $data;
+
+ $count = count($path);
+ $last = $count - 1;
+ foreach ($path as $i => $key) {
+ if (is_numeric($key) && intval($key) > 0 || $key === '0') {
+ $key = intval($key);
+ }
+ if ($op === 'insert') {
+ if ($i === $last) {
+ $_list[$key] = $values;
+ return $data;
+ }
+ if (!isset($_list[$key])) {
+ $_list[$key] = array();
+ }
+ $_list =& $_list[$key];
+ if (!is_array($_list)) {
+ $_list = array();
+ }
+ } elseif ($op === 'remove') {
+ if ($i === $last) {
+ unset($_list[$key]);
+ return $data;
+ }
+ if (!isset($_list[$key])) {
+ return $data;
+ }
+ $_list =& $_list[$key];
+ }
+ }
+ }
+
+/**
+ * Remove data matching $path from the $data array.
+ * You can use `{n}` and `{s}` to remove multiple elements
+ * from $data.
+ *
+ * @param array $data The data to operate on
+ * @param string $path A path expression to use to remove.
+ * @return array The modified array.
+ */
+ public static function remove(array $data, $path) {
+ $tokens = explode('.', $path);
+ if (strpos($path, '{') === false) {
+ return self::_simpleOp('remove', $data, $tokens);
+ }
+
+ $token = array_shift($tokens);
+ $nextPath = implode('.', $tokens);
+ foreach ($data as $k => $v) {
+ $match = self::_matchToken($k, $token);
+ if ($match && is_array($v)) {
+ $data[$k] = self::remove($v, $nextPath);
+ } elseif ($match) {
+ unset($data[$k]);
+ }
+ }
+ return $data;
+ }
+
+/**
+ * Creates an associative array using `$keyPath` as the path to build its keys, and optionally
+ * `$valuePath` as path to get the values. If `$valuePath` is not specified, all values will be initialized
+ * to null (useful for Hash::merge). You can optionally group the values by what is obtained when
+ * following the path specified in `$groupPath`.
+ *
+ * @param array $data Array from where to extract keys and values
+ * @param string $keyPath A dot-separated string.
+ * @param string $valuePath A dot-separated string.
+ * @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
+ */
+ public static function combine(array $data, $keyPath, $valuePath = null, $groupPath = null) {
+ if (empty($data)) {
+ return array();
+ }
+
+ if (is_array($keyPath)) {
+ $format = array_shift($keyPath);
+ $keys = self::format($data, $keyPath, $format);
+ } else {
+ $keys = self::extract($data, $keyPath);
+ }
+ if (empty($keys)) {
+ return array();
+ }
+
+ if (!empty($valuePath) && is_array($valuePath)) {
+ $format = array_shift($valuePath);
+ $vals = self::format($data, $valuePath, $format);
+ } elseif (!empty($valuePath)) {
+ $vals = self::extract($data, $valuePath);
+ }
+
+ $count = count($keys);
+ for ($i = 0; $i < $count; $i++) {
+ $vals[$i] = isset($vals[$i]) ? $vals[$i] : null;
+ }
+
+ if ($groupPath !== null) {
+ $group = self::extract($data, $groupPath);
+ if (!empty($group)) {
+ $c = count($keys);
+ for ($i = 0; $i < $c; $i++) {
+ if (!isset($group[$i])) {
+ $group[$i] = 0;
+ }
+ if (!isset($out[$group[$i]])) {
+ $out[$group[$i]] = array();
+ }
+ $out[$group[$i]][$keys[$i]] = $vals[$i];
+ }
+ return $out;
+ }
+ }
+ if (empty($vals)) {
+ return array();
+ }
+ return array_combine($keys, $vals);
+ }
+
+/**
+ * Returns a formated 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.
+ *
+ * @param array $data Source array from which to extract the data
+ * @param string $paths An array containing one or more Hash::extract()-style key paths
+ * @param string $format Format string into which values will be inserted, see sprintf()
+ * @return array An array of strings extracted from `$path` and formatted with `$format`
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::format
+ * @see sprintf()
+ * @see Hash::extract()
+ */
+ public static function format(array $data, array $paths, $format) {
+ $extracted = array();
+ $count = count($paths);
+
+ if (!$count) {
+ return;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $extracted[] = self::extract($data, $paths[$i]);
+ }
+ $out = array();
+ $data = $extracted;
+ $count = count($data[0]);
+
+ $countTwo = count($data);
+ for ($j = 0; $j < $count; $j++) {
+ $args = array();
+ for ($i = 0; $i < $countTwo; $i++) {
+ if (array_key_exists($j, $data[$i])) {
+ $args[] = $data[$i][$j];
+ }
+ }
+ $out[] = vsprintf($format, $args);
+ }
+ return $out;
+ }
+
+/**
+ * Determines if one array contains the exact keys and values of another.
+ *
+ * @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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::contains
+ */
+ public static function contains(array $data, array $needle) {
+ if (empty($data) || empty($needle)) {
+ return false;
+ }
+ $stack = array();
+
+ $i = 1;
+ while (!empty($needle)) {
+ $key = key($needle);
+ $val = $needle[$key];
+ unset($needle[$key]);
+
+ if (isset($data[$key]) && is_array($val)) {
+ $next = $data[$key];
+ unset($data[$key]);
+
+ if (!empty($val)) {
+ $stack[] = array($val, $next);
+ }
+ } elseif (!isset($data[$key]) || $data[$key] != $val) {
+ return false;
+ }
+
+ if (empty($needle) && !empty($stack)) {
+ list($needle, $data) = array_pop($stack);
+ }
+ }
+ return true;
+ }
+
+/**
+ * Test whether or not a given path exists in $data.
+ * This method uses the same path syntax as Hash::extract()
+ *
+ * Checking for paths that could target more than one element will
+ * make sure that at least one matching element exists.
+ *
+ * @param array $data The data to check.
+ * @param string $path The path to check for.
+ * @return boolean Existence of path.
+ * @see Hash::extract()
+ */
+ public static function check(array $data, $path) {
+ $results = self::extract($data, $path);
+ if (!is_array($results)) {
+ return false;
+ }
+ return count($results) > 0;
+ }
+
+/**
+ * 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.
+ * @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);
+ }
+ }
+ return array_filter($data, $callback);
+ }
+
+/**
+ * Callback function for filtering.
+ *
+ * @param array $var Array to filter.
+ * @return boolean
+ */
+ protected static function _filter($var) {
+ if ($var === 0 || $var === '0' || !empty($var)) {
+ return true;
+ }
+ return false;
+ }
+
+/**
+ * Collapses a multi-dimensional array into a single dimension, using a delimited array path for
+ * each array element's key, i.e. array(array('Foo' => array('Bar' => 'Far'))) becomes
+ * array('0.Foo.Bar' => 'Far').)
+ *
+ * @param array $data Array to flatten
+ * @param string $separator String used to separate array key elements in a path, defaults to '.'
+ * @return array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::flatten
+ */
+ public static function flatten(array $data, $separator = '.') {
+ $result = array();
+ $stack = array();
+ $path = null;
+
+ reset($data);
+ while (!empty($data)) {
+ $key = key($data);
+ $element = $data[$key];
+ unset($data[$key]);
+
+ if (is_array($element)) {
+ if (!empty($data)) {
+ $stack[] = array($data, $path);
+ }
+ $data = $element;
+ $path .= $key . $separator;
+ } else {
+ $result[$path . $key] = $element;
+ }
+
+ if (empty($data) && !empty($stack)) {
+ list($data, $path) = array_pop($stack);
+ }
+ }
+ return $result;
+ }
+
+/**
+ * Expand/unflattens an string to an array
+ *
+ * For example, unflattens an array that was collapsed with `Hash::flatten()`
+ * into a multi-dimensional array. So, `array('0.Foo.Bar' => 'Far')` becomes
+ * `array(array('Foo' => array('Bar' => 'Far')))`.
+ *
+ * @param array $data Flattened array
+ * @param string $separator The delimiter used
+ * @return array
+ */
+ public static function expand($data, $separator = '.') {
+ $result = array();
+ foreach ($data as $flat => $value) {
+ $keys = explode($separator, $flat);
+ $keys = array_reverse($keys);
+ $child = array(
+ $keys[0] => $value
+ );
+ array_shift($keys);
+ foreach ($keys as $k) {
+ $child = array(
+ $k => $child
+ );
+ }
+ $result = self::merge($result, $child);
+ }
+ return $result;
+ }
+
+/**
+ * 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
+ * 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.
+ *
+ * @param array $data Array to be merged
+ * @param mixed $merge Array to merge with. The argument and all trailing arguments will be array cast when merged
+ * @return array Merged array
+ * @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);
+
+ 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;
+ }
+ }
+ }
+ return $return;
+ }
+
+/**
+ * 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
+ * @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);
+ }
+
+/**
+ * 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()
+ * to get the dimensions of the array.
+ *
+ * @param array $array Array to count dimensions on
+ * @return integer 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) {
+ if (empty($data)) {
+ return 0;
+ }
+ reset($data);
+ $depth = 1;
+ while ($elem = array_shift($data)) {
+ if (is_array($elem)) {
+ $depth += 1;
+ $data =& $elem;
+ } else {
+ break;
+ }
+ }
+ return $depth;
+ }
+
+/**
+ * Counts the dimensions of *all* array elements. Useful for finding the maximum
+ * number of dimensions in a mixed array.
+ *
+ * @param array $data Array to count dimensions on
+ * @return integer 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) {
+ $depth = array();
+ if (is_array($data) && reset($data) !== false) {
+ foreach ($data as $value) {
+ $depth[] = self::dimensions((array)$value) + 1;
+ }
+ }
+ return max($depth);
+ }
+
+/**
+ * Map a callback across all elements in a set.
+ * Can be provided a path to only modify slices of the set.
+ *
+ * @param array $data The data to map over, and extract data out of.
+ * @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.
+ */
+ public static function map(array $data, $path, $function) {
+ $values = (array)self::extract($data, $path);
+ return array_map($function, $values);
+ }
+
+/**
+ * Reduce a set of extracted values using `$function`.
+ *
+ * @param array $data The data to reduce.
+ * @param string $path The path to extract from $data.
+ * @return mixed The reduced value.
+ */
+ public static function reduce(array $data, $path, $function) {
+ $values = (array)self::extract($data, $path);
+ return array_reduce($values, $function);
+ }
+
+/**
+ * Apply a callback to a set of extracted values using `$function`.
+ * The function will get the extracted values as the first argument.
+ *
+ * @param array $data The data to reduce.
+ * @param string $path The path to extract from $data.
+ * @return mixed The results of the applied method.
+ */
+ public static function apply(array $data, $path, $function) {
+ $values = (array)self::extract($data, $path);
+ return call_user_func($function, $values);
+ }
+
+/**
+ * Sorts an array by any value, determined by a Set-compatible path
+ *
+ * ### Sort directions
+ *
+ * - `asc` Sort ascending.
+ * - `desc` Sort descending.
+ *
+ * ## 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.
+ *
+ * @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'.
+ * @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') {
+ $originalKeys = array_keys($data);
+ $numeric = is_numeric(implode('', $originalKeys));
+ if ($numeric) {
+ $data = array_values($data);
+ }
+ $sortValues = self::extract($data, $path);
+ $sortCount = count($sortValues);
+ $dataCount = count($data);
+
+ // Make sortValues match the data length, as some keys could be missing
+ // the sorted value path.
+ 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');
+
+ $dir = strtolower($dir);
+ $type = strtolower($type);
+ if ($type == 'natural' && version_compare(PHP_VERSION, '5.4.0', '<')) {
+ $type == 'regular';
+ }
+ if ($dir === 'asc') {
+ $dir = SORT_ASC;
+ } else {
+ $dir = SORT_DESC;
+ }
+ if ($type === 'numeric') {
+ $type = SORT_NUMERIC;
+ } elseif ($type === 'string') {
+ $type = SORT_STRING;
+ } elseif ($type === 'natural') {
+ $type = SORT_NATURAL;
+ } else {
+ $type = SORT_REGULAR;
+ }
+ array_multisort($values, $dir, $type, $keys, $dir, $type);
+ $sorted = array();
+ $keys = array_unique($keys);
+
+ foreach ($keys as $k) {
+ if ($numeric) {
+ $sorted[] = $data[$k];
+ continue;
+ }
+ if (isset($originalKeys[$k])) {
+ $sorted[$originalKeys[$k]] = $data[$originalKeys[$k]];
+ } else {
+ $sorted[$k] = $data[$k];
+ }
+ }
+ return $sorted;
+ }
+
+/**
+ * Helper method for sort()
+ * Sqaushes 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.
+ * @return array
+ */
+ protected static function _squash($data, $key = null) {
+ $stack = array();
+ foreach ($data as $k => $r) {
+ $id = $k;
+ if (!is_null($key)) {
+ $id = $key;
+ }
+ if (is_array($r) && !empty($r)) {
+ $stack = array_merge($stack, self::_squash($r, $id));
+ } else {
+ $stack[] = array('id' => $id, 'value' => $r);
+ }
+ }
+ return $stack;
+ }
+
+/**
+ * Computes the difference between two complex arrays.
+ * This method differs from the built-in array_diff() in that it will preserve keys
+ * and work on multi-dimensional arrays.
+ *
+ * @param array $data First value
+ * @param array $compare Second value
+ * @return array Returns the key => value pairs that are not common in $data and $compare
+ * The expression for this function is ($data - $compare) + ($compare - ($data - $compare))
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::diff
+ */
+ public static function diff(array $data, $compare) {
+ if (empty($data)) {
+ return (array)$compare;
+ }
+ if (empty($compare)) {
+ return (array)$data;
+ }
+ $intersection = array_intersect_key($data, $compare);
+ while (($key = key($intersection)) !== null) {
+ if ($data[$key] == $compare[$key]) {
+ unset($data[$key]);
+ unset($compare[$key]);
+ }
+ next($intersection);
+ }
+ return $data + $compare;
+ }
+
+/**
+ * Merges the difference between $data and $push 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.
+ */
+ public static function mergeDiff(array $data, $compare) {
+ if (empty($data) && !empty($compare)) {
+ return $compare;
+ }
+ if (empty($compare)) {
+ return $data;
+ }
+ foreach ($compare as $key => $value) {
+ if (!array_key_exists($key, $data)) {
+ $data[$key] = $value;
+ } elseif (is_array($value)) {
+ $data[$key] = self::mergeDiff($data[$key], $compare[$key]);
+ }
+ }
+ return $data;
+ }
+
+/**
+ * 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.
+ * @return array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::normalize
+ */
+ public static function normalize(array $data, $assoc = true) {
+ $keys = array_keys($data);
+ $count = count($keys);
+ $numeric = true;
+
+ if (!$assoc) {
+ for ($i = 0; $i < $count; $i++) {
+ if (!is_int($keys[$i])) {
+ $numeric = false;
+ break;
+ }
+ }
+ }
+ if (!$numeric || $assoc) {
+ $newList = array();
+ for ($i = 0; $i < $count; $i++) {
+ if (is_int($keys[$i])) {
+ $newList[$data[$keys[$i]]] = null;
+ } else {
+ $newList[$keys[$i]] = $data[$keys[$i]];
+ }
+ }
+ $data = $newList;
+ }
+ return $data;
+ }
+
+/**
+ * Takes in a flat array and returns a nested array
+ *
+ * ### Options:
+ *
+ * - `children` The key name to use in the resultset for children.
+ * - `idPath` The path to a key that identifies each entry. Should be
+ * compatible with Hash::extract(). Defaults to `{n}.$alias.id`
+ * - `parentPath` The path to a key that identifies the parent of each entry.
+ * Should be compatible with Hash::extract(). Defaults to `{n}.$alias.parent_id`
+ * - `root` The id of the desired top-most result.
+ *
+ * @param array $data The data to nest.
+ * @param array $options Options are:
+ * @return array of results, nested
+ * @see Hash::extract()
+ */
+ public static function nest(array $data, $options = array()) {
+ if (!$data) {
+ return $data;
+ }
+
+ $alias = key(current($data));
+ $options += array(
+ 'idPath' => "{n}.$alias.id",
+ 'parentPath' => "{n}.$alias.parent_id",
+ 'children' => 'children',
+ 'root' => null
+ );
+
+ $return = $idMap = array();
+ $ids = self::extract($data, $options['idPath']);
+
+ $idKeys = explode('.', $options['idPath']);
+ array_shift($idKeys);
+
+ $parentKeys = explode('.', $options['parentPath']);
+ array_shift($parentKeys);
+
+ foreach ($data as $result) {
+ $result[$options['children']] = array();
+
+ $id = self::get($result, $idKeys);
+ $parentId = self::get($result, $parentKeys);
+
+ if (isset($idMap[$id][$options['children']])) {
+ $idMap[$id] = array_merge($result, (array)$idMap[$id]);
+ } else {
+ $idMap[$id] = array_merge($result, array($options['children'] => array()));
+ }
+ if (!$parentId || !in_array($parentId, $ids)) {
+ $return[] =& $idMap[$id];
+ } else {
+ $idMap[$parentId][$options['children']][] =& $idMap[$id];
+ }
+ }
+
+ if ($options['root']) {
+ $root = $options['root'];
+ } else {
+ $root = self::get($return[0], $parentKeys);
+ }
+
+ foreach ($return as $i => $result) {
+ $id = self::get($result, $idKeys);
+ $parentId = self::get($result, $parentKeys);
+ if ($id !== $root && $parentId != $root) {
+ unset($return[$i]);
+ }
+ }
+ return array_values($return);
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Inflector.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Inflector.php
new file mode 100644
index 0000000..31829d6
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Inflector.php
@@ -0,0 +1,551 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+/**
+ * Pluralize and singularize English words.
+ *
+ * Inflector pluralizes and singularizes English nouns.
+ * Used by Cake's naming conventions throughout the framework.
+ *
+ * @package Cake.Utility
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html
+ */
+class Inflector {
+
+/**
+ * Plural inflector rules
+ *
+ * @var array
+ */
+ protected static $_plural = array(
+ 'rules' => array(
+ '/(s)tatus$/i' => '\1\2tatuses',
+ '/(quiz)$/i' => '\1zes',
+ '/^(ox)$/i' => '\1\2en',
+ '/([m|l])ouse$/i' => '\1ice',
+ '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
+ '/(x|ch|ss|sh)$/i' => '\1es',
+ '/([^aeiouy]|qu)y$/i' => '\1ies',
+ '/(hive)$/i' => '\1s',
+ '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
+ '/sis$/i' => 'ses',
+ '/([ti])um$/i' => '\1a',
+ '/(p)erson$/i' => '\1eople',
+ '/(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',
+ '/us$/i' => 'uses',
+ '/(alias)$/i' => '\1es',
+ '/(ax|cris|test)is$/i' => '\1es',
+ '/s$/' => 's',
+ '/^$/' => '',
+ '/$/' => 's',
+ ),
+ 'uninflected' => array(
+ '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'people'
+ ),
+ 'irregular' => array(
+ 'atlas' => 'atlases',
+ 'beef' => 'beefs',
+ 'brother' => 'brothers',
+ 'cafe' => 'cafes',
+ 'child' => 'children',
+ 'corpus' => 'corpuses',
+ 'cow' => 'cows',
+ 'ganglion' => 'ganglions',
+ 'genie' => 'genies',
+ 'genus' => 'genera',
+ 'graffito' => 'graffiti',
+ 'hoof' => 'hoofs',
+ 'loaf' => 'loaves',
+ 'man' => 'men',
+ 'money' => 'monies',
+ 'mongoose' => 'mongooses',
+ 'move' => 'moves',
+ 'mythos' => 'mythoi',
+ 'niche' => 'niches',
+ 'numen' => 'numina',
+ 'occiput' => 'occiputs',
+ 'octopus' => 'octopuses',
+ 'opus' => 'opuses',
+ 'ox' => 'oxen',
+ 'penis' => 'penises',
+ 'person' => 'people',
+ 'sex' => 'sexes',
+ 'soliloquy' => 'soliloquies',
+ 'testis' => 'testes',
+ 'trilby' => 'trilbys',
+ 'turf' => 'turfs'
+ )
+ );
+
+/**
+ * Singular inflector rules
+ *
+ * @var array
+ */
+ protected static $_singular = array(
+ 'rules' => array(
+ '/(s)tatuses$/i' => '\1\2tatus',
+ '/^(.*)(menu)s$/i' => '\1\2',
+ '/(quiz)zes$/i' => '\\1',
+ '/(matr)ices$/i' => '\1ix',
+ '/(vert|ind)ices$/i' => '\1ex',
+ '/^(ox)en/i' => '\1',
+ '/(alias)(es)*$/i' => '\1',
+ '/(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',
+ '/(o)es$/i' => '\1',
+ '/ouses$/' => 'ouse',
+ '/([^a])uses$/' => '\1us',
+ '/([m|l])ice$/i' => '\1ouse',
+ '/(x|ch|ss|sh)es$/i' => '\1',
+ '/(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',
+ '/(^analy)ses$/i' => '\1sis',
+ '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
+ '/([ti])a$/i' => '\1um',
+ '/(p)eople$/i' => '\1\2erson',
+ '/(m)en$/i' => '\1an',
+ '/(c)hildren$/i' => '\1\2hild',
+ '/(n)ews$/i' => '\1\2ews',
+ '/eaus$/' => 'eau',
+ '/^(.*us)$/' => '\\1',
+ '/s$/i' => ''
+ ),
+ 'uninflected' => array(
+ '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss'
+ ),
+ 'irregular' => array(
+ 'foes' => 'foe',
+ 'waves' => 'wave',
+ 'curves' => 'curve'
+ )
+ );
+
+/**
+ * Words that should not be inflected
+ *
+ * @var array
+ */
+ protected static $_uninflected = array(
+ 'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus',
+ 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps',
+ 'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder',
+ 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
+ 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings',
+ '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',
+ 'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'swine', 'testes',
+ 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', 'whiting', 'wildebeest',
+ 'Yengeese'
+ );
+
+/**
+ * Default map of accented and special characters to ASCII characters
+ *
+ * @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',
+ '/IJ/' => 'IJ',
+ '/ij/' => 'ij',
+ '/Œ/' => 'OE',
+ '/ƒ/' => 'f'
+ );
+
+/**
+ * Method cache array.
+ *
+ * @var array
+ */
+ protected static $_cache = array();
+
+/**
+ * The initial state of Inflector so reset() works.
+ *
+ * @var array
+ */
+ protected static $_initialState = array();
+
+/**
+ * Cache inflected values, and return if already available
+ *
+ * @param string $type Inflection type
+ * @param string $key Original value
+ * @param string $value Inflected value
+ * @return string Inflected value, from cache
+ */
+ protected static function _cache($type, $key, $value = false) {
+ $key = '_' . $key;
+ $type = '_' . $type;
+ if ($value !== false) {
+ self::$_cache[$type][$key] = $value;
+ return $value;
+ }
+ if (!isset(self::$_cache[$type][$key])) {
+ return false;
+ }
+ return self::$_cache[$type][$key];
+ }
+
+/**
+ * Clears Inflectors inflected value caches. And resets the inflection
+ * rules to the initial values.
+ *
+ * @return void
+ */
+ public static function reset() {
+ if (empty(self::$_initialState)) {
+ self::$_initialState = get_class_vars('Inflector');
+ return;
+ }
+ foreach (self::$_initialState as $key => $val) {
+ if ($key != '_initialState') {
+ self::${$key} = $val;
+ }
+ }
+ }
+
+/**
+ * Adds custom inflection $rules, of either 'plural', 'singular' or 'transliteration' $type.
+ *
+ * ### Usage:
+ *
+ * {{{
+ * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
+ * Inflector::rules('plural', array(
+ * 'rules' => array('/^(inflect)ors$/i' => '\1ables'),
+ * 'uninflected' => array('dontinflectme'),
+ * '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
+ * new rules that are being defined in $rules.
+ * @return void
+ */
+ public static function rules($type, $rules, $reset = false) {
+ $var = '_' . $type;
+
+ switch ($type) {
+ case 'transliteration':
+ if ($reset) {
+ self::$_transliteration = $rules;
+ } else {
+ self::$_transliteration = $rules + self::$_transliteration;
+ }
+ break;
+
+ default:
+ foreach ($rules as $rule => $pattern) {
+ if (is_array($pattern)) {
+ if ($reset) {
+ self::${$var}[$rule] = $pattern;
+ } else {
+ if ($rule === 'uninflected') {
+ self::${$var}[$rule] = array_merge($pattern, self::${$var}[$rule]);
+ } else {
+ self::${$var}[$rule] = $pattern + self::${$var}[$rule];
+ }
+ }
+ unset($rules[$rule], self::${$var}['cache' . ucfirst($rule)]);
+ if (isset(self::${$var}['merged'][$rule])) {
+ unset(self::${$var}['merged'][$rule]);
+ }
+ if ($type === 'plural') {
+ self::$_cache['pluralize'] = self::$_cache['tableize'] = array();
+ } elseif ($type === 'singular') {
+ self::$_cache['singularize'] = array();
+ }
+ }
+ }
+ self::${$var}['rules'] = $rules + self::${$var}['rules'];
+ break;
+ }
+ }
+
+/**
+ * Return $word in plural form.
+ *
+ * @param string $word Word in singular
+ * @return string Word in plural
+ * @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(self::$_plural['merged']['irregular'])) {
+ self::$_plural['merged']['irregular'] = self::$_plural['irregular'];
+ }
+
+ if (!isset(self::$_plural['merged']['uninflected'])) {
+ self::$_plural['merged']['uninflected'] = array_merge(self::$_plural['uninflected'], self::$_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 (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('/^(' . self::$_plural['cacheUninflected'] . ')$/i', $word, $regs)) {
+ self::$_cache['pluralize'][$word] = $word;
+ return $word;
+ }
+
+ foreach (self::$_plural['rules'] as $rule => $replacement) {
+ if (preg_match($rule, $word)) {
+ self::$_cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
+ return self::$_cache['pluralize'][$word];
+ }
+ }
+ }
+
+/**
+ * Return $word in singular form.
+ *
+ * @param string $word Word in plural
+ * @return string Word in singular
+ * @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(self::$_singular['merged']['uninflected'])) {
+ self::$_singular['merged']['uninflected'] = array_merge(
+ self::$_singular['uninflected'],
+ self::$_uninflected
+ );
+ }
+
+ if (!isset(self::$_singular['merged']['irregular'])) {
+ self::$_singular['merged']['irregular'] = array_merge(
+ self::$_singular['irregular'],
+ array_flip(self::$_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 (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('/^(' . self::$_singular['cacheUninflected'] . ')$/i', $word, $regs)) {
+ self::$_cache['singularize'][$word] = $word;
+ return $word;
+ }
+
+ foreach (self::$_singular['rules'] as $rule => $replacement) {
+ if (preg_match($rule, $word)) {
+ self::$_cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
+ return self::$_cache['singularize'][$word];
+ }
+ }
+ self::$_cache['singularize'][$word] = $word;
+ return $word;
+ }
+
+/**
+ * Returns the given lower_case_and_underscored_word as a CamelCased word.
+ *
+ * @param string $lowerCaseAndUnderscoredWord Word to camelize
+ * @return string Camelized word. LikeThis.
+ * @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))) {
+ $result = str_replace(' ', '', Inflector::humanize($lowerCaseAndUnderscoredWord));
+ self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
+ }
+ return $result;
+ }
+
+/**
+ * Returns the given camelCasedWord as an underscored_word.
+ *
+ * @param string $camelCasedWord Camel-cased word to be "underscorized"
+ * @return string Underscore-syntaxed version of the $camelCasedWord
+ * @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);
+ }
+ return $result;
+ }
+
+/**
+ * Returns the given underscored_word_group as a Human Readable Word Group.
+ * (Underscores are replaced by spaces and capitalized following words.)
+ *
+ * @param string $lowerCaseAndUnderscoredWord String to be made more readable
+ * @return string Human-readable string
+ * @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);
+ }
+ return $result;
+ }
+
+/**
+ * Returns corresponding table name for given model $className. ("people" for the model class "Person").
+ *
+ * @param string $className Name of class to get database table name for
+ * @return string Name of the database table for given class
+ * @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))) {
+ $result = Inflector::pluralize(Inflector::underscore($className));
+ self::_cache(__FUNCTION__, $className, $result);
+ }
+ return $result;
+ }
+
+/**
+ * Returns Cake model class name ("Person" for the database table "people".) for given database table.
+ *
+ * @param string $tableName Name of database table to get class name for
+ * @return string Class name
+ * @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))) {
+ $result = Inflector::camelize(Inflector::singularize($tableName));
+ self::_cache(__FUNCTION__, $tableName, $result);
+ }
+ return $result;
+ }
+
+/**
+ * Returns camelBacked version of an underscored string.
+ *
+ * @param string $string
+ * @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))) {
+ $camelized = Inflector::camelize(Inflector::underscore($string));
+ $replace = strtolower(substr($camelized, 0, 1));
+ $result = preg_replace('/\\w/', $replace, $camelized, 1);
+ self::_cache(__FUNCTION__, $string, $result);
+ }
+ return $result;
+ }
+
+/**
+ * Returns a string with all spaces converted to underscores (by default), accented
+ * characters converted to non-accented characters, and non word characters removed.
+ *
+ * @param string $string the string you want to slug
+ * @param string $replacement will replace keys in map
+ * @return string
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/inflector.html#Inflector::slug
+ */
+ public static function slug($string, $replacement = '_') {
+ $quotedReplacement = preg_quote($replacement, '/');
+
+ $merge = array(
+ '/[^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
+ '/\\s+/' => $replacement,
+ sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
+ );
+
+ $map = self::$_transliteration + $merge;
+ return preg_replace(array_keys($map), array_values($map), $string);
+ }
+
+}
+
+// Store the initial state
+Inflector::reset();
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/ObjectCollection.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/ObjectCollection.php
new file mode 100644
index 0000000..96ec3d5
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/ObjectCollection.php
@@ -0,0 +1,326 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+/**
+ * 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
+ * when loaded.
+ *
+ * @package Cake.Utility
+ * @since CakePHP(tm) v 2.0
+ */
+abstract class ObjectCollection {
+
+/**
+ * List of the currently-enabled objects
+ *
+ * @var array
+ */
+ protected $_enabled = array();
+
+/**
+ * A hash of loaded objects, indexed by name
+ *
+ * @var array
+ */
+ protected $_loaded = array();
+
+/**
+ * Default object priority. A non zero integer.
+ *
+ * @var int
+ */
+ public $defaultPriority = 10;
+
+/**
+ * Loads a new object onto the collection. Can throw a variety of exceptions
+ *
+ * Implementations of this class support a `$options['enabled']` flag which enables/disables
+ * a loaded object.
+ *
+ * @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
+ */
+ 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
+ * order they were attached.
+ *
+ * ### Options
+ *
+ * - `breakOn` Set to the value or values you want the callback propagation to stop on.
+ * 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.
+ * Defaults to `false`.
+ *
+ * - `collectReturn` Set to true to collect the return of each object into an array.
+ * This array of return values will be returned from the trigger() call. Defaults to `false`.
+ *
+ * - `modParams` Allows each object the callback gets called on to modify the parameters to the next object.
+ * Setting modParams to an integer value will allow you to modify the parameter with that index.
+ * 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
+ * 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)
+ * @param array $params Array of parameters for the triggered callback.
+ * @param array $options Array of options.
+ * @return mixed Either the last result or all results if collectReturn is on.
+ * @throws CakeException when modParams is used with an index that does not exist.
+ */
+ public function trigger($callback, $params = array(), $options = array()) {
+ if (empty($this->_enabled)) {
+ return true;
+ }
+ if ($callback instanceof CakeEvent) {
+ $event = $callback;
+ if (is_array($event->data)) {
+ $params =& $event->data;
+ }
+ if (empty($event->omitSubject)) {
+ $subject = $event->subject();
+ }
+ //TODO: Temporary BC check, while we move all the triggers system into the CakeEventManager
+ foreach (array('break', 'breakOn', 'collectReturn', 'modParams') as $opt) {
+ if (isset($event->{$opt})) {
+ $options[$opt] = $event->{$opt};
+ }
+ }
+ $parts = explode('.', $event->name());
+ $callback = array_pop($parts);
+ }
+ $options = array_merge(
+ array(
+ 'break' => false,
+ 'breakOn' => false,
+ 'collectReturn' => false,
+ 'modParams' => false
+ ),
+ $options
+ );
+ $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.'));
+ }
+ 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'] ||
+ (is_array($options['breakOn']) && in_array($result, $options['breakOn'], true)))
+ ) {
+ return $result;
+ } elseif ($options['modParams'] !== false && !in_array($result, array(true, false, null), true)) {
+ $params[$options['modParams']] = $result;
+ }
+ }
+ if ($options['modParams'] !== false) {
+ return $params[$options['modParams']];
+ }
+ return $options['collectReturn'] ? $collected : $result;
+ }
+
+/**
+ * Provide public read access to the loaded objects
+ *
+ * @param string $name Name of property to read
+ * @return mixed
+ */
+ public function __get($name) {
+ if (isset($this->_loaded[$name])) {
+ return $this->_loaded[$name];
+ }
+ return null;
+ }
+
+/**
+ * Provide isset access to _loaded
+ *
+ * @param string $name Name of object being checked.
+ * @return boolean
+ */
+ public function __isset($name) {
+ return isset($this->_loaded[$name]);
+ }
+
+/**
+ * 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)
+ * @return void
+ */
+ public function enable($name, $prioritize = true) {
+ $enabled = false;
+ foreach ((array)$name as $object) {
+ if (isset($this->_loaded[$object]) && !isset($this->_enabled[$object])) {
+ $priority = isset($this->_loaded[$object]->settings['priority']) ? $this->_loaded[$object]->settings['priority'] : $this->defaultPriority;
+ $this->_enabled[$object] = array($priority);
+ $enabled = true;
+ }
+ }
+ if ($prioritize && $enabled) {
+ $this->prioritize();
+ }
+ }
+
+/**
+ * Prioritize list of enabled object
+ *
+ * @return array Prioritized list of object
+ */
+ public function prioritize() {
+ $i = 1;
+ foreach ($this->_enabled as $name => $priority) {
+ $priority[1] = $i++;
+ $this->_enabled[$name] = $priority;
+ }
+ asort($this->_enabled);
+ return $this->_enabled;
+ }
+
+/**
+ * Set priority for an object or array of objects
+ *
+ * @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
+ * @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;
+ }
+ $this->_loaded[$obj]->settings['priority'] = $prio;
+ if (isset($this->_enabled[$obj])) {
+ $this->_enabled[$obj] = array($prio);
+ }
+ }
+ }
+ $this->prioritize();
+ }
+
+/**
+ * Disables callbacks on a 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)
+ * @return void
+ */
+ public function disable($name) {
+ foreach ((array)$name as $object) {
+ unset($this->_enabled[$object]);
+ }
+ }
+
+/**
+ * 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,
+ * 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)) {
+ return isset($this->_enabled[$name]);
+ }
+ return array_keys($this->_enabled);
+ }
+
+/**
+ * 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.
+ */
+ public function attached($name = null) {
+ if (!empty($name)) {
+ return isset($this->_loaded[$name]);
+ }
+ return array_keys($this->_loaded);
+ }
+
+/**
+ * Name of the object to remove from the collection
+ *
+ * @param string $name Name of the object to delete.
+ * @return void
+ */
+ public function unload($name) {
+ list($plugin, $name) = pluginSplit($name);
+ unset($this->_loaded[$name]);
+ unset($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
+ * @return array Loaded objects
+ */
+ public function set($name = null, $object = null) {
+ if (!empty($name) && !empty($object)) {
+ list($plugin, $name) = pluginSplit($name);
+ $this->_loaded[$name] = $object;
+ }
+ return $this->_loaded;
+ }
+
+/**
+ * Normalizes an object array, creates an array that makes lazy loading
+ * easier
+ *
+ * @param array $objects Array of child objects to normalize.
+ * @return array Array of normalized objects.
+ */
+ public static function normalizeObjectArray($objects) {
+ $normal = array();
+ foreach ($objects as $i => $objectName) {
+ $options = array();
+ if (!is_int($i)) {
+ $options = (array)$objectName;
+ $objectName = $i;
+ }
+ list($plugin, $name) = pluginSplit($objectName);
+ $normal[$name] = array('class' => $objectName, 'settings' => $options);
+ }
+ return $normal;
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Sanitize.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Sanitize.php
new file mode 100644
index 0000000..dfb55f3
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Sanitize.php
@@ -0,0 +1,264 @@
+<?php
+/**
+ * Washes strings from unwanted noise.
+ *
+ * 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)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+App::import('Model', 'ConnectionManager');
+
+/**
+ * Data Sanitization.
+ *
+ * Removal of alphanumeric characters, SQL-safe slash-added strings, HTML-friendly strings,
+ * and all of the above on arrays.
+ *
+ * @package Cake.Utility
+ */
+class Sanitize {
+
+/**
+ * Removes any non-alphanumeric characters.
+ *
+ * @param string $string String to sanitize
+ * @param array $allowed An array of additional characters that are not to be removed.
+ * @return string Sanitized string
+ */
+ public static function paranoid($string, $allowed = array()) {
+ $allow = null;
+ if (!empty($allowed)) {
+ foreach ($allowed as $value) {
+ $allow .= "\\$value";
+ }
+ }
+
+ 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);
+ }
+ return $cleaned;
+ }
+
+/**
+ * Makes a string SQL-safe.
+ *
+ * @param string $string String to sanitize
+ * @param string $connection Database connection being used
+ * @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;
+ }
+ $string = $db->value($string, 'string');
+ if ($string[0] === 'N') {
+ $string = substr($string, 2);
+ } else {
+ $string = substr($string, 1);
+ }
+
+ $string = substr($string, 0, -1);
+ return $string;
+ }
+
+/**
+ * Returns given string safe for display as HTML. Renders entities.
+ *
+ * strip_tags() does not validating HTML syntax or structure, so it might strip whole passages
+ * with broken HTML.
+ *
+ * ### Options:
+ *
+ * - 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
+ *
+ * @param string $string String from where to strip tags
+ * @param array $options Array of options to use.
+ * @return string Sanitized string
+ */
+ public static function html($string, $options = array()) {
+ static $defaultCharset = false;
+ if ($defaultCharset === false) {
+ $defaultCharset = Configure::read('App.encoding');
+ if ($defaultCharset === null) {
+ $defaultCharset = 'UTF-8';
+ }
+ }
+ $default = array(
+ 'remove' => false,
+ 'charset' => $defaultCharset,
+ 'quotes' => ENT_QUOTES,
+ 'double' => true
+ );
+
+ $options = array_merge($default, $options);
+
+ if ($options['remove']) {
+ $string = strip_tags($string);
+ }
+
+ return htmlentities($string, $options['quotes'], $options['charset'], $options['double']);
+ }
+
+/**
+ * Strips extra whitespace from output
+ *
+ * @param string $str String to 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);
+ }
+
+/**
+ * Strips image tags from output
+ *
+ * @param string $str String to 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;
+ }
+
+/**
+ * Strips scripts and stylesheets from output
+ *
+ * @param string $str String to sanitize
+ * @return string String with <script>, <style>, <link>, <img> elements removed.
+ */
+ public static function stripScripts($str) {
+ return preg_replace('/(<link[^>]+rel="[^"]*stylesheet"[^>]*>|<img[^>]*>|style="[^"]*")|<script[^>]*>.*?<\/script>|<style[^>]*>.*?<\/style>|<!--.*?-->/is', '', $str);
+ }
+
+/**
+ * Strips extra whitespace, images, scripts and stylesheets from output
+ *
+ * @param string $str String to sanitize
+ * @return string sanitized string
+ */
+ public static function stripAll($str) {
+ $str = Sanitize::stripWhitespace($str);
+ $str = Sanitize::stripImages($str);
+ $str = Sanitize::stripScripts($str);
+ return $str;
+ }
+
+/**
+ * Strips the specified tags from output. First parameter is string from
+ * where to remove tags. All subsequent parameters are tags.
+ *
+ * Ex.`$clean = Sanitize::stripTags($dirty, 'b', 'p', 'div');`
+ *
+ * Will remove all `<b>`, `<p>`, and `<div>` tags from the $dirty string.
+ *
+ * @param string $str,... String to sanitize
+ * @return string sanitized String
+ */
+ public static function stripTags($str) {
+ $params = func_get_args();
+
+ for ($i = 1, $count = count($params); $i < $count; $i++) {
+ $str = preg_replace('/<' . $params[$i] . '\b[^>]*>/i', '', $str);
+ $str = preg_replace('/<\/' . $params[$i] . '[^>]*>/i', '', $str);
+ }
+ return $str;
+ }
+
+/**
+ * Sanitizes given array or value for safe input. Use the options to specify
+ * the connection to use, and what filters should be applied (with a boolean
+ * value). Valid filters:
+ *
+ * - odd_spaces - removes any non space whitespace characters
+ * - encode - Encode any html entities. Encode must be true for the `remove_html` to work.
+ * - dollar - Escape `$` with `\$`
+ * - carriage - Remove `\r`
+ * - unicode -
+ * - escape - Should the string be SQL escaped.
+ * - backslash -
+ * - remove_html - Strip HTML with strip_tags. `encode` must be true for this option to work.
+ *
+ * @param string|array $data Data to sanitize
+ * @param string|array $options If string, DB connection being used, otherwise set of options
+ * @return mixed Sanitized data
+ */
+ public static function clean($data, $options = array()) {
+ if (empty($data)) {
+ return $data;
+ }
+
+ if (is_string($options)) {
+ $options = array('connection' => $options);
+ } elseif (!is_array($options)) {
+ $options = array();
+ }
+
+ $options = array_merge(array(
+ 'connection' => 'default',
+ 'odd_spaces' => true,
+ 'remove_html' => false,
+ 'encode' => true,
+ 'dollar' => true,
+ 'carriage' => true,
+ '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("/&amp;#([0-9]+);/s", "&#\\1;", $data);
+ }
+ if ($options['escape']) {
+ $data = Sanitize::escape($data, $options['connection']);
+ }
+ if ($options['backslash']) {
+ $data = preg_replace("/\\\(?!&amp;#|\?#)/", "\\", $data);
+ }
+ return $data;
+ }
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Security.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Security.php
new file mode 100644
index 0000000..6438338
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Security.php
@@ -0,0 +1,192 @@
+<?php
+/**
+ * Core Security
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+App::uses('String', 'Utility');
+
+/**
+ * Security Library contains utility methods related to security
+ *
+ * @package Cake.Utility
+ */
+class Security {
+
+/**
+ * Default hash method
+ *
+ * @var string
+ */
+ public static $hashType = null;
+
+/**
+ * Get allowed minutes of inactivity based on security level.
+ *
+ * @return integer Allowed inactivity in minutes
+ */
+ public static function inactiveMins() {
+ switch (Configure::read('Security.level')) {
+ case 'high':
+ return 10;
+ break;
+ case 'medium':
+ return 100;
+ break;
+ case 'low':
+ default:
+ return 300;
+ break;
+ }
+ }
+
+/**
+ * Generate authorization hash.
+ *
+ * @return string Hash
+ */
+ public static function generateAuthKey() {
+ return Security::hash(String::uuid());
+ }
+
+/**
+ * Validate authorization hash.
+ *
+ * @param string $authKey Authorization hash
+ * @return boolean Success
+ * @todo Complete implementation
+ */
+ public static function validateAuthKey($authKey) {
+ return true;
+ }
+
+/**
+ * Create a hash from string using given method.
+ * Fallback on next available method.
+ *
+ * @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)
+ * @return string 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 = strtolower($type);
+
+ if ($type == 'sha1' || $type == null) {
+ if (function_exists('sha1')) {
+ $return = sha1($string);
+ return $return;
+ }
+ $type = 'sha256';
+ }
+
+ if ($type == 'sha256' && function_exists('mhash')) {
+ return bin2hex(mhash(MHASH_SHA256, $string));
+ }
+
+ if (function_exists('hash')) {
+ return hash($type, $string);
+ }
+ return md5($string);
+ }
+
+/**
+ * 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)
+ * @return void
+ * @see Security::hash()
+ */
+ public static function setHash($hash) {
+ self::$hashType = $hash;
+ }
+
+/**
+ * Encrypts/Decrypts a text using the given key.
+ *
+ * @param string $text Encrypted string to decrypt, normal string to encrypt
+ * @param string $key Key to use
+ * @return string Encrypted/Decrypted string
+ */
+ 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);
+ return '';
+ }
+
+ srand(Configure::read('Security.cipherSeed'));
+ $out = '';
+ $keyLength = strlen($key);
+ for ($i = 0, $textLength = strlen($text); $i < $textLength; $i++) {
+ $j = ord(substr($key, $i % $keyLength, 1));
+ while ($j--) {
+ rand(0, 255);
+ }
+ $mask = rand(0, 255);
+ $out .= chr(ord(substr($text, $i, 1)) ^ $mask);
+ }
+ srand();
+ return $out;
+ }
+
+/**
+ * Encrypts/Decrypts a text using the given key using rijndael method.
+ *
+ * @param string $text Encrypted string to decrypt, normal string to encrypt
+ * @param string $key Key to use
+ * @param string $operation Operation to perform, encrypt or decrypt
+ * @return string Encrypted/Descrypted 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);
+ return '';
+ }
+ if (empty($operation) || !in_array($operation, array('encrypt', 'decrypt'))) {
+ trigger_error(__d('cake_dev', 'You must specify the operation for Security::rijndael(), either encrypt or decrypt'), E_USER_WARNING);
+ return '';
+ }
+ if (strlen($key) < 32) {
+ 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';
+ $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");
+ }
+ return $out;
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Set.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Set.php
new file mode 100644
index 0000000..3bfa008
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Set.php
@@ -0,0 +1,1108 @@
+<?php
+/**
+ * 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)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+App::uses('String', 'Utility');
+App::uses('Hash', 'Utility');
+
+/**
+ * Class used for manipulation of arrays.
+ *
+ * @package Cake.Utility
+ */
+class Set {
+
+/**
+ * This function can be thought of as a hybrid between PHP's array_merge and array_merge_recursive. The difference
+ * 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
+ * order numeric keys containing arrays, results can be lossy.
+ *
+ * Note: This function will work with an unlimited amount of arguments and typecasts non-array
+ * parameters into arrays.
+ *
+ * @param array $data Array to be merged
+ * @param array $merge Array to merge with
+ * @return array Merged array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::merge
+ */
+ public static function merge($data, $merge = null) {
+ $args = func_get_args();
+ if (empty($args[1])) {
+ return (array)$args[0];
+ }
+ if (!is_array($args[0])) {
+ $args[0] = (array)$args[0];
+ }
+ return call_user_func_array('Hash::merge', $args);
+ }
+
+/**
+ * Filters empty elements out of a route array, excluding '0'.
+ *
+ * @param array $var Either an array to filter, or value when in callback
+ * @return mixed Either filtered array, or true/false when in callback
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::filter
+ */
+ public static function filter(array $var) {
+ return Hash::filter($var);
+ }
+
+/**
+ * Pushes the differences in $array2 onto the end of $array
+ *
+ * @param array $array Original array
+ * @param array $array2 Differences to push
+ * @return array Combined array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::pushDiff
+ */
+ public static function pushDiff($array, $array2) {
+ if (empty($array) && !empty($array2)) {
+ return $array2;
+ }
+ if (!empty($array) && !empty($array2)) {
+ foreach ($array2 as $key => $value) {
+ if (!array_key_exists($key, $array)) {
+ $array[$key] = $value;
+ } else {
+ if (is_array($value)) {
+ $array[$key] = Set::pushDiff($array[$key], $array2[$key]);
+ }
+ }
+ }
+ }
+ return $array;
+ }
+
+/**
+ * Maps the contents of the Set object to an object hierarchy.
+ * Maintains numeric keys as arrays of objects
+ *
+ * @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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::map
+ */
+ public static function map($class = 'stdClass', $tmp = 'stdClass') {
+ if (is_array($class)) {
+ $val = $class;
+ $class = $tmp;
+ }
+
+ if (empty($val)) {
+ return null;
+ }
+ return Set::_map($val, $class);
+ }
+
+/**
+ * Maps the given value as an object. If $value is an object,
+ * it returns $value. Otherwise it maps $value as an object of
+ * type $class, and if primary assign _name_ $key on first array.
+ * If $value is not empty, it will be used to set properties of
+ * returned object (recursively). If $key is numeric will maintain array
+ * structure
+ *
+ * @param array $array Array to map
+ * @param string $class Class name
+ * @param boolean $primary whether to assign first array key as the _name_
+ * @return mixed Mapped object
+ */
+ protected static function _map(&$array, $class, $primary = false) {
+ if ($class === true) {
+ $out = new stdClass;
+ } else {
+ $out = new $class;
+ }
+ if (is_array($array)) {
+ $keys = array_keys($array);
+ foreach ($array as $key => $value) {
+ if ($keys[0] === $key && $class !== true) {
+ $primary = true;
+ }
+ if (is_numeric($key)) {
+ if (is_object($out)) {
+ $out = get_object_vars($out);
+ }
+ $out[$key] = Set::_map($value, $class);
+ if (is_object($out[$key])) {
+ if ($primary !== true && is_array($value) && Set::countDim($value, true) === 2) {
+ if (!isset($out[$key]->_name_)) {
+ $out[$key]->_name_ = $primary;
+ }
+ }
+ }
+ } elseif (is_array($value)) {
+ if ($primary === true) {
+ // @codingStandardsIgnoreStart Legacy junk
+ if (!isset($out->_name_)) {
+ $out->_name_ = $key;
+ }
+ // @codingStandardsIgnoreEnd
+ $primary = false;
+ foreach ($value as $key2 => $value2) {
+ $out->{$key2} = Set::_map($value2, true);
+ }
+ } else {
+ if (!is_numeric($key)) {
+ $out->{$key} = Set::_map($value, true, $key);
+ if (is_object($out->{$key}) && !is_numeric($key)) {
+ if (!isset($out->{$key}->_name_)) {
+ $out->{$key}->_name_ = $key;
+ }
+ }
+ } else {
+ $out->{$key} = Set::_map($value, true);
+ }
+ }
+ } else {
+ $out->{$key} = $value;
+ }
+ }
+ } else {
+ $out = $array;
+ }
+ return $out;
+ }
+
+/**
+ * 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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::numeric
+ */
+ public static function numeric($array = null) {
+ return Hash::numeric($array);
+ }
+
+/**
+ * Return a value from an array list if the key exists.
+ *
+ * If a comma separated $list is passed arrays are numeric with the key of the first being 0
+ * $list = 'no, yes' would translate to $list = array(0 => 'no', 1 => 'yes');
+ *
+ * If an array is used, keys can be strings example: array('no' => 0, 'yes' => 1);
+ *
+ * $list defaults to 0 = no 1 = yes if param is not passed
+ *
+ * @param array $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
+ */
+ public static function enum($select, $list = null) {
+ if (empty($list)) {
+ $list = array('no', 'yes');
+ }
+
+ $return = null;
+ $list = Set::normalize($list, false);
+
+ if (array_key_exists($select, $list)) {
+ $return = $list[$select];
+ }
+ return $return;
+ }
+
+/**
+ * Returns a series of values extracted from an array, formatted in a format string.
+ *
+ * @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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::format
+ */
+ public static function format($data, $format, $keys) {
+ $extracted = array();
+ $count = count($keys);
+
+ if (!$count) {
+ return;
+ }
+
+ for ($i = 0; $i < $count; $i++) {
+ $extracted[] = Set::extract($data, $keys[$i]);
+ }
+ $out = array();
+ $data = $extracted;
+ $count = count($data[0]);
+
+ if (preg_match_all('/\{([0-9]+)\}/msi', $format, $keys2) && isset($keys2[1])) {
+ $keys = $keys2[1];
+ $format = preg_split('/\{([0-9]+)\}/msi', $format);
+ $count2 = count($format);
+
+ for ($j = 0; $j < $count; $j++) {
+ $formatted = '';
+ for ($i = 0; $i <= $count2; $i++) {
+ if (isset($format[$i])) {
+ $formatted .= $format[$i];
+ }
+ if (isset($keys[$i]) && isset($data[$keys[$i]][$j])) {
+ $formatted .= $data[$keys[$i]][$j];
+ }
+ }
+ $out[] = $formatted;
+ }
+ } else {
+ $count2 = count($data);
+ for ($j = 0; $j < $count; $j++) {
+ $args = array();
+ for ($i = 0; $i < $count2; $i++) {
+ if (array_key_exists($j, $data[$i])) {
+ $args[] = $data[$i][$j];
+ }
+ }
+ $out[] = vsprintf($format, $args);
+ }
+ }
+ return $out;
+ }
+
+/**
+ * Implements partial support for XPath 2.0. If $path does not contain a '/' the call
+ * is delegated to Set::classicExtract(). Also the $path and $data arguments are
+ * reversible.
+ *
+ * #### Currently implemented selectors:
+ *
+ * - /User/id (similar to the classic {n}.User.id)
+ * - /User[2]/name (selects the name of the second User)
+ * - /User[id>2] (selects all Users with an id > 2)
+ * - /User[id>2][<5] (selects all Users with an id > 2 but < 5)
+ * - /Post/Comment[author_name=john]/../name (Selects the name of all Posts that have at least one Comment written by john)
+ * - /Posts[name] (Selects all Posts that have a 'name' key)
+ * - /Comment/.[1] (Selects the contents of the first comment)
+ * - /Comment/.[:last] (Selects the last comment)
+ * - /Comment/.[:first] (Selects the first comment)
+ * - /Comment[text=/cakephp/i] (Selects the all comments that have a text matching the regex /cakephp/i)
+ * - /Comment/@* (Selects the all key names of all comments)
+ *
+ * #### Other limitations:
+ *
+ * - Only absolute paths starting with a single '/' are supported right now
+ *
+ * **Warning**: Even so it has plenty of unit tests the XPath support has not gone through a lot of
+ * real-world testing. Please report Bugs as you find them. Suggestions for additional features to
+ * implement are also very welcome!
+ *
+ * @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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::extract
+ */
+ public static function extract($path, $data = null, $options = array()) {
+ if (is_string($data)) {
+ $tmp = $data;
+ $data = $path;
+ $path = $tmp;
+ }
+ if (strpos($path, '/') === false) {
+ return Set::classicExtract($data, $path);
+ }
+ if (empty($data)) {
+ return array();
+ }
+ if ($path === '/') {
+ return $data;
+ }
+ $contexts = $data;
+ $options = array_merge(array('flatten' => true), $options);
+ if (!isset($contexts[0])) {
+ $current = current($data);
+ if ((is_array($current) && count($data) < 1) || !is_array($current) || !Set::numeric(array_keys($data))) {
+ $contexts = array($data);
+ }
+ }
+ $tokens = array_slice(preg_split('/(?<!=|\\\\)\/(?![a-z-\s]*\])/', $path), 1);
+
+ do {
+ $token = array_shift($tokens);
+ $conditions = false;
+ if (preg_match_all('/\[([^=]+=\/[^\/]+\/|[^\]]+)\]/', $token, $m)) {
+ $conditions = $m[1];
+ $token = substr($token, 0, strpos($token, '['));
+ }
+ $matches = array();
+ foreach ($contexts as $key => $context) {
+ if (!isset($context['trace'])) {
+ $context = array('trace' => array(null), 'item' => $context, 'key' => $key);
+ }
+ if ($token === '..') {
+ if (count($context['trace']) == 1) {
+ $context['trace'][] = $context['key'];
+ }
+ $parent = implode('/', $context['trace']) . '/.';
+ $context['item'] = Set::extract($parent, $data);
+ $context['key'] = array_pop($context['trace']);
+ if (isset($context['trace'][1]) && $context['trace'][1] > 0) {
+ $context['item'] = $context['item'][0];
+ } elseif (!empty($context['item'][$key])) {
+ $context['item'] = $context['item'][$key];
+ } else {
+ $context['item'] = array_shift($context['item']);
+ }
+ $matches[] = $context;
+ continue;
+ }
+ if ($token === '@*' && is_array($context['item'])) {
+ $matches[] = array(
+ 'trace' => array_merge($context['trace'], (array)$key),
+ 'key' => $key,
+ 'item' => array_keys($context['item']),
+ );
+ } elseif (is_array($context['item'])
+ && array_key_exists($token, $context['item'])
+ && !(strval($key) === strval($token) && count($tokens) == 1 && $tokens[0] === '.')) {
+ $items = $context['item'][$token];
+ if (!is_array($items)) {
+ $items = array($items);
+ } elseif (!isset($items[0])) {
+ $current = current($items);
+ $currentKey = key($items);
+ if (!is_array($current) || (is_array($current) && count($items) <= 1 && !is_numeric($currentKey))) {
+ $items = array($items);
+ }
+ }
+
+ foreach ($items as $key => $item) {
+ $ctext = array($context['key']);
+ if (!is_numeric($key)) {
+ $ctext[] = $token;
+ $tok = array_shift($tokens);
+ if (isset($items[$tok])) {
+ $ctext[] = $tok;
+ $item = $items[$tok];
+ $matches[] = array(
+ 'trace' => array_merge($context['trace'], $ctext),
+ 'key' => $tok,
+ 'item' => $item,
+ );
+ break;
+ } elseif ($tok !== null) {
+ array_unshift($tokens, $tok);
+ }
+ } else {
+ $key = $token;
+ }
+
+ $matches[] = array(
+ 'trace' => array_merge($context['trace'], $ctext),
+ 'key' => $key,
+ 'item' => $item,
+ );
+ }
+ } elseif ($key === $token || (ctype_digit($token) && $key == $token) || $token === '.') {
+ $context['trace'][] = $key;
+ $matches[] = array(
+ 'trace' => $context['trace'],
+ 'key' => $key,
+ 'item' => $context['item'],
+ );
+ }
+ }
+ if ($conditions) {
+ foreach ($conditions as $condition) {
+ $filtered = array();
+ $length = count($matches);
+ foreach ($matches as $i => $match) {
+ if (Set::matches(array($condition), $match['item'], $i + 1, $length)) {
+ $filtered[$i] = $match;
+ }
+ }
+ $matches = $filtered;
+ }
+ }
+ $contexts = $matches;
+
+ if (empty($tokens)) {
+ break;
+ }
+ } while (1);
+
+ $r = array();
+
+ foreach ($matches as $match) {
+ if ((!$options['flatten'] || is_array($match['item'])) && !is_int($match['key'])) {
+ $r[] = array($match['key'] => $match['item']);
+ } else {
+ $r[] = $match['item'];
+ }
+ }
+ return $r;
+ }
+
+/**
+ * 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
+ * @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) {
+ if (empty($conditions)) {
+ return true;
+ }
+ if (is_string($conditions)) {
+ return !!Set::extract($conditions, $data);
+ }
+ foreach ($conditions as $condition) {
+ if ($condition === ':last') {
+ if ($i != $length) {
+ return false;
+ }
+ continue;
+ } elseif ($condition === ':first') {
+ if ($i != 1) {
+ return false;
+ }
+ continue;
+ }
+ if (!preg_match('/(.+?)([><!]?[=]|[><])(.*)/', $condition, $match)) {
+ if (ctype_digit($condition)) {
+ if ($i != $condition) {
+ return false;
+ }
+ } elseif (preg_match_all('/(?:^[0-9]+|(?<=,)[0-9]+)/', $condition, $matches)) {
+ return in_array($i, $matches[0]);
+ } elseif (!array_key_exists($condition, $data)) {
+ return false;
+ }
+ continue;
+ }
+ list(, $key, $op, $expected) = $match;
+ if (!(isset($data[$key]) || array_key_exists($key, $data))) {
+ return false;
+ }
+
+ $val = $data[$key];
+
+ if ($op === '=' && $expected && $expected{0} === '/') {
+ return preg_match($expected, $val);
+ }
+ if ($op === '=' && $val != $expected) {
+ return false;
+ }
+ if ($op === '!=' && $val == $expected) {
+ return false;
+ }
+ if ($op === '>' && $val <= $expected) {
+ return false;
+ }
+ if ($op === '<' && $val >= $expected) {
+ return false;
+ }
+ if ($op === '<=' && $val > $expected) {
+ return false;
+ }
+ if ($op === '>=' && $val < $expected) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+/**
+ * Gets a value from an array or object that is contained in a given path using an array path syntax, i.e.:
+ * "{n}.Person.{[a-z]+}" - Where "{n}" represents a numeric key, "Person" represents a string literal,
+ * and "{[a-z]+}" (i.e. any string literal enclosed in brackets besides {n} and {s}) is interpreted as
+ * a regular expression.
+ *
+ * @param array $data Array from where to extract
+ * @param string|array $path As an array, or as a dot-separated string.
+ * @return array Extracted data
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::classicExtract
+ */
+ public static function classicExtract($data, $path = null) {
+ if (empty($path)) {
+ return $data;
+ }
+ if (is_object($data)) {
+ if (!($data instanceof ArrayAccess || $data instanceof Traversable)) {
+ $data = get_object_vars($data);
+ }
+ }
+ if (empty($data)) {
+ return null;
+ }
+ if (is_string($path) && strpos($path, '{') !== false) {
+ $path = String::tokenize($path, '.', '{', '}');
+ } elseif (is_string($path)) {
+ $path = explode('.', $path);
+ }
+ $tmp = array();
+
+ if (empty($path)) {
+ return null;
+ }
+
+ foreach ($path as $i => $key) {
+ if (is_numeric($key) && intval($key) > 0 || $key === '0') {
+ if (isset($data[$key])) {
+ $data = $data[$key];
+ } else {
+ return null;
+ }
+ } elseif ($key === '{n}') {
+ foreach ($data as $j => $val) {
+ if (is_int($j)) {
+ $tmpPath = array_slice($path, $i + 1);
+ if (empty($tmpPath)) {
+ $tmp[] = $val;
+ } else {
+ $tmp[] = Set::classicExtract($val, $tmpPath);
+ }
+ }
+ }
+ return $tmp;
+ } elseif ($key === '{s}') {
+ foreach ($data as $j => $val) {
+ if (is_string($j)) {
+ $tmpPath = array_slice($path, $i + 1);
+ if (empty($tmpPath)) {
+ $tmp[] = $val;
+ } else {
+ $tmp[] = Set::classicExtract($val, $tmpPath);
+ }
+ }
+ }
+ return $tmp;
+ } elseif (false !== strpos($key, '{') && false !== strpos($key, '}')) {
+ $pattern = substr($key, 1, -1);
+
+ foreach ($data as $j => $val) {
+ if (preg_match('/^' . $pattern . '/s', $j) !== 0) {
+ $tmpPath = array_slice($path, $i + 1);
+ if (empty($tmpPath)) {
+ $tmp[$j] = $val;
+ } else {
+ $tmp[$j] = Set::classicExtract($val, $tmpPath);
+ }
+ }
+ }
+ return $tmp;
+ } else {
+ if (isset($data[$key])) {
+ $data = $data[$key];
+ } else {
+ return null;
+ }
+ }
+ }
+ return $data;
+ }
+
+/**
+ * Inserts $data into an array as defined by $path.
+ *
+ * @param array $list Where to insert into
+ * @param string $path A dot-separated string.
+ * @param array $data Data to insert
+ * @return array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::insert
+ */
+ public static function insert($list, $path, $data = null) {
+ return Hash::insert($list, $path, $data);
+ }
+
+/**
+ * Removes an element from a Set or array as defined by $path.
+ *
+ * @param array $list From where to remove
+ * @param string $path A dot-separated string.
+ * @return array Array with $path removed from its value
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::remove
+ */
+ public static function remove($list, $path = null) {
+ return Hash::remove($list, $path);
+ }
+
+/**
+ * Checks if a particular path is set in an array
+ *
+ * @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
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::check
+ */
+ public static function check($data, $path = null) {
+ if (empty($path)) {
+ return $data;
+ }
+ if (!is_array($path)) {
+ $path = explode('.', $path);
+ }
+
+ foreach ($path as $i => $key) {
+ if (is_numeric($key) && intval($key) > 0 || $key === '0') {
+ $key = intval($key);
+ }
+ if ($i === count($path) - 1) {
+ return (is_array($data) && array_key_exists($key, $data));
+ }
+
+ if (!is_array($data) || !array_key_exists($key, $data)) {
+ return false;
+ }
+ $data =& $data[$key];
+ }
+ return true;
+ }
+
+/**
+ * Computes the difference between a Set and an array, two Sets, or two arrays
+ *
+ * @param mixed $val1 First value
+ * @param mixed $val2 Second value
+ * @return array Returns the key => value pairs that are not common in $val1 and $val2
+ * The expression for this function is($val1 - $val2) + ($val2 - ($val1 - $val2))
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::diff
+ */
+ public static function diff($val1, $val2 = null) {
+ if (empty($val1)) {
+ return (array)$val2;
+ }
+ if (empty($val2)) {
+ return (array)$val1;
+ }
+ $intersection = array_intersect_key($val1, $val2);
+ while (($key = key($intersection)) !== null) {
+ if ($val1[$key] == $val2[$key]) {
+ unset($val1[$key]);
+ unset($val2[$key]);
+ }
+ next($intersection);
+ }
+
+ return $val1 + $val2;
+ }
+
+/**
+ * Determines if one Set or array contains the exact keys and values of another.
+ *
+ * @param array $val1 First value
+ * @param array $val2 Second value
+ * @return boolean 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) {
+ if (empty($val1) || empty($val2)) {
+ return false;
+ }
+
+ foreach ($val2 as $key => $val) {
+ if (is_numeric($key)) {
+ Set::contains($val, $val1);
+ } else {
+ if (!isset($val1[$key]) || $val1[$key] != $val) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+/**
+ * Counts the dimensions of an array. If $all is set to false (which is the default) it will
+ * 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
+ * @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) {
+ if ($all) {
+ $depth = array($count);
+ if (is_array($array) && reset($array) !== false) {
+ foreach ($array as $value) {
+ $depth[] = Set::countDim($value, true, $count + 1);
+ }
+ }
+ $return = max($depth);
+ } else {
+ if (is_array(reset($array))) {
+ $return = Set::countDim(reset($array)) + 1;
+ } else {
+ $return = 1;
+ }
+ }
+ return $return;
+ }
+
+/**
+ * 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 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
+ * @return array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::normalize
+ */
+ public static function normalize($list, $assoc = true, $sep = ',', $trim = true) {
+ if (is_string($list)) {
+ $list = explode($sep, $list);
+ if ($trim) {
+ foreach ($list as $key => $value) {
+ $list[$key] = trim($value);
+ }
+ }
+ if ($assoc) {
+ return Hash::normalize($list);
+ }
+ } elseif (is_array($list)) {
+ $list = Hash::normalize($list, $assoc);
+ }
+ return $list;
+ }
+
+/**
+ * Creates an associative array using a $path1 as the path to build its keys, and optionally
+ * $path2 as path to get the values. If $path2 is not specified, all values will be initialized
+ * to null (useful for Set::merge). You can optionally group the values by what is obtained when
+ * following the path specified in $groupPath.
+ *
+ * @param array|object $data Array or object from where to extract keys and values
+ * @param string|array $path1 As an array, or as a dot-separated string.
+ * @param string|array $path2 As an array, or as a dot-separated string.
+ * @param string $groupPath As an array, or as a dot-separated string.
+ * @return array Combined array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::combine
+ */
+ public static function combine($data, $path1 = null, $path2 = null, $groupPath = null) {
+ if (empty($data)) {
+ return array();
+ }
+
+ if (is_object($data)) {
+ if (!($data instanceof ArrayAccess || $data instanceof Traversable)) {
+ $data = get_object_vars($data);
+ }
+ }
+
+ if (is_array($path1)) {
+ $format = array_shift($path1);
+ $keys = Set::format($data, $format, $path1);
+ } else {
+ $keys = Set::extract($data, $path1);
+ }
+ if (empty($keys)) {
+ return array();
+ }
+
+ if (!empty($path2) && is_array($path2)) {
+ $format = array_shift($path2);
+ $vals = Set::format($data, $format, $path2);
+ } elseif (!empty($path2)) {
+ $vals = Set::extract($data, $path2);
+ } else {
+ $count = count($keys);
+ for ($i = 0; $i < $count; $i++) {
+ $vals[$i] = null;
+ }
+ }
+
+ if ($groupPath != null) {
+ $group = Set::extract($data, $groupPath);
+ if (!empty($group)) {
+ $c = count($keys);
+ for ($i = 0; $i < $c; $i++) {
+ if (!isset($group[$i])) {
+ $group[$i] = 0;
+ }
+ if (!isset($out[$group[$i]])) {
+ $out[$group[$i]] = array();
+ }
+ $out[$group[$i]][$keys[$i]] = $vals[$i];
+ }
+ return $out;
+ }
+ }
+ if (empty($vals)) {
+ return array();
+ }
+ return array_combine($keys, $vals);
+ }
+
+/**
+ * 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
+ */
+ public static function reverse($object) {
+ $out = array();
+ if ($object instanceof SimpleXMLElement) {
+ return Xml::toArray($object);
+ } elseif (is_object($object)) {
+ $keys = get_object_vars($object);
+ if (isset($keys['_name_'])) {
+ $identity = $keys['_name_'];
+ unset($keys['_name_']);
+ }
+ $new = array();
+ foreach ($keys as $key => $value) {
+ if (is_array($value)) {
+ $new[$key] = (array)Set::reverse($value);
+ } else {
+ // @codingStandardsIgnoreStart Legacy junk
+ if (isset($value->_name_)) {
+ $new = array_merge($new, Set::reverse($value));
+ } else {
+ $new[$key] = Set::reverse($value);
+ }
+ // @codingStandardsIgnoreEnd
+ }
+ }
+ if (isset($identity)) {
+ $out[$identity] = $new;
+ } else {
+ $out = $new;
+ }
+ } elseif (is_array($object)) {
+ foreach ($object as $key => $value) {
+ $out[$key] = Set::reverse($value);
+ }
+ } else {
+ $out = $object;
+ }
+ return $out;
+ }
+
+/**
+ * Collapses a multi-dimensional array into a single dimension, using a delimited array path for
+ * each array element's key, i.e. array(array('Foo' => array('Bar' => 'Far'))) becomes
+ * array('0.Foo.Bar' => 'Far').
+ *
+ * @param array $data Array to flatten
+ * @param string $separator String used to separate array key elements in a path, defaults to '.'
+ * @return array
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::flatten
+ */
+ public static function flatten($data, $separator = '.') {
+ return Hash::flatten($data, $separator);
+ }
+
+/**
+ * Expand/unflattens an 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
+ * `array(array('Foo' => array('Bar' => 'Far')))`.
+ *
+ * @param array $data Flattened array
+ * @param string $separator The delimiter used
+ * @return array
+ */
+ public static function expand($data, $separator = '.') {
+ return Hash::expand($data, $separator);
+ }
+
+/**
+ * Flattens an array for sorting
+ *
+ * @param array $results
+ * @param string $key
+ * @return array
+ */
+ protected static function _flatten($results, $key = null) {
+ $stack = array();
+ foreach ($results as $k => $r) {
+ $id = $k;
+ if (!is_null($key)) {
+ $id = $key;
+ }
+ if (is_array($r) && !empty($r)) {
+ $stack = array_merge($stack, Set::_flatten($r, $id));
+ } else {
+ $stack[] = array('id' => $id, 'value' => $r);
+ }
+ }
+ return $stack;
+ }
+
+/**
+ * Sorts an array by any value, determined by a Set-compatible path
+ *
+ * @param array $data An array of data to sort
+ * @param string $path A Set-compatible path to the array value
+ * @param string $dir Direction of sorting - either ascending (ASC), or descending (DESC)
+ * @return array Sorted array of data
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::sort
+ */
+ public static function sort($data, $path, $dir) {
+ $originalKeys = array_keys($data);
+ $numeric = false;
+ if (is_numeric(implode('', $originalKeys))) {
+ $data = array_values($data);
+ $numeric = true;
+ }
+ $result = Set::_flatten(Set::extract($data, $path));
+ list($keys, $values) = array(Set::extract($result, '{n}.id'), Set::extract($result, '{n}.value'));
+
+ $dir = strtolower($dir);
+ if ($dir === 'asc') {
+ $dir = SORT_ASC;
+ } elseif ($dir === 'desc') {
+ $dir = SORT_DESC;
+ }
+ array_multisort($values, $dir, $keys, $dir);
+ $sorted = array();
+ $keys = array_unique($keys);
+
+ foreach ($keys as $k) {
+ if ($numeric) {
+ $sorted[] = $data[$k];
+ } else {
+ if (isset($originalKeys[$k])) {
+ $sorted[$originalKeys[$k]] = $data[$originalKeys[$k]];
+ } else {
+ $sorted[$k] = $data[$k];
+ }
+ }
+ }
+ return $sorted;
+ }
+
+/**
+ * Allows the application of a callback method to elements of an
+ * array extracted by a Set::extract() compatible path.
+ *
+ * @param mixed $path Set-compatible path to the array value
+ * @param array $data An array of data to extract from & then process with the $callback.
+ * @param mixed $callback Callback method to be applied to extracted data.
+ * See http://ca2.php.net/manual/en/language.pseudo-types.php#language.types.callback for examples
+ * of callback formats.
+ * @param array $options Options are:
+ * - type : can be pass, map, or reduce. Map will handoff the given callback
+ * to array_map, reduce will handoff to array_reduce, and pass will
+ * use call_user_func_array().
+ * @return mixed Result of the callback when applied to extracted data
+ * @link http://book.cakephp.org/2.0/en/core-utility-libraries/set.html#Set::apply
+ */
+ public static function apply($path, $data, $callback, $options = array()) {
+ $defaults = array('type' => 'pass');
+ $options = array_merge($defaults, $options);
+ $extracted = Set::extract($path, $data);
+
+ if ($options['type'] === 'map') {
+ return array_map($callback, $extracted);
+ } elseif ($options['type'] === 'reduce') {
+ return array_reduce($extracted, $callback);
+ } elseif ($options['type'] === 'pass') {
+ return call_user_func_array($callback, array($extracted));
+ }
+ return null;
+ }
+
+/**
+ * Takes in a flat array and returns a nested array
+ *
+ * @param mixed $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
+ * parentPath - the path to a key that identifies the parent of each entry
+ * root - the id of the desired top-most result
+ * @return array of results, nested
+ * @link
+ */
+ public static function nest($data, $options = array()) {
+ if (!$data) {
+ return $data;
+ }
+
+ $alias = key(current($data));
+ $options += array(
+ 'idPath' => "/$alias/id",
+ 'parentPath' => "/$alias/parent_id",
+ 'children' => 'children',
+ 'root' => null
+ );
+
+ $return = $idMap = array();
+ $ids = Set::extract($data, $options['idPath']);
+ $idKeys = explode('/', trim($options['idPath'], '/'));
+ $parentKeys = explode('/', trim($options['parentPath'], '/'));
+
+ foreach ($data as $result) {
+ $result[$options['children']] = array();
+
+ $id = Set::get($result, $idKeys);
+ $parentId = Set::get($result, $parentKeys);
+
+ if (isset($idMap[$id][$options['children']])) {
+ $idMap[$id] = array_merge($result, (array)$idMap[$id]);
+ } else {
+ $idMap[$id] = array_merge($result, array($options['children'] => array()));
+ }
+ if (!$parentId || !in_array($parentId, $ids)) {
+ $return[] =& $idMap[$id];
+ } else {
+ $idMap[$parentId][$options['children']][] =& $idMap[$id];
+ }
+ }
+
+ if ($options['root']) {
+ $root = $options['root'];
+ } else {
+ $root = Set::get($return[0], $parentKeys);
+ }
+
+ foreach ($return as $i => $result) {
+ $id = Set::get($result, $idKeys);
+ $parentId = Set::get($result, $parentKeys);
+ if ($id !== $root && $parentId != $root) {
+ unset($return[$i]);
+ }
+ }
+
+ return array_values($return);
+ }
+
+/**
+ * Return the value at the specified position
+ *
+ * @param array $input an array
+ * @param string|array $path string or array of array keys
+ * @return the value at the specified position or null if it doesn't exist
+ */
+ public static function get($input, $path = null) {
+ if (is_string($path)) {
+ if (strpos($path, '/') !== false) {
+ $keys = explode('/', trim($path, '/'));
+ } else {
+ $keys = explode('.', trim($path, '.'));
+ }
+ } else {
+ $keys = $path;
+ }
+ return Hash::get($input, $keys);
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/String.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/String.php
new file mode 100644
index 0000000..01f9acb
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/String.php
@@ -0,0 +1,605 @@
+<?php
+/**
+ * String handling methods.
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+/**
+ * String handling methods.
+ *
+ *
+ * @package Cake.Utility
+ */
+class String {
+
+/**
+ * Generate a random UUID
+ *
+ * @see http://www.ietf.org/rfc/rfc4122.txt
+ * @return RFC 4122 UUID
+ */
+ public static function uuid() {
+ $node = env('SERVER_ADDR');
+
+ if (strpos($node, ':') !== false) {
+ if (substr_count($node, '::')) {
+ $node = str_replace(
+ '::', str_repeat(':0000', 8 - substr_count($node, ':')) . ':', $node
+ );
+ }
+ $node = explode(':', $node);
+ $ipSix = '';
+
+ foreach ($node as $id) {
+ $ipSix .= str_pad(base_convert($id, 16, 2), 16, 0, STR_PAD_LEFT);
+ }
+ $node = base_convert($ipSix, 2, 10);
+
+ if (strlen($node) < 38) {
+ $node = null;
+ } else {
+ $node = crc32($node);
+ }
+ } elseif (empty($node)) {
+ $host = env('HOSTNAME');
+
+ if (empty($host)) {
+ $host = env('HOST');
+ }
+
+ if (!empty($host)) {
+ $ip = gethostbyname($host);
+
+ if ($ip === $host) {
+ $node = crc32($host);
+ } else {
+ $node = ip2long($ip);
+ }
+ }
+ } elseif ($node !== '127.0.0.1') {
+ $node = ip2long($node);
+ } else {
+ $node = null;
+ }
+
+ if (empty($node)) {
+ $node = crc32(Configure::read('Security.salt'));
+ }
+
+ if (function_exists('hphp_get_thread_id')) {
+ $pid = hphp_get_thread_id();
+ } elseif (function_exists('zend_thread_id')) {
+ $pid = zend_thread_id();
+ } else {
+ $pid = getmypid();
+ }
+
+ if (!$pid || $pid > 65535) {
+ $pid = mt_rand(0, 0xfff) | 0x4000;
+ }
+
+ list($timeMid, $timeLow) = explode(' ', microtime());
+ $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) {
+ array_push($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);
+ }
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Validation.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Validation.php
new file mode 100644
index 0000000..d709732
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Validation.php
@@ -0,0 +1,937 @@
+<?php
+/**
+ * Validation Class. Used for validation of model data
+ *
+ * PHP Version 5.x
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link http://cakephp.org CakePHP(tm) Project
+ * @package Cake.Utility
+ * @since CakePHP(tm) v 1.2.0.3830
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+App::uses('Multibyte', 'I18n');
+App::uses('File', 'Utility');
+// Load multibyte if the extension is missing.
+if (!function_exists('mb_strlen')) {
+ class_exists('Multibyte');
+}
+
+/**
+ * Offers different validation methods.
+ *
+ * @package Cake.Utility
+ * @since CakePHP v 1.2.0.3830
+ */
+class Validation {
+
+/**
+ * Some complex patterns needed in multiple places
+ *
+ * @var array
+ */
+ protected static $_pattern = array(
+ 'hostname' => '(?:[a-z0-9][-a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,4}|museum|travel)'
+ );
+
+/**
+ * Holds an array of errors messages set in this class.
+ * These are used for debugging purposes
+ *
+ * @var array
+ */
+ public static $errors = array();
+
+/**
+ * Checks that a string contains something other than whitespace
+ *
+ * Returns true if string contains something other than whitespace
+ *
+ * $check can be passed as an array:
+ * array('check' => 'valueToCheck');
+ *
+ * @param string|array $check Value to check
+ * @return boolean Success
+ */
+ public static function notEmpty($check) {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+
+ if (empty($check) && $check != '0') {
+ return false;
+ }
+ return self::_check($check, '/[^\s]+/m');
+ }
+
+/**
+ * Checks that a string contains only integer or letters
+ *
+ * Returns true if string contains only integer or letters
+ *
+ * $check can be passed as an array:
+ * array('check' => 'valueToCheck');
+ *
+ * @param string|array $check Value to check
+ * @return boolean Success
+ */
+ public static function alphaNumeric($check) {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+
+ if (empty($check) && $check != '0') {
+ return false;
+ }
+ return self::_check($check, '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/mu');
+ }
+
+/**
+ * Checks that a string length is within s specified range.
+ * Spaces are included in the character count.
+ * Returns true is string matches value min, max, or between min and max,
+ *
+ * @param string $check Value to check for length
+ * @param integer $min Minimum value in range (inclusive)
+ * @param integer $max Maximum value in range (inclusive)
+ * @return boolean Success
+ */
+ public static function between($check, $min, $max) {
+ $length = mb_strlen($check);
+ return ($length >= $min && $length <= $max);
+ }
+
+/**
+ * Returns true if field is left blank -OR- only whitespace characters are present in it's value
+ * Whitespace characters include Space, Tab, Carriage Return, Newline
+ *
+ * $check can be passed as an array:
+ * array('check' => 'valueToCheck');
+ *
+ * @param string|array $check Value to check
+ * @return boolean Success
+ */
+ public static function blank($check) {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+ return !self::_check($check, '/[^\\s]/');
+ }
+
+/**
+ * Validation of credit card numbers.
+ * Returns true if $check is in the proper credit card format.
+ *
+ * @param string|array $check credit card number to validate
+ * @param string|array $type 'all' may be passed as a sting, defaults to fast which checks format of most major credit cards
+ * if an array is used only the values of the array are checked.
+ * Example: array('amex', 'bankcard', 'maestro')
+ * @param boolean $deep set to true this will check the Luhn algorithm of the credit card.
+ * @param string $regex A custom regex can also be passed, this will be used instead of the defined regex values
+ * @return boolean Success
+ * @see Validation::luhn()
+ */
+ public static function cc($check, $type = 'fast', $deep = false, $regex = null) {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+
+ $check = str_replace(array('-', ' '), '', $check);
+ if (mb_strlen($check) < 13) {
+ return false;
+ }
+
+ if (!is_null($regex)) {
+ if (self::_check($check, $regex)) {
+ return self::luhn($check, $deep);
+ }
+ }
+ $cards = array(
+ 'all' => array(
+ 'amex' => '/^3[4|7]\\d{13}$/',
+ 'bankcard' => '/^56(10\\d\\d|022[1-5])\\d{10}$/',
+ 'diners' => '/^(?:3(0[0-5]|[68]\\d)\\d{11})|(?:5[1-5]\\d{14})$/',
+ 'disc' => '/^(?:6011|650\\d)\\d{12}$/',
+ 'electron' => '/^(?:417500|4917\\d{2}|4913\\d{2})\\d{10}$/',
+ 'enroute' => '/^2(?:014|149)\\d{11}$/',
+ 'jcb' => '/^(3\\d{4}|2100|1800)\\d{11}$/',
+ 'maestro' => '/^(?:5020|6\\d{3})\\d{12}$/',
+ 'mc' => '/^5[1-5]\\d{14}$/',
+ 'solo' => '/^(6334[5-9][0-9]|6767[0-9]{2})\\d{10}(\\d{2,3})?$/',
+ 'switch' => '/^(?:49(03(0[2-9]|3[5-9])|11(0[1-2]|7[4-9]|8[1-2])|36[0-9]{2})\\d{10}(\\d{2,3})?)|(?:564182\\d{10}(\\d{2,3})?)|(6(3(33[0-4][0-9])|759[0-9]{2})\\d{10}(\\d{2,3})?)$/',
+ 'visa' => '/^4\\d{12}(\\d{3})?$/',
+ 'voyager' => '/^8699[0-9]{11}$/'
+ ),
+ 'fast' => '/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})$/'
+ );
+
+ if (is_array($type)) {
+ foreach ($type as $value) {
+ $regex = $cards['all'][strtolower($value)];
+
+ if (self::_check($check, $regex)) {
+ return self::luhn($check, $deep);
+ }
+ }
+ } elseif ($type == 'all') {
+ foreach ($cards['all'] as $value) {
+ $regex = $value;
+
+ if (self::_check($check, $regex)) {
+ return self::luhn($check, $deep);
+ }
+ }
+ } else {
+ $regex = $cards['fast'];
+
+ if (self::_check($check, $regex)) {
+ return self::luhn($check, $deep);
+ }
+ }
+ return false;
+ }
+
+/**
+ * Used to compare 2 numeric values.
+ *
+ * @param string|array $check1 if string is passed for a string must also be passed for $check2
+ * used as an array it must be passed as array('check1' => value, 'operator' => 'value', 'check2' -> value)
+ * @param string $operator Can be either a word or operand
+ * is greater >, is less <, greater or equal >=
+ * less or equal <=, is less <, equal to ==, not equal !=
+ * @param integer $check2 only needed if $check1 is a string
+ * @return boolean Success
+ */
+ public static function comparison($check1, $operator = null, $check2 = null) {
+ if (is_array($check1)) {
+ extract($check1, EXTR_OVERWRITE);
+ }
+ $operator = str_replace(array(' ', "\t", "\n", "\r", "\0", "\x0B"), '', strtolower($operator));
+
+ switch ($operator) {
+ case 'isgreater':
+ case '>':
+ if ($check1 > $check2) {
+ return true;
+ }
+ break;
+ case 'isless':
+ case '<':
+ if ($check1 < $check2) {
+ return true;
+ }
+ break;
+ case 'greaterorequal':
+ case '>=':
+ if ($check1 >= $check2) {
+ return true;
+ }
+ break;
+ case 'lessorequal':
+ case '<=':
+ if ($check1 <= $check2) {
+ return true;
+ }
+ break;
+ case 'equalto':
+ case '==':
+ if ($check1 == $check2) {
+ return true;
+ }
+ break;
+ case 'notequal':
+ case '!=':
+ if ($check1 != $check2) {
+ return true;
+ }
+ break;
+ default:
+ self::$errors[] = __d('cake_dev', 'You must define the $operator parameter for Validation::comparison()');
+ break;
+ }
+ return false;
+ }
+
+/**
+ * Used when a custom regular expression is needed.
+ *
+ * @param string|array $check When used as a string, $regex must also be a valid regular expression.
+ * As and array: array('check' => value, 'regex' => 'valid regular expression')
+ * @param string $regex If $check is passed as a string, $regex must also be set to valid regular expression
+ * @return boolean Success
+ */
+ public static function custom($check, $regex = null) {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+ if ($regex === null) {
+ self::$errors[] = __d('cake_dev', 'You must define a regular expression for Validation::custom()');
+ return false;
+ }
+ return self::_check($check, $regex);
+ }
+
+/**
+ * Date validation, determines if the string passed is a valid date.
+ * keys that expect full month, day and year will validate leap years
+ *
+ * @param string $check a valid date string
+ * @param string|array $format Use a string or an array of the keys below. Arrays should be passed as array('dmy', 'mdy', etc)
+ * Keys: dmy 27-12-2006 or 27-12-06 separators can be a space, period, dash, forward slash
+ * mdy 12-27-2006 or 12-27-06 separators can be a space, period, dash, forward slash
+ * ymd 2006-12-27 or 06-12-27 separators can be a space, period, dash, forward slash
+ * dMy 27 December 2006 or 27 Dec 2006
+ * Mdy December 27, 2006 or Dec 27, 2006 comma is optional
+ * My December 2006 or Dec 2006
+ * my 12/2006 separators can be a space, period, dash, forward slash
+ * @param string $regex If a custom regular expression is used this is the only validation that will occur.
+ * @return boolean Success
+ */
+ public static function date($check, $format = 'ymd', $regex = null) {
+ if (!is_null($regex)) {
+ return self::_check($check, $regex);
+ }
+
+ $regex['dmy'] = '%^(?:(?:31(\\/|-|\\.|\\x20)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)(\\/|-|\\.|\\x20)(?:0?[1,3-9]|1[0-2])\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:29(\\/|-|\\.|\\x20)0?2\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\\d|2[0-8])(\\/|-|\\.|\\x20)(?:(?:0?[1-9])|(?:1[0-2]))\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%';
+ $regex['mdy'] = '%^(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.|\\x20)31)\\1|(?:(?:0?[13-9]|1[0-2])(\\/|-|\\.|\\x20)(?:29|30)\\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:0?2(\\/|-|\\.|\\x20)29\\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(\\/|-|\\.|\\x20)(?:0?[1-9]|1\\d|2[0-8])\\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$%';
+ $regex['ymd'] = '%^(?:(?:(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\\/|-|\\.|\\x20)(?:0?2\\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\\d)?\\d{2})(\\/|-|\\.|\\x20)(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%';
+ $regex['dMy'] = '/^((31(?!\\ (Feb(ruary)?|Apr(il)?|June?|(Sep(?=\\b|t)t?|Nov)(ember)?)))|((30|29)(?!\\ Feb(ruary)?))|(29(?=\\ Feb(ruary)?\\ (((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))|(0?[1-9])|1\\d|2[0-8])\\ (Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)\\ ((1[6-9]|[2-9]\\d)\\d{2})$/';
+ $regex['Mdy'] = '/^(?:(((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?)\\ 31)|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep)(tember)?|(Nov|Dec)(ember)?)\\ (0?[1-9]|([12]\\d)|30))|(Feb(ruary)?\\ (0?[1-9]|1\\d|2[0-8]|(29(?=,?\\ ((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)))))))\\,?\\ ((1[6-9]|[2-9]\\d)\\d{2}))$/';
+ $regex['My'] = '%^(Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)[ /]((1[6-9]|[2-9]\\d)\\d{2})$%';
+ $regex['my'] = '%^(((0[123456789]|10|11|12)([- /.])(([1][9][0-9][0-9])|([2][0-9][0-9][0-9]))))$%';
+
+ $format = (is_array($format)) ? array_values($format) : array($format);
+ foreach ($format as $key) {
+ if (self::_check($check, $regex[$key]) === true) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+/**
+ * Validates a datetime value
+ * All values matching the "date" core validation rule, and the "time" one will be valid
+ *
+ * @param array $check Value to check
+ * @param string|array $dateFormat Format of the date part
+ * Use a string or an array of the keys below. Arrays should be passed as array('dmy', 'mdy', etc)
+ * ## Keys:
+ *
+ * - dmy 27-12-2006 or 27-12-06 separators can be a space, period, dash, forward slash
+ * - mdy 12-27-2006 or 12-27-06 separators can be a space, period, dash, forward slash
+ * - ymd 2006-12-27 or 06-12-27 separators can be a space, period, dash, forward slash
+ * - dMy 27 December 2006 or 27 Dec 2006
+ * - Mdy December 27, 2006 or Dec 27, 2006 comma is optional
+ * - My December 2006 or Dec 2006
+ * - my 12/2006 separators can be a space, period, dash, forward slash
+ * @param string $regex Regex for the date part. If a custom regular expression is used this is the only validation that will occur.
+ * @return boolean True if the value is valid, false otherwise
+ * @see Validation::date
+ * @see Validation::time
+ */
+ public static function datetime($check, $dateFormat = 'ymd', $regex = null) {
+ $valid = false;
+ $parts = explode(' ', $check);
+ if (!empty($parts) && count($parts) > 1) {
+ $time = array_pop($parts);
+ $date = implode(' ', $parts);
+ $valid = self::date($date, $dateFormat, $regex) && self::time($time);
+ }
+ return $valid;
+ }
+
+/**
+ * Time validation, determines if the string passed is a valid time.
+ * Validates time as 24hr (HH:MM) or am/pm ([H]H:MM[a|p]m)
+ * Does not allow/validate seconds.
+ *
+ * @param string $check a valid time string
+ * @return boolean Success
+ */
+ public static function time($check) {
+ return self::_check($check, '%^((0?[1-9]|1[012])(:[0-5]\d){0,2} ?([AP]M|[ap]m))$|^([01]\d|2[0-3])(:[0-5]\d){0,2}$%');
+ }
+
+/**
+ * Boolean validation, determines if value passed is a boolean integer or true/false.
+ *
+ * @param string $check a valid boolean
+ * @return boolean Success
+ */
+ public static function boolean($check) {
+ $booleanList = array(0, 1, '0', '1', true, false);
+ return in_array($check, $booleanList, true);
+ }
+
+/**
+ * Checks that a value is a valid decimal. If $places is null, the $check is allowed to be a scientific float
+ * If no decimal point is found a false will be returned. Both the sign and exponent are optional.
+ *
+ * @param integer $check The value the test for decimal
+ * @param integer $places if set $check value must have exactly $places after the decimal point
+ * @param string $regex If a custom regular expression is used this is the only validation that will occur.
+ * @return boolean Success
+ */
+ public static function decimal($check, $places = null, $regex = null) {
+ if (is_null($regex)) {
+ if (is_null($places)) {
+ $regex = '/^[-+]?[0-9]*(\\.{1}[0-9]+(?:[eE][-+]?[0-9]+)?)?$/';
+ } else {
+ $regex = '/^[-+]?[0-9]*(\\.{1}[0-9]{' . $places . '})?$/';
+ }
+ }
+ return self::_check($check, $regex);
+ }
+
+/**
+ * Validates for an email address.
+ *
+ * Only uses getmxrr() checking for deep validation if PHP 5.3.0+ is used, or
+ * any PHP version on a non-windows distribution
+ *
+ * @param string $check Value to check
+ * @param boolean $deep Perform a deeper validation (if true), by also checking availability of host
+ * @param string $regex Regex to use (if none it will use built in regex)
+ * @return boolean Success
+ */
+ public static function email($check, $deep = false, $regex = null) {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+
+ if (is_null($regex)) {
+ $regex = '/^[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . self::$_pattern['hostname'] . '$/i';
+ }
+ $return = self::_check($check, $regex);
+ if ($deep === false || $deep === null) {
+ return $return;
+ }
+
+ if ($return === true && preg_match('/@(' . self::$_pattern['hostname'] . ')$/i', $check, $regs)) {
+ if (function_exists('getmxrr') && getmxrr($regs[1], $mxhosts)) {
+ return true;
+ }
+ if (function_exists('checkdnsrr') && checkdnsrr($regs[1], 'MX')) {
+ return true;
+ }
+ return is_array(gethostbynamel($regs[1]));
+ }
+ return false;
+ }
+
+/**
+ * Check that value is exactly $comparedTo.
+ *
+ * @param mixed $check Value to check
+ * @param mixed $comparedTo Value to compare
+ * @return boolean Success
+ */
+ public static function equalTo($check, $comparedTo) {
+ return ($check === $comparedTo);
+ }
+
+/**
+ * Check that value has a valid file extension.
+ *
+ * @param string|array $check Value to check
+ * @param array $extensions file extensions to allow. By default extensions are 'gif', 'jpeg', 'png', 'jpg'
+ * @return boolean Success
+ */
+ public static function extension($check, $extensions = array('gif', 'jpeg', 'png', 'jpg')) {
+ if (is_array($check)) {
+ return self::extension(array_shift($check), $extensions);
+ }
+ $extension = strtolower(pathinfo($check, PATHINFO_EXTENSION));
+ foreach ($extensions as $value) {
+ if ($extension === strtolower($value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+/**
+ * Validation of an IP address.
+ *
+ * @param string $check The string to test.
+ * @param string $type The IP Protocol version to validate against
+ * @return boolean Success
+ */
+ public static function ip($check, $type = 'both') {
+ $type = strtolower($type);
+ $flags = 0;
+ if ($type === 'ipv4') {
+ $flags = FILTER_FLAG_IPV4;
+ }
+ if ($type === 'ipv6') {
+ $flags = FILTER_FLAG_IPV6;
+ }
+ return (boolean)filter_var($check, FILTER_VALIDATE_IP, array('flags' => $flags));
+ }
+
+/**
+ * Checks whether the length of a string is greater or equal to a minimal length.
+ *
+ * @param string $check The string to test
+ * @param integer $min The minimal string length
+ * @return boolean Success
+ */
+ public static function minLength($check, $min) {
+ return mb_strlen($check) >= $min;
+ }
+
+/**
+ * Checks whether the length of a string is smaller or equal to a maximal length..
+ *
+ * @param string $check The string to test
+ * @param integer $max The maximal string length
+ * @return boolean Success
+ */
+ public static function maxLength($check, $max) {
+ return mb_strlen($check) <= $max;
+ }
+
+/**
+ * Checks that a value is a monetary amount.
+ *
+ * @param string $check Value to check
+ * @param string $symbolPosition Where symbol is located (left/right)
+ * @return boolean Success
+ */
+ public static function money($check, $symbolPosition = 'left') {
+ $money = '(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{2})?';
+ if ($symbolPosition == 'right') {
+ $regex = '/^' . $money . '(?<!\x{00a2})\p{Sc}?$/u';
+ } else {
+ $regex = '/^(?!\x{00a2})\p{Sc}?' . $money . '$/u';
+ }
+ return self::_check($check, $regex);
+ }
+
+/**
+ * Validate a multiple select.
+ *
+ * Valid Options
+ *
+ * - in => provide a list of choices that selections must be made from
+ * - max => maximum number of non-zero choices that can be made
+ * - min => minimum number of non-zero choices that can be made
+ *
+ * @param array $check Value to check
+ * @param array $options Options for the check.
+ * @param boolean $strict Defaults to true, set to false to disable strict type check
+ * @return boolean Success
+ */
+ public static function multiple($check, $options = array(), $strict = true) {
+ $defaults = array('in' => null, 'max' => null, 'min' => null);
+ $options = array_merge($defaults, $options);
+ $check = array_filter((array)$check);
+ if (empty($check)) {
+ return false;
+ }
+ if ($options['max'] && count($check) > $options['max']) {
+ return false;
+ }
+ if ($options['min'] && count($check) < $options['min']) {
+ return false;
+ }
+ if ($options['in'] && is_array($options['in'])) {
+ foreach ($check as $val) {
+ if (!in_array($val, $options['in'], $strict)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+/**
+ * Checks if a value is numeric.
+ *
+ * @param string $check Value to check
+ * @return boolean Success
+ */
+ public static function numeric($check) {
+ return is_numeric($check);
+ }
+
+/**
+ * Checks if a value is a natural number.
+ *
+ * @param string $check Value to check
+ * @param boolean $allowZero Set true to allow zero, defaults to false
+ * @return boolean Success
+ * @see http://en.wikipedia.org/wiki/Natural_number
+ */
+ public static function naturalNumber($check, $allowZero = false) {
+ $regex = $allowZero ? '/^(?:0|[1-9][0-9]*)$/' : '/^[1-9][0-9]*$/';
+ return self::_check($check, $regex);
+ }
+
+/**
+ * Check that a value is a valid phone number.
+ *
+ * @param string|array $check Value to check (string or array)
+ * @param string $regex Regular expression to use
+ * @param string $country Country code (defaults to 'all')
+ * @return boolean Success
+ */
+ public static function phone($check, $regex = null, $country = 'all') {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+
+ if (is_null($regex)) {
+ switch ($country) {
+ case 'us':
+ case 'all':
+ case 'can':
+ // includes all NANPA members.
+ // see http://en.wikipedia.org/wiki/North_American_Numbering_Plan#List_of_NANPA_countries_and_territories
+ $regex = '/^(?:\+?1)?[-. ]?\\(?[2-9][0-8][0-9]\\)?[-. ]?[2-9][0-9]{2}[-. ]?[0-9]{4}$/';
+ break;
+ }
+ }
+ if (empty($regex)) {
+ return self::_pass('phone', $check, $country);
+ }
+ return self::_check($check, $regex);
+ }
+
+/**
+ * Checks that a given value is a valid postal code.
+ *
+ * @param string|array $check Value to check
+ * @param string $regex Regular expression to use
+ * @param string $country Country to use for formatting
+ * @return boolean Success
+ */
+ public static function postal($check, $regex = null, $country = 'us') {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+
+ if (is_null($regex)) {
+ switch ($country) {
+ case 'uk':
+ $regex = '/\\A\\b[A-Z]{1,2}[0-9][A-Z0-9]? [0-9][ABD-HJLNP-UW-Z]{2}\\b\\z/i';
+ break;
+ case 'ca':
+ $regex = '/\\A\\b[ABCEGHJKLMNPRSTVXY][0-9][A-Z] [0-9][A-Z][0-9]\\b\\z/i';
+ break;
+ case 'it':
+ case 'de':
+ $regex = '/^[0-9]{5}$/i';
+ break;
+ case 'be':
+ $regex = '/^[1-9]{1}[0-9]{3}$/i';
+ break;
+ case 'us':
+ $regex = '/\\A\\b[0-9]{5}(?:-[0-9]{4})?\\b\\z/i';
+ break;
+ }
+ }
+ if (empty($regex)) {
+ return self::_pass('postal', $check, $country);
+ }
+ return self::_check($check, $regex);
+ }
+
+/**
+ * Validate that a number is in specified range.
+ * if $lower and $upper are not set, will return true if
+ * $check is a legal finite on this platform
+ *
+ * @param string $check Value to check
+ * @param integer $lower Lower limit
+ * @param integer $upper Upper limit
+ * @return boolean Success
+ */
+ public static function range($check, $lower = null, $upper = null) {
+ if (!is_numeric($check)) {
+ return false;
+ }
+ if (isset($lower) && isset($upper)) {
+ return ($check > $lower && $check < $upper);
+ }
+ return is_finite($check);
+ }
+
+/**
+ * Checks that a value is a valid Social Security Number.
+ *
+ * @param string|array $check Value to check
+ * @param string $regex Regular expression to use
+ * @param string $country Country
+ * @return boolean Success
+ */
+ public static function ssn($check, $regex = null, $country = null) {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+
+ if (is_null($regex)) {
+ switch ($country) {
+ case 'dk':
+ $regex = '/\\A\\b[0-9]{6}-[0-9]{4}\\b\\z/i';
+ break;
+ case 'nl':
+ $regex = '/\\A\\b[0-9]{9}\\b\\z/i';
+ break;
+ case 'us':
+ $regex = '/\\A\\b[0-9]{3}-[0-9]{2}-[0-9]{4}\\b\\z/i';
+ break;
+ }
+ }
+ if (empty($regex)) {
+ return self::_pass('ssn', $check, $country);
+ }
+ return self::_check($check, $regex);
+ }
+
+/**
+ * Checks that a value is a valid URL according to http://www.w3.org/Addressing/URL/url-spec.txt
+ *
+ * The regex checks for the following component parts:
+ *
+ * - a valid, optional, scheme
+ * - a valid ip address OR
+ * a valid domain name as defined by section 2.3.1 of http://www.ietf.org/rfc/rfc1035.txt
+ * with an optional port number
+ * - an optional valid path
+ * - an optional query string (get parameters)
+ * - an optional fragment (anchor tag)
+ *
+ * @param string $check Value to check
+ * @param boolean $strict Require URL to be prefixed by a valid scheme (one of http(s)/ftp(s)/file/news/gopher)
+ * @return boolean Success
+ */
+ public static function url($check, $strict = false) {
+ self::_populateIp();
+ $validChars = '([' . preg_quote('!"$&\'()*+,-.@_:;=~[]') . '\/0-9a-z\p{L}\p{N}]|(%[0-9a-f]{2}))';
+ $regex = '/^(?:(?:https?|ftps?|sftp|file|news|gopher):\/\/)' . (!empty($strict) ? '' : '?') .
+ '(?:' . self::$_pattern['IPv4'] . '|\[' . self::$_pattern['IPv6'] . '\]|' . self::$_pattern['hostname'] . ')(?::[1-9][0-9]{0,4})?' .
+ '(?:\/?|\/' . $validChars . '*)?' .
+ '(?:\?' . $validChars . '*)?' .
+ '(?:#' . $validChars . '*)?$/iu';
+ return self::_check($check, $regex);
+ }
+
+/**
+ * Checks if a value is in a given list.
+ *
+ * @param string $check Value to check
+ * @param array $list List to check against
+ * @param boolean $strict Defaults to true, set to false to disable strict type check
+ * @return boolean Success
+ */
+ public static function inList($check, $list, $strict = true) {
+ return in_array($check, $list, $strict);
+ }
+
+/**
+ * Runs an user-defined validation.
+ *
+ * @param string|array $check value that will be validated in user-defined methods.
+ * @param object $object class that holds validation method
+ * @param string $method class method name for validation to run
+ * @param array $args arguments to send to method
+ * @return mixed user-defined class class method returns
+ */
+ public static function userDefined($check, $object, $method, $args = null) {
+ return call_user_func_array(array($object, $method), array($check, $args));
+ }
+
+/**
+ * Checks that a value is a valid uuid - http://tools.ietf.org/html/rfc4122
+ *
+ * @param string $check Value to check
+ * @return boolean Success
+ */
+ public static function uuid($check) {
+ $regex = '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i';
+ return self::_check($check, $regex);
+ }
+
+/**
+ * Attempts to pass unhandled Validation locales to a class starting with $classPrefix
+ * and ending with Validation. For example $classPrefix = 'nl', the class would be
+ * `NlValidation`.
+ *
+ * @param string $method The method to call on the other class.
+ * @param mixed $check The value to check or an array of parameters for the method to be called.
+ * @param string $classPrefix The prefix for the class to do the validation.
+ * @return mixed Return of Passed method, false on failure
+ */
+ protected static function _pass($method, $check, $classPrefix) {
+ $className = ucwords($classPrefix) . 'Validation';
+ if (!class_exists($className)) {
+ trigger_error(__d('cake_dev', 'Could not find %s class, unable to complete validation.', $className), E_USER_WARNING);
+ return false;
+ }
+ if (!method_exists($className, $method)) {
+ trigger_error(__d('cake_dev', 'Method %s does not exist on %s unable to complete validation.', $method, $className), E_USER_WARNING);
+ return false;
+ }
+ $check = (array)$check;
+ return call_user_func_array(array($className, $method), $check);
+ }
+
+/**
+ * Runs a regular expression match.
+ *
+ * @param string $check Value to check against the $regex expression
+ * @param string $regex Regular expression
+ * @return boolean Success of match
+ */
+ protected static function _check($check, $regex) {
+ if (preg_match($regex, $check)) {
+ self::$errors[] = false;
+ return true;
+ } else {
+ self::$errors[] = true;
+ return false;
+ }
+ }
+
+/**
+ * Get the values to use when value sent to validation method is
+ * an array.
+ *
+ * @param array $params Parameters sent to validation method
+ * @return void
+ */
+ protected static function _defaults($params) {
+ self::_reset();
+ $defaults = array(
+ 'check' => null,
+ 'regex' => null,
+ 'country' => null,
+ 'deep' => false,
+ 'type' => null
+ );
+ $params = array_merge($defaults, $params);
+ if ($params['country'] !== null) {
+ $params['country'] = mb_strtolower($params['country']);
+ }
+ return $params;
+ }
+
+/**
+ * Luhn algorithm
+ *
+ * @param string|array $check
+ * @param boolean $deep
+ * @return boolean Success
+ * @see http://en.wikipedia.org/wiki/Luhn_algorithm
+ */
+ public static function luhn($check, $deep = false) {
+ if (is_array($check)) {
+ extract(self::_defaults($check));
+ }
+ if ($deep !== true) {
+ return true;
+ }
+ if ($check == 0) {
+ return false;
+ }
+ $sum = 0;
+ $length = strlen($check);
+
+ for ($position = 1 - ($length % 2); $position < $length; $position += 2) {
+ $sum += $check[$position];
+ }
+
+ for ($position = ($length % 2); $position < $length; $position += 2) {
+ $number = $check[$position] * 2;
+ $sum += ($number < 10) ? $number : $number - 9;
+ }
+
+ return ($sum % 10 == 0);
+ }
+
+/**
+ * Checks the mime type of a file
+ *
+ * @param string|array $check
+ * @param array $mimeTypes to check for
+ * @return boolean Success
+ * @throws CakeException when mime type can not be determined.
+ */
+ public static function mimeType($check, $mimeTypes = array()) {
+ if (is_array($check) && isset($check['tmp_name'])) {
+ $check = $check['tmp_name'];
+ }
+
+ $File = new File($check);
+ $mime = $File->mime();
+
+ if ($mime === false) {
+ throw new CakeException(__d('cake_dev', 'Can not determine the mimetype.'));
+ }
+ return in_array($mime, $mimeTypes);
+ }
+
+/**
+ * Checking for upload errors
+ *
+ * @param string|array $check
+ * @retrun boolean
+ * @see http://www.php.net/manual/en/features.file-upload.errors.php
+ */
+ public static function uploadError($check) {
+ if (is_array($check) && isset($check['error'])) {
+ $check = $check['error'];
+ }
+
+ return $check === UPLOAD_ERR_OK;
+ }
+
+/**
+ * Lazily populate the IP address patterns used for validations
+ *
+ * @return void
+ */
+ protected static function _populateIp() {
+ if (!isset(self::$_pattern['IPv6'])) {
+ $pattern = '((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}';
+ $pattern .= '(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})';
+ $pattern .= '|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})';
+ $pattern .= '(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)';
+ $pattern .= '{4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))';
+ $pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}';
+ $pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|';
+ $pattern .= '((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}';
+ $pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))';
+ $pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4})';
+ $pattern .= '{0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)';
+ $pattern .= '|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]';
+ $pattern .= '\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4})';
+ $pattern .= '{1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?';
+
+ self::$_pattern['IPv6'] = $pattern;
+ }
+ if (!isset(self::$_pattern['IPv4'])) {
+ $pattern = '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])';
+ self::$_pattern['IPv4'] = $pattern;
+ }
+ }
+
+/**
+ * Reset internal variables for another validation run.
+ *
+ * @return void
+ */
+ protected static function _reset() {
+ self::$errors = array();
+ }
+
+}
diff --git a/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Xml.php b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Xml.php
new file mode 100644
index 0000000..f8662b2
--- /dev/null
+++ b/poc/poc02-compiling-cake/src/vendor/cakephp-2.2.1-0-gcc44130/lib/Cake/Utility/Xml.php
@@ -0,0 +1,370 @@
+<?php
+/**
+ * XML handling for Cake.
+ *
+ * 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)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright Copyright 2005-2012, 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)
+ */
+
+/**
+ * XML handling for Cake.
+ *
+ * The methods in these classes enable the datasources that use XML to work.
+ *
+ * @package Cake.Utility
+ */
+class Xml {
+
+/**
+ * Initialize SimpleXMLElement or DOMDocument from a given XML string, file path, URL or array.
+ *
+ * ### Usage:
+ *
+ * Building XML from a string:
+ *
+ * `$xml = Xml::build('<example>text</example>');`
+ *
+ * Building XML from string (output DOMDocument):
+ *
+ * `$xml = Xml::build('<example>text</example>', array('return' => 'domdocument'));`
+ *
+ * Building XML from a file path:
+ *
+ * `$xml = Xml::build('/path/to/an/xml/file.xml');`
+ *
+ * Building from a remote URL:
+ *
+ * `$xml = Xml::build('http://example.com/example.xml');`
+ *
+ * Building from an array:
+ *
+ * {{{
+ * $value = array(
+ * 'tags' => array(
+ * 'tag' => array(
+ * array(
+ * 'id' => '1',
+ * 'name' => 'defect'
+ * ),
+ * array(
+ * 'id' => '2',
+ * 'name' => 'enhancement'
+ * )
+ * )
+ * )
+ * );
+ * $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
+ * is disabled by default for security reasons.
+ * - If using array as input, you can pass `options` from Xml::fromArray.
+ *
+ * @param string|array $input XML string, a path to a file, an URL or an array
+ * @param array $options The options to use
+ * @return SimpleXMLElement|DOMDocument SimpleXMLElement or DOMDocument
+ * @throws XmlException
+ */
+ public static function build($input, $options = array()) {
+ if (!is_array($options)) {
+ $options = array('return' => (string)$options);
+ }
+ $defaults = array(
+ 'return' => 'simplexml',
+ 'loadEntities' => false,
+ );
+ $options = array_merge($defaults, $options);
+
+ if (is_array($input) || is_object($input)) {
+ return self::fromArray((array)$input, $options);
+ } elseif (strpos($input, '<') !== false) {
+ return self::_loadXml($input, $options);
+ } elseif (file_exists($input) || strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
+ $input = file_get_contents($input);
+ return self::_loadXml($input, $options);
+ } elseif (!is_string($input)) {
+ throw new XmlException(__d('cake_dev', 'Invalid input.'));
+ }
+ throw new XmlException(__d('cake_dev', 'XML cannot be read.'));
+ }
+
+/**
+ * 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.
+ */
+ protected static function _loadXml($input, $options) {
+ $hasDisable = function_exists('libxml_disable_entity_loader');
+ $internalErrors = libxml_use_internal_errors(true);
+ 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);
+ }
+ if ($hasDisable && !$options['loadEntities']) {
+ libxml_disable_entity_loader(false);
+ }
+ libxml_use_internal_errors($internalErrors);
+ return $xml;
+ }
+
+/**
+ * Transform an array into a SimpleXMLElement
+ *
+ * ### Options
+ *
+ * - `format` If create childs ('tags') or attributes ('attribute').
+ * - `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(
+ * 'id' => 1,
+ * 'value' => 'defect',
+ * '@' => 'description'
+ * )
+ * )
+ * );
+ * }}}
+ *
+ * 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:
+ *
+ * `<root><tag id="1" value="defect">description</tag></root>`
+ *
+ * @param array $input Array with data
+ * @param array $options The options to use
+ * @return SimpleXMLElement|DOMDocument SimpleXMLElement or DOMDocument
+ * @throws XmlException
+ */
+ public static function fromArray($input, $options = array()) {
+ if (!is_array($input) || count($input) !== 1) {
+ throw new XmlException(__d('cake_dev', 'Invalid input.'));
+ }
+ $key = key($input);
+ if (is_integer($key)) {
+ throw new XmlException(__d('cake_dev', 'The key of input must be alphanumeric'));
+ }
+
+ if (!is_array($options)) {
+ $options = array('format' => (string)$options);
+ }
+ $defaults = array(
+ 'format' => 'tags',
+ 'version' => '1.0',
+ 'encoding' => Configure::read('App.encoding'),
+ 'return' => 'simplexml'
+ );
+ $options = array_merge($defaults, $options);
+
+ $dom = new DOMDocument($options['version'], $options['encoding']);
+ self::_fromArray($dom, $dom, $input, $options['format']);
+
+ $options['return'] = strtolower($options['return']);
+ if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
+ return new SimpleXMLElement($dom->saveXML());
+ }
+ return $dom;
+ }
+
+/**
+ * Recursive method to create childs from array
+ *
+ * @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.
+ * @return void
+ * @throws XmlException
+ */
+ protected static function _fromArray($dom, $node, &$data, $format) {
+ if (empty($data) || !is_array($data)) {
+ return;
+ }
+ foreach ($data as $key => $value) {
+ if (is_string($key)) {
+ if (!is_array($value)) {
+ if (is_bool($value)) {
+ $value = (int)$value;
+ } elseif ($value === null) {
+ $value = '';
+ }
+ $isNamespace = strpos($key, 'xmlns:');
+ if ($isNamespace !== false) {
+ $node->setAttributeNS('http://www.w3.org/2000/xmlns/', $key, $value);
+ continue;
+ }
+ if ($key[0] !== '@' && $format === 'tags') {
+ $child = null;
+ if (!is_numeric($value)) {
+ // Escape special characters
+ // http://www.w3.org/TR/REC-xml/#syntax
+ // https://bugs.php.net/bug.php?id=36795
+ $child = $dom->createElement($key, '');
+ $child->appendChild(new DOMText($value));
+ } else {
+ $child = $dom->createElement($key, $value);
+ }
+ $node->appendChild($child);
+ } else {
+ if ($key[0] === '@') {
+ $key = substr($key, 1);
+ }
+ $attribute = $dom->createAttribute($key);
+ $attribute->appendChild($dom->createTextNode($value));
+ $node->appendChild($attribute);
+ }
+ } else {
+ if ($key[0] === '@') {
+ throw new XmlException(__d('cake_dev', 'Invalid array'));
+ }
+ if (is_numeric(implode('', array_keys($value)))) { // List
+ foreach ($value as $item) {
+ $itemData = compact('dom', 'node', 'key', 'format');
+ $itemData['value'] = $item;
+ self::_createChild($itemData);
+ }
+ } else { // Struct
+ self::_createChild(compact('dom', 'node', 'key', 'value', 'format'));
+ }
+ }
+ } else {
+ throw new XmlException(__d('cake_dev', 'Invalid array'));
+ }
+ }
+ }
+
+/**
+ * Helper to _fromArray(). It will create childs of arrays
+ *
+ * @param array $data Array with informations to create childs
+ * @return void
+ */
+ protected static function _createChild($data) {
+ extract($data);
+ $childNS = $childValue = null;
+ if (is_array($value)) {
+ if (isset($value['@'])) {
+ $childValue = (string)$value['@'];
+ unset($value['@']);
+ }
+ if (isset($value['xmlns:'])) {
+ $childNS = $value['xmlns:'];
+ unset($value['xmlns:']);
+ }
+ } elseif (!empty($value) || $value === 0) {
+ $childValue = (string)$value;
+ }
+
+ if ($childValue) {
+ $child = $dom->createElement($key, $childValue);
+ } else {
+ $child = $dom->createElement($key);
+ }
+ if ($childNS) {
+ $child->setAttribute('xmlns', $childNS);
+ }
+
+ self::_fromArray($dom, $child, $value, $format);
+ $node->appendChild($child);
+ }
+
+/**
+ * Returns this XML structure as a array.
+ *
+ * @param SimpleXMLElement|DOMDocument|DOMNode $obj SimpleXMLElement, DOMDocument or DOMNode instance
+ * @return array Array representation of the XML structure.
+ * @throws XmlException
+ */
+ public static function toArray($obj) {
+ if ($obj instanceof DOMNode) {
+ $obj = simplexml_import_dom($obj);
+ }
+ if (!($obj instanceof SimpleXMLElement)) {
+ throw new XmlException(__d('cake_dev', 'The input is not instance of SimpleXMLElement, DOMDocument or DOMNode.'));
+ }
+ $result = array();
+ $namespaces = array_merge(array('' => ''), $obj->getNamespaces(true));
+ self::_toArray($obj, $result, '', array_keys($namespaces));
+ return $result;
+ }
+
+/**
+ * Recursive method to toArray
+ *
+ * @param SimpleXMLElement $xml SimpleXMLElement object
+ * @param array $parentData Parent array with data
+ * @param string $ns Namespace of current child
+ * @param array $namespaces List of namespaces in XML
+ * @return void
+ */
+ protected static function _toArray($xml, &$parentData, $ns, $namespaces) {
+ $data = array();
+
+ foreach ($namespaces as $namespace) {
+ foreach ($xml->attributes($namespace, true) as $key => $value) {
+ if (!empty($namespace)) {
+ $key = $namespace . ':' . $key;
+ }
+ $data['@' . $key] = (string)$value;
+ }
+
+ foreach ($xml->children($namespace, true) as $child) {
+ self::_toArray($child, $data, $namespace, $namespaces);
+ }
+ }
+
+ $asString = trim((string)$xml);
+ if (empty($data)) {
+ $data = $asString;
+ } elseif (!empty($asString)) {
+ $data['@'] = $asString;
+ }
+
+ if (!empty($ns)) {
+ $ns .= ':';
+ }
+ $name = $ns . $xml->getName();
+ if (isset($parentData[$name])) {
+ if (!is_array($parentData[$name]) || !isset($parentData[$name][0])) {
+ $parentData[$name] = array($parentData[$name]);
+ }
+ $parentData[$name][] = $data;
+ } else {
+ $parentData[$name] = $data;
+ }
+ }
+
+}