Commit: 7440fbd54c7b1e6e911f9489f1a5a06e138a3a7b

Author: chawbacca | Date: 2011-03-07 19:08:47 +0000
Merge from lithium.git
diff --git a/app/config/bootstrap.php b/app/config/bootstrap.php index 9c036b2..0b45e42 100644 --- a/app/config/bootstrap.php +++ b/app/config/bootstrap.php @@ -21,6 +21,15 @@ * new bootstrap file and `require` it here. * * @see lithium\util\collection\Filters +<<<<<<< HEAD + */ + +/** + * The libraries file contains the loading instructions for all plugins, frameworks and other class + * libraries used in the application, including the Lithium core, and the application itself. These + * instructions include library names, paths to files, and any applicable class-loading rules. This + * file also statically loads common classes to improve bootstrap performance. +======= */ /** @@ -32,6 +41,14 @@ require __DIR__ . '/bootstrap/libraries.php'; /** + * The error configuration allows you to use the filter system along with the advanced matching + * rules of the `ErrorHandler` class to provide a high level of control over managing exceptions in + * your application, with no impact on framework or application code. +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e + */ +// require __DIR__ . '/bootstrap/errors.php'; + +/** * This file contains configurations for connecting to external caching resources, as well as * default caching rules for various systems within your application */ diff --git a/app/config/bootstrap/cache.php b/app/config/bootstrap/cache.php index 3e124be..a929466 100644 --- a/app/config/bootstrap/cache.php +++ b/app/config/bootstrap/cache.php @@ -27,6 +27,14 @@ if (!($apcEnabled = Apc::enabled()) && !is_writable(LITHIUM_APP_PATH . '/resourc return; } +<<<<<<< HEAD +======= +/** + * This configures the default cache, based on whether ot not APC user caching is enabled. If it is + * not, file caching will be used. Most of this code is for getting you up and running only, and + * should be replaced with a hard-coded configuration, based on the cache(s) you plan to use. + */ +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e if ($apcEnabled) { $default = array( 'adapter' => 'lithium\storage\cache\adapter\Apc', @@ -39,15 +47,28 @@ if ($apcEnabled) { } Cache::config(compact('default')); +/** + * Caches paths for auto-loaded and service-located classes. + */ Dispatcher::applyFilter('run', function($self, $params, $chain) { +<<<<<<< HEAD if ($cache = Cache::read('default', 'core.libraries')) { +======= + $key = md5(LITHIUM_APP_PATH) . '.core.libraries'; + + if ($cache = Cache::read('default', $key)) { +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $cache = (array) $cache + Libraries::cache(); Libraries::cache($cache); } $result = $chain->next($self, $params, $chain); if ($cache != Libraries::cache()) { +<<<<<<< HEAD Cache::write('default', 'core.libraries', Libraries::cache(), '+1 day'); +======= + Cache::write('default', $key, Libraries::cache(), '+1 day'); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } return $result; }); diff --git a/app/config/bootstrap/errors.php b/app/config/bootstrap/errors.php new file mode 100644 index 0000000..27de2a2 --- /dev/null +++ b/app/config/bootstrap/errors.php @@ -0,0 +1,25 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2011, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +use lithium\core\ErrorHandler; +use lithium\action\Response; +use lithium\net\http\Media; + +ErrorHandler::apply('lithium\action\Dispatcher::run', array(), function($info, $params) { + $response = new Response(array('request' => $params['request'])); + + Media::render($response, compact('info', 'params'), array( + 'controller' => '_errors', + 'template' => 'development', + 'layout' => 'error', + 'request' => $params['request'] + )); + return $response; +}); + +?> \ No newline at end of file diff --git a/app/resources/g11n/empty b/app/resources/g11n/empty old mode 100755 new mode 100644 diff --git a/app/resources/tmp/cache/templates/empty b/app/resources/tmp/cache/templates/empty old mode 100755 new mode 100644 diff --git a/app/resources/tmp/logs/empty b/app/resources/tmp/logs/empty old mode 100755 new mode 100644 diff --git a/app/resources/tmp/tests/empty b/app/resources/tmp/tests/empty old mode 100755 new mode 100644 diff --git a/app/views/_errors/development.html.php b/app/views/_errors/development.html.php new file mode 100644 index 0000000..18654f6 --- /dev/null +++ b/app/views/_errors/development.html.php @@ -0,0 +1,108 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2011, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +use lithium\analysis\Debugger; +use lithium\analysis\Inspector; + +$exception = $info['exception']; +$replace = array('&lt;?php', '?&gt;', '<code>', '</code>', "\n"); +$context = 5; + +/** + * Set Lithium-esque colors for syntax highlighing. + */ +ini_set('highlight.string', '#4DDB4A'); +ini_set('highlight.comment', '#D42AAE'); +ini_set('highlight.keyword', '#D42AAE'); +ini_set('highlight.default', '#3C96FF'); +ini_set('highlight.htm', '#FFFFFF'); + +$stack = Debugger::trace(array('format' => 'array', 'trace' => $exception->getTrace())); + +array_unshift($stack, array( + 'functionRef' => '[exception]', + 'file' => $exception->getFile(), + 'line' => $exception->getLine() +)); + +?> +<h3>Exception</h3> + +<div class="lithium-exception-class"> + <?=get_class($exception);?> + + <?php if ($code = $exception->getCode()): ?> + <span class="code">(code <?=$code; ?>)</span> + <?php endif ?> +</div> + +<div class="lithium-exception-message"><?=$exception->getMessage(); ?></div> + +<h3 id="source">Source</h3> + +<div id="sourceCode"></div> + +<h3>Stack Trace</h3> + +<div class="lithium-stack-trace"> + <ol> + <?php foreach ($stack as $id => $frame): ?> + <?php + $location = "{$frame['file']}: {$frame['line']}"; + $lines = range($frame['line'] - $context, $frame['line'] + $context); + $code = Inspector::lines($frame['file'], $lines); + ?> + <li> + <tt><a href="#source" id="<?=$id; ?>" class="display-source-excerpt"> + <?=$frame['functionRef']; ?> + </a></tt> + <div id="sourceCode<?=$id; ?>" style="display: none;"> + + <div class="lithium-exception-location"> + <?=$location; ?> + </div> + + <div class="lithium-code-dump"> + <pre><code><?php + foreach ($code as $num => $content): + $numPad = str_pad($num, 3, ' '); + $content = str_ireplace(array('<?php', '?>'), '', $content); + $content = highlight_string("<?php {$numPad}{$content} ?>", true); + $content = str_replace($replace, '', $content); + + if ($frame['line'] === $num): + ?><span class="code-highlight"><?php + endif;?><?php echo "{$content}\n"; ?><?php + if ($frame['line'] === $num): + ?></span><?php + endif; + + endforeach; + ?></code></pre> + </div> + </div> + </li> + <?php endforeach; ?> + </ol> +</div> + +<script type="text/javascript"> + window.onload = function() { + var $ = function() { return document.getElementById.apply(document, arguments); }; + var links = document.getElementsByTagName('a'); + + for (i = 0; i < links.length; i++) { + if (links[i].className.indexOf('display-source-excerpt') >= 0) { + links[i].onclick = function() { + $('sourceCode').innerHTML = $('sourceCode' + this.id).innerHTML; + } + } + } + $('sourceCode').innerHTML = $('sourceCode0').innerHTML; + } +</script> \ No newline at end of file diff --git a/app/views/layouts/error.html.php b/app/views/layouts/error.html.php new file mode 100644 index 0000000..2f26d97 --- /dev/null +++ b/app/views/layouts/error.html.php @@ -0,0 +1,46 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2011, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +/** + * This layout is used to render error pages in both development and production. It is recommended + * that you maintain a separate, simplified layout for rendering errors that does not involve any + * complex logic or dynamic data, which could potentially trigger recursive errors. + */ +?> +<!doctype html> +<html> +<head> + <?php echo $this->html->charset(); ?> + <title>Unhandled exception</title> + <?php echo $this->html->style(array('debug', 'lithium')); ?> + <?php echo $this->scripts(); ?> + <?php echo $this->html->link('Icon', null, array('type' => 'icon')); ?> +</head> +<body class="app"> + <div id="container"> + <div id="header"> + <h1>An unhandled exception was thrown</h1> + <h3>Configuration</h3> + <p> + This layout can be changed by modifying + <code><?php + echo realpath(LITHIUM_APP_PATH . '/views/layouts/error.html.php'); + ?></code> + </p><p> + To modify your error-handling configuration, see + <code><?php + echo realpath(LITHIUM_APP_PATH . '/config/bootstrap/errors.php'); + ?></code> + </p> + </div> + <div id="content"> + <?php echo $this->content(); ?> + </div> + </div> +</body> +</html> \ No newline at end of file diff --git a/app/webroot/css/debug.css b/app/webroot/css/debug.css index a9ecb71..ed5c4b8 100644 --- a/app/webroot/css/debug.css +++ b/app/webroot/css/debug.css @@ -403,6 +403,16 @@ pre.lithium-debug { position: relative; } +div.lithium-exception-class, div.lithium-exception-location { + font-weight: bold; +} + +div.lithium-exception-message { + color: #000; + background: #f0f0f0; + padding: 1em; +} + div.lithium-stack-trace { background: #fff; border: 4px dotted #ffcc00; @@ -440,7 +450,6 @@ div.lithium-code-dump pre, div.lithium-code-dump pre code { div.lithium-code-dump span.code-highlight { background-color: #ff0; - padding: 4px; } /*--- Code Coverage Analysis ---*/ diff --git a/libraries/lithium/action/Controller.php b/libraries/lithium/action/Controller.php index 70f2f4d..db9ab55 100644 --- a/libraries/lithium/action/Controller.php +++ b/libraries/lithium/action/Controller.php @@ -174,7 +174,7 @@ class Controller extends \lithium\core\Object { throw new DispatchException('Attempted to invoke a private method.'); } if (!method_exists($self, $action)) { - throw new DispatchException("Action '{$action}' not found."); + throw new DispatchException("Action `{$action}` not found."); } $render['template'] = $render['template'] ?: $action; @@ -183,7 +183,9 @@ class Controller extends \lithium\core\Object { $self->render(array('text' => $result)); return $self->response; } - $self->set($result); + if (is_array($result)) { + $self->set($result); + } } if (!$render['hasRendered'] && $render['auto']) { @@ -275,7 +277,13 @@ class Controller extends \lithium\core\Object { * - `'head'` _boolean_: Determines whether only headers are returned with the * response. Defaults to `true`, in which case only headers and no body are * returned. Set to `false` to render a body as well. +<<<<<<< HEAD * - `'exit'` _boolean_: Exit immediately after rendering. Defaults to `true`. +======= + * - `'exit'` _boolean_: Exit immediately after rendering. Defaults to `false`. + * Because `redirect()` does not exit by default, you should always prefix calls + * with a `return` statement, so that the action is always immedately exited. +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e * @return object Returns the instance of the `Response` object associated with this controller. * @filter This method can be filtered. */ diff --git a/libraries/lithium/action/Dispatcher.php b/libraries/lithium/action/Dispatcher.php index d00dd7d..92a4024 100644 --- a/libraries/lithium/action/Dispatcher.php +++ b/libraries/lithium/action/Dispatcher.php @@ -116,7 +116,7 @@ class Dispatcher extends \lithium\core\StaticObject { $params = $self::applyRules($result->params); if (!$params) { - throw new DispatchException('Could not route request'); + throw new DispatchException('Could not route request.'); } $callable = $self::invokeMethod('_callable', array($result, $params, $options)); return $self::invokeMethod('_call', array($callable, $result, $params)); @@ -195,7 +195,11 @@ class Dispatcher extends \lithium\core\StaticObject { try { return Libraries::instance('controllers', $controller, $options); } catch (ClassNotFoundException $e) { +<<<<<<< HEAD throw new DispatchException("Controller '{$controller}' not found", null, $e); +======= + throw new DispatchException("Controller `{$controller}` not found.", null, $e); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } }); } @@ -206,7 +210,7 @@ class Dispatcher extends \lithium\core\StaticObject { if (is_callable($callable = $params['callable'])) { return $callable($params['request'], $params['params']); } - throw new DispatchException('Result not callable'); + throw new DispatchException('Result not callable.'); }); } } diff --git a/libraries/lithium/action/Response.php b/libraries/lithium/action/Response.php index ab9caad..c64014a 100644 --- a/libraries/lithium/action/Response.php +++ b/libraries/lithium/action/Response.php @@ -59,7 +59,11 @@ class Response extends \lithium\net\http\Response { * @deprecated */ public function disableCache() { +<<<<<<< HEAD $message = 'Request::disableCache() is deprecated. Please use Request::cache(false).'; +======= + $message = '`Request::disableCache()` is deprecated. Please use `Request::cache(false)`.'; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e throw new BadMethodCallException($message); } @@ -106,7 +110,7 @@ class Response extends \lithium\net\http\Response { $code = 302; } if (!$status = $this->status($code)) { - throw new UnexpectedValueException('Invalid status code'); + throw new UnexpectedValueException('Invalid status code.'); } $this->_writeHeader($status); diff --git a/libraries/lithium/action/readme.wiki b/libraries/lithium/action/readme.wiki old mode 100755 new mode 100644 diff --git a/libraries/lithium/analysis/Debugger.php b/libraries/lithium/analysis/Debugger.php index 83cb75a..926aecc 100644 --- a/libraries/lithium/analysis/Debugger.php +++ b/libraries/lithium/analysis/Debugger.php @@ -8,7 +8,13 @@ namespace lithium\analysis; +<<<<<<< HEAD use lithium\util\String; +======= +use ReflectionClass; +use lithium\util\String; +use lithium\analysis\Inspector; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e /** * The `Debugger` class provides basic facilities for generating and rendering meta-data about the @@ -16,6 +22,8 @@ use lithium\util\String; */ class Debugger extends \lithium\core\Object { + protected static $_closureCache = array(); + /** * Outputs a stack trace based on the supplied options. * @@ -38,7 +46,8 @@ class Debugger extends \lithium\core\Object { 'start' => 0, 'scope' => array(), 'trace' => array(), - 'includeScope' => true + 'includeScope' => true, + 'closures' => true, ); $options += $defaults; @@ -68,6 +77,9 @@ class Debugger extends \lithium\core\Object { } } + if ($options['closures'] && strpos($function, '{closure}') !== false) { + $function = static::_closureDef($backtrace[$i], $function); + } if (in_array($function, array('call_user_func_array', 'trigger_error'))) { continue; } @@ -116,6 +128,76 @@ class Debugger extends \lithium\core\Object { } return $export; } + + /** + * Locates original location of closures. + * + * @param mixed $reference File or class name to inspect. + * @param integer $callLine Line number of class reference. + */ + protected static function _definition($reference, $callLine) { + if (file_exists($reference)) { + foreach (array_reverse(token_get_all(file_get_contents($reference))) as $token) { + if (!is_array($token) || $token[2] > $callLine) { + continue; + } + if ($token[0] === T_FUNCTION) { + return $token[2]; + } + } + return; + } + list($class, $method) = explode('::', $reference); + + if (!class_exists($class)) { + return; + } + + $classRef = new ReflectionClass($class); + $methodInfo = Inspector::info($reference); + $methodDef = join("\n", Inspector::lines($classRef->getFileName(), range( + $methodInfo['start'] + 1, $methodInfo['end'] - 1 + ))); + + foreach (array_reverse(token_get_all("<?php {$methodDef} ?>")) as $token) { + if (!is_array($token) || $token[2] > $callLine) { + continue; + } + if ($token[0] === T_FUNCTION) { + return $token[2] + $methodInfo['start']; + } + } + } + + protected static function _closureDef($frame, $function) { + $reference = '::'; + $frame += array('file' => '??', 'line' => '??'); + $cacheKey = "{$frame['file']}@{$frame['line']}"; + + if (isset(static::$_closureCache[$cacheKey])) { + return static::$_closureCache[$cacheKey]; + } + + if ($class = Inspector::classes(array('file' => $frame['file']))) { + foreach (Inspector::methods(key($class), 'extents') as $method => $extents) { + $line = $frame['line']; + + if (!($extents[0] <= $line && $line <= $extents[1])) { + continue; + } + $class = key($class); + $reference = "{$class}::{$method}"; + $function = "{$reference}()::{closure}"; + break; + } + } else { + $reference = $frame['file']; + $function = "{$reference}::{closure}"; + } + $line = static::_definition($reference, $frame['line']) ?: '?'; + $function .= " @ {$line}"; + return static::$_closureCache[$cacheKey] = $function; + } } ?> \ No newline at end of file diff --git a/libraries/lithium/analysis/Inspector.php b/libraries/lithium/analysis/Inspector.php index 6684e9f..c84b5f2 100644 --- a/libraries/lithium/analysis/Inspector.php +++ b/libraries/lithium/analysis/Inspector.php @@ -383,18 +383,22 @@ class Inspector extends \lithium\core\StaticObject { $options += $defaults; $list = get_declared_classes(); + $files = get_included_files(); $classes = array(); - if (!empty($options['file'])) { + if ($file = $options['file']) { $loaded = static::_instance('collection', array('data' => array_map( function($class) { return new ReflectionClass($class); }, $list ))); + $classFiles = $loaded->getFileName(); - if (!in_array($options['file'], $loaded->getFileName())) { - include $options['file']; + if (in_array($file, $files) && !in_array($file, $classFiles)) { + return array(); + } + if (!in_array($file, $classFiles)) { + include $file; $list = array_diff(get_declared_classes(), $list); } else { - $file = $options['file']; $filter = function($class) use ($file) { return $class->getFileName() == $file; }; $list = $loaded->find($filter)->getName(); } @@ -468,7 +472,7 @@ class Inspector extends \lithium\core\StaticObject { */ protected static function _class($class) { if (!class_exists($class)) { - throw new RuntimeException(sprintf('Class "%s" could not be found.', $class)); + throw new RuntimeException(sprintf('Class `%s` could not be found.', $class)); } return unserialize(sprintf('O:%d:"%s":0:{}', strlen($class), $class)); } diff --git a/libraries/lithium/analysis/Logger.php b/libraries/lithium/analysis/Logger.php index 24567dc..453df80 100644 --- a/libraries/lithium/analysis/Logger.php +++ b/libraries/lithium/analysis/Logger.php @@ -8,7 +8,7 @@ namespace lithium\analysis; -use \UnexpectedValueException; +use UnexpectedValueException; /** * The `Logger` class provides a consistent, application-wide interface for configuring and writing @@ -105,7 +105,7 @@ class Logger extends \lithium\core\Adaptable { if ($name = $options['name']) { $methods = array($name => static::adapter($name)->write($priority, $message, $options)); } elseif (!isset(static::$_priorities[$priority])) { - $message = "Attempted to write log message with invalid priority '{$priority}'."; + $message = "Attempted to write log message with invalid priority `{$priority}`."; throw new UnexpectedValueException($message); } else { $methods = static::_configsByPriority($priority, $message, $options); @@ -114,7 +114,11 @@ class Logger extends \lithium\core\Adaptable { foreach ($methods as $name => $method) { $params = compact('priority', 'message', 'options'); $config = static::_config($name); +<<<<<<< HEAD $result = $result && static::_filter(__FUNCTION__, $params, $method, $config['filters']); +======= + $result &= static::_filter(__FUNCTION__, $params, $method, $config['filters']); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } return $methods ? $result : false; } diff --git a/libraries/lithium/analysis/logger/adapter/Growl.php b/libraries/lithium/analysis/logger/adapter/Growl.php index 335fd50..907685d 100755 --- a/libraries/lithium/analysis/logger/adapter/Growl.php +++ b/libraries/lithium/analysis/logger/adapter/Growl.php @@ -90,7 +90,7 @@ class Growl extends \lithium\core\Object { if ($conn = fsockopen($host, $port, $message, $code)) { return $conn; } - throw new NetworkException("Growl connection failed: ({$code}) {$message}"); + throw new NetworkException("Growl connection failed: (`{$code}`) `{$message}`."); } ); parent::__construct($config + $defaults); diff --git a/libraries/lithium/console/Dispatcher.php b/libraries/lithium/console/Dispatcher.php index 24152e3..435b01e 100755 --- a/libraries/lithium/console/Dispatcher.php +++ b/libraries/lithium/console/Dispatcher.php @@ -130,7 +130,7 @@ class Dispatcher extends \lithium\core\StaticObject { if (class_exists($class = Libraries::locate('command', $name))) { return new $class(compact('request')); } - throw new UnexpectedValueException("Command `{$name}` not found"); + throw new UnexpectedValueException("Command `{$name}` not found."); }); } @@ -191,7 +191,7 @@ class Dispatcher extends \lithium\core\StaticObject { } return $callable($params['action'], $params['args']); } - throw new UnexpectedValueException("{$callable} not callable"); + throw new UnexpectedValueException("Callable `{$callable}` is actually not callable."); }); } } diff --git a/libraries/lithium/console/Request.php b/libraries/lithium/console/Request.php index 2886776..dd4a1f4 100644 --- a/libraries/lithium/console/Request.php +++ b/libraries/lithium/console/Request.php @@ -44,7 +44,7 @@ class Request extends \lithium\core\Object { * Enviroment variables. * * @var array - **/ + */ protected $_env = array(); /** diff --git a/libraries/lithium/console/Response.php b/libraries/lithium/console/Response.php index eeb1b6b..ba39290 100644 --- a/libraries/lithium/console/Response.php +++ b/libraries/lithium/console/Response.php @@ -21,14 +21,14 @@ class Response extends \lithium\core\Object { * Output stream, STDOUT * * @var stream - **/ + */ public $output = null; /** * Error stream, STDERR * * @var stream - **/ + */ public $error = null; /** @@ -94,7 +94,7 @@ class Response extends \lithium\core\Object { * * @return void * - **/ + */ public function __destruct() { if ($this->output) { fclose($this->output); diff --git a/libraries/lithium/console/Router.php b/libraries/lithium/console/Router.php index 398410c..1018169 100644 --- a/libraries/lithium/console/Router.php +++ b/libraries/lithium/console/Router.php @@ -21,7 +21,7 @@ class Router extends \lithium\core\Object { * @param object $request lithium\console\Request * @return array $params * - **/ + */ public static function parse($request = null) { $params = array( 'command' => null, 'action' => 'run', 'args' => array() diff --git a/libraries/lithium/console/command/Library.php b/libraries/lithium/console/command/Library.php index 1f9f47d..b89bd43 100644 --- a/libraries/lithium/console/command/Library.php +++ b/libraries/lithium/console/command/Library.php @@ -214,7 +214,7 @@ class Library extends \lithium\console\Command { */ public function archive($name = null, $result = null) { if (ini_get('phar.readonly') == '1') { - throw new RuntimeException('set phar.readonly = 0 in php.ini'); + throw new RuntimeException('Set `phar.readonly` to `0` in `php.ini`.'); } $from = $name; $to = $name; diff --git a/libraries/lithium/console/command/Test.php b/libraries/lithium/console/command/Test.php index 21b0848..59b7609 100644 --- a/libraries/lithium/console/command/Test.php +++ b/libraries/lithium/console/command/Test.php @@ -11,7 +11,10 @@ namespace lithium\console\command; use lithium\core\Libraries; use lithium\test\Group; use lithium\test\Dispatcher; +<<<<<<< HEAD use lithium\analysis\Inspector; +======= +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e /** * Runs a given set of tests and outputs the results. @@ -110,7 +113,7 @@ class Test extends \lithium\console\Command { $classes = Libraries::find(true, array( 'recursive' => true, - 'exclude' => '/\w+Test$|webroot|index$|^app\\\\config|^app\\\\views/' + 'exclude' => '/tests|resources|webroot|index$|^app\\\\config|^app\\\\views/' )); $tests = Group::all(); $classes = array_diff($classes, $tests); diff --git a/libraries/lithium/console/command/create/Controller.php b/libraries/lithium/console/command/create/Controller.php index 5aaaffb..e29ee03 100644 --- a/libraries/lithium/console/command/create/Controller.php +++ b/libraries/lithium/console/command/create/Controller.php @@ -27,7 +27,7 @@ class Controller extends \lithium\console\command\Create { */ protected function _use($request) { $request->params['command'] = 'model'; - return '\\' . $this->_namespace($request) . '\\' . $this->_model($request); + return $this->_namespace($request) . '\\' . $this->_model($request); } /** diff --git a/libraries/lithium/console/command/create/Test.php b/libraries/lithium/console/command/create/Test.php index f76f685..74af250 100644 --- a/libraries/lithium/console/command/create/Test.php +++ b/libraries/lithium/console/command/create/Test.php @@ -40,9 +40,13 @@ class Test extends \lithium\console\command\Create { * @return string */ protected function _use($request) { +<<<<<<< HEAD $namespace = parent::_namespace($request); $name = $this->_name($request); return "\\{$namespace}\\{$name}"; +======= + return parent::_namespace($request) . '\\' . $this->_name($request); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } /** diff --git a/libraries/lithium/core/Adaptable.php b/libraries/lithium/core/Adaptable.php index 0e6a4bd..ee235e6 100644 --- a/libraries/lithium/core/Adaptable.php +++ b/libraries/lithium/core/Adaptable.php @@ -101,7 +101,7 @@ class Adaptable extends \lithium\core\StaticObject { $config = static::_config($name); if ($config === null) { - throw new ConfigException("Configuration '{$name}' has not been defined."); + throw new ConfigException("Configuration `{$name}` has not been defined."); } if (isset($config['object'])) { @@ -126,7 +126,7 @@ class Adaptable extends \lithium\core\StaticObject { $config = static::_config($name); if ($config === null) { - throw new ConfigException("Configuration '{$name}' has not been defined."); + throw new ConfigException("Configuration `{$name}` has not been defined."); } if (!isset($config['strategies'])) { return null; @@ -213,11 +213,11 @@ class Adaptable extends \lithium\core\StaticObject { protected static function _class($config, $paths = array()) { if (!$name = $config['adapter']) { $self = get_called_class(); - throw new ConfigException("No adapter set for configuration in class {$self}."); + throw new ConfigException("No adapter set for configuration in class `{$self}`."); } if (!$class = static::_locate($paths, $name)) { $self = get_called_class(); - throw new ConfigException("Could not find adapter '{$name}' in class {$self}."); + throw new ConfigException("Could not find adapter `{$name}` in class `{$self}`."); } return $class; } @@ -233,11 +233,11 @@ class Adaptable extends \lithium\core\StaticObject { protected static function _strategy($name, $paths = array()) { if (!$name) { $self = get_called_class(); - throw new ConfigException("No strategy set for configuration in class {$self}."); + throw new ConfigException("No strategy set for configuration in class `{$self}`."); } if (!$class = static::_locate($paths, $name)) { $self = get_called_class(); - throw new ConfigException("Could not find strategy '{$name}' in class {$self}."); + throw new ConfigException("Could not find strategy `{$name}` in class `{$self}`."); } return $class; } @@ -281,6 +281,10 @@ class Adaptable extends \lithium\core\StaticObject { } $env = Environment::get(); $config = isset($settings[$env]) ? $settings[$env] : $settings; + + if (isset($settings[$env]) && isset($settings[true])) { + $config += $settings[true]; + } static::$_configurations[$name] += array(static::_initConfig($name, $config)); return static::$_configurations[$name][0]; } diff --git a/libraries/lithium/core/ErrorHandler.php b/libraries/lithium/core/ErrorHandler.php index 9434bc5..650da84 100644 --- a/libraries/lithium/core/ErrorHandler.php +++ b/libraries/lithium/core/ErrorHandler.php @@ -9,8 +9,13 @@ namespace lithium\core; use Exception; +<<<<<<< HEAD use lithium\util\Collection; use lithium\core\Environment; +======= +use ErrorException; +use lithium\util\collection\Filters; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e /** * The `ErrorHandler` class allows PHP errors and exceptions to be handled in a uniform way. Using @@ -61,6 +66,8 @@ class ErrorHandler extends \lithium\core\StaticObject { */ protected static $_isRunning = false; + protected static $_runOptions = array(); + /** * Setup basic error handling checks/types, as well as register the error and exception * hanlders. @@ -72,21 +79,15 @@ class ErrorHandler extends \lithium\core\StaticObject { public static function __init() { static::$_checks = array( 'type' => function($config, $info) { - return ( - $config['type'] == $info['type'] || - is_subclass_of($info['type'], $config['type']) - ); + return (boolean) array_filter((array) $config['type'], function($type) use ($info) { + return $type == $info['type'] || is_subclass_of($info['type'], $type); + }); }, 'code' => function($config, $info) { return ($config['code'] & $info['code']); }, 'stack' => function($config, $info) { - foreach ((array) $config['stack'] as $frame) { - if (in_array($frame, $info['stack'])) { - return true; - } - } - return false; + return (boolean) array_intersect((array) $config['stack'], $info['stack']); }, 'message' => function($config, $info) { return preg_match($config['message'], $info['message']); @@ -95,16 +96,15 @@ class ErrorHandler extends \lithium\core\StaticObject { $self = get_called_class(); static::$_exceptionHandler = function($exception, $return = false) use ($self) { - $info = array('type' => get_class($exception)) + compact('exception'); - + $info = compact('exception') + array( + 'type' => get_class($exception), + 'stack' => $self::trace($exception->getTrace()) + ); foreach (array('message', 'file', 'line', 'trace') as $key) { $method = 'get' . ucfirst($key); $info[$key] = $exception->{$method}(); } - if ($return) { - return $info; - } - $self::invokeMethod('handle', array($info)); + return $return ? $info : $self::handle($info); }; } @@ -141,18 +141,32 @@ class ErrorHandler extends \lithium\core\StaticObject { * * @return void */ - public static function run() { + public static function run(array $config = array()) { + $defaults = array('trapErrors' => false, 'convertErrors' => true); + + if (static::$_isRunning) { + return; + } + static::$_isRunning = true; + static::$_runOptions = $config + $defaults; $self = get_called_class(); - set_error_handler(function($code, $message, $file, $line = 0, $context = null) use ($self) { + $trap = function($code, $message, $file, $line = 0, $context = null) use ($self) { $trace = debug_backtrace(); $trace = array_slice($trace, 1, count($trace)); - $self::invokeMethod('handle', array( - compact('type', 'code', 'message', 'file', 'line', 'trace', 'context') - )); - }); + $self::handle(compact('type', 'code', 'message', 'file', 'line', 'trace', 'context')); + }; + + $convert = function($code, $message, $file, $line = 0, $context = null) use ($self) { + throw new ErrorException($message, 500, $code, $file, $line); + }; + + if (static::$_runOptions['trapErrors']) { + set_error_handler($trap); + } elseif (static::$_runOptions['convertErrors']) { + set_error_handler($convert); + } set_exception_handler(static::$_exceptionHandler); - static::$_isRunning = true; } /** @@ -212,7 +226,7 @@ class ErrorHandler extends \lithium\core\StaticObject { ); $info = (array) $info + $defaults; - $info['stack'] = static::_trace($info['trace']); + $info['stack'] = static::trace($info['trace']); $info['origin'] = static::_origin($info['trace']); foreach ($rules as $config) { @@ -245,7 +259,11 @@ class ErrorHandler extends \lithium\core\StaticObject { /** * Determine frame from the stack trace where the error/exception was first generated. * +<<<<<<< HEAD * @var array $stack Stack trace from error/exception that was produced. +======= + * @param array $stack Stack trace from error/exception that was produced. +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e * @return string Class where error/exception was generated. */ protected static function _origin(array $stack) { @@ -256,22 +274,40 @@ class ErrorHandler extends \lithium\core\StaticObject { } } +<<<<<<< HEAD public static function apply($class, $method, array $conditions, $handler) { $_self = get_called_class(); $filter = function($self, $params, $chain) use ($_self, $conditions, $handler) { +======= + public static function apply($object, array $conditions, $handler) { + $conditions = $conditions ?: array('type' => 'Exception'); + list($class, $method) = is_string($object) ? explode('::', $object) : $object; + $wrap = static::$_exceptionHandler; + $_self = get_called_class(); + + $filter = function($self, $params, $chain) use ($_self, $conditions, $handler, $wrap) { +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e try { return $chain->next($self, $params, $chain); } catch (Exception $e) { if (!$_self::matches($e, $conditions)) { throw $e; } +<<<<<<< HEAD return $handler($e, $params); +======= + return $handler($wrap($e, true), $params); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } }; if (is_string($class)) { +<<<<<<< HEAD $class::applyFilter($method, $filter); +======= + Filters::apply($class, $method, $filter); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } else { $class->applyFilter($method, $filter); } @@ -302,10 +338,14 @@ class ErrorHandler extends \lithium\core\StaticObject { /** * Trim down a typical stack trace to class & method calls. * +<<<<<<< HEAD * @var array $stack A `debug_backtrace()`-compatible stack trace output. +======= + * @param array $stack A `debug_backtrace()`-compatible stack trace output. +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e * @return array Returns a flat stack array containing class and method references. */ - protected static function _trace(array $stack) { + public static function trace(array $stack) { $result = array(); foreach ($stack as $frame) { diff --git a/libraries/lithium/core/Libraries.php b/libraries/lithium/core/Libraries.php index 79d6fc0..8e8a67a 100644 --- a/libraries/lithium/core/Libraries.php +++ b/libraries/lithium/core/Libraries.php @@ -280,7 +280,11 @@ class Libraries { if (!$config['path']) { if (!$config['path'] = static::_locatePath('libraries', compact('name'))) { +<<<<<<< HEAD throw new ConfigException("Library '{$name}' not found."); +======= + throw new ConfigException("Library `{$name}` not found."); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } } $config['path'] = str_replace('\\', '/', $config['path']); @@ -428,7 +432,7 @@ class Libraries { static::$_cachedPaths[$class] = $path; method_exists($class, '__init') ? $class::__init() : null; } elseif ($require) { - throw new RuntimeException("Failed to load {$class} from {$path}"); + throw new RuntimeException("Failed to load class `{$class}` from path `{$path}`."); } } @@ -470,15 +474,50 @@ class Libraries { $fullPath = "{$params['path']}/{$path}"; if (!$options['dirs']) { - return static::$_cachedPaths[$class] = realpath($fullPath . $suffix); + return static::$_cachedPaths[$class] = static::realPath($fullPath . $suffix); } $list = glob(dirname($fullPath) . '/*'); $list = array_map(function($i) { return str_replace('\\', '/', $i); }, $list); if (in_array($fullPath . $suffix, $list)) { - return static::$_cachedPaths[$class] = realpath($fullPath . $suffix); + return static::$_cachedPaths[$class] = static::realPath($fullPath . $suffix); + } + return is_dir($fullPath) ? static::realPath($fullPath) : null; + } + } + + /** + * Wraps the PHP `realpath()` function to add support for finding paths to files inside Phar + * archives. + * + * @param string $path An unresolved path to a file inside a Phar archive which may or may not + * exist. + * @return string If `$path` is a valid path to a file inside a Phar archive, returns a string + * in the format `'phar://<path-to-phar>/<path-to-file>'`. Otherwise returns + * `null`. + */ + public static function realPath($path) { + if (($absolutePath = realpath($path)) !== false) { + return $absolutePath; + } + if (!preg_match('%^phar://([^.]+\.phar(?:\.gz)?)(.+)%', $path, $pathComponents)) { + return; + } + list(, $relativePath, $pharPath) = $pathComponents; + + $pharPath = implode('/', array_reduce(explode('/', $pharPath), function ($parts, $value) { + if ($value == '..') { + array_pop($parts); + } elseif ($value != '.') { + $parts[] = $value; + } + return $parts; + })); + + if (($resolvedPath = realpath($relativePath)) !== false) { + if (file_exists($absolutePath = "phar://{$resolvedPath}{$pharPath}")) { + return $absolutePath; } - return is_dir($fullPath) ? realpath($fullPath) : null; } } @@ -526,7 +565,7 @@ class Libraries { */ public static function instance($type, $name, array $options = array()) { if (!$class = (string) static::locate($type, $name)) { - throw new ClassNotFoundException("Class '{$name}' of type '{$type}' not found."); + throw new ClassNotFoundException("Class `{$name}` of type `{$type}` not found."); } return class_exists($class) ? new $class($options) : null; } @@ -590,6 +629,10 @@ class Libraries { return $name; } $ident = $name ? ($type . '.' . $name) : ($type . '.*'); +<<<<<<< HEAD +======= + $ident .= $options ? '.' . md5(serialize($options)) : null; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e if (isset(static::$_cachedPaths[$ident])) { return static::$_cachedPaths[$ident]; @@ -804,10 +847,17 @@ class Libraries { $queue = array_merge($queue, array_diff((array) glob("{$dir}/*", $dFlags), $libs)); } $libs = preg_grep($match, $libs); +<<<<<<< HEAD + } + if ($suffix) { + $libs = $options['preFilter'] ? preg_grep($options['preFilter'], $libs) : $libs; + } +======= } if ($suffix) { $libs = $options['preFilter'] ? preg_grep($options['preFilter'], $libs) : $libs; } +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e return static::_filter($libs, (array) $config, $options + compact('name')); } diff --git a/libraries/lithium/data/Collection.php b/libraries/lithium/data/Collection.php index 39e9b00..e2e9345 100644 --- a/libraries/lithium/data/Collection.php +++ b/libraries/lithium/data/Collection.php @@ -166,10 +166,10 @@ abstract class Collection extends \lithium\util\Collection { } /** - * Returns a boolean indicating whether an offset exists for the + * Returns a boolean indicating whether an offset exists for the * current `Collection`. * - * @param string $offset String or integer indicating the offset or + * @param string $offset String or integer indicating the offset or * index of an entity in the set. * @return boolean Result. */ diff --git a/libraries/lithium/data/Entity.php b/libraries/lithium/data/Entity.php index 1f32c67..23903f3 100644 --- a/libraries/lithium/data/Entity.php +++ b/libraries/lithium/data/Entity.php @@ -211,7 +211,11 @@ class Entity extends \lithium\core\Object { */ public function __call($method, $params) { if (!($model = $this->_model) || !method_exists($model, $method)) { +<<<<<<< HEAD $message = "No model bound or unhandled method call '{$method}'."; +======= + $message = "No model bound or unhandled method call `{$method}`."; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e throw new BadMethodCallException($message); } array_unshift($params, $this); diff --git a/libraries/lithium/data/Model.php b/libraries/lithium/data/Model.php index 810506f..15f58c8 100755 --- a/libraries/lithium/data/Model.php +++ b/libraries/lithium/data/Model.php @@ -321,9 +321,13 @@ class Model extends \lithium\core\StaticObject { if (static::_isBase($class = get_called_class())) { return; } +<<<<<<< HEAD $self = static::_object(); $base = get_class_vars(__CLASS__); +======= + $self = static::_object(); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $meta = array(); $schema = array(); $source = array(); @@ -342,6 +346,7 @@ class Model extends \lithium\core\StaticObject { } } $tmp = $options + $self->_meta + $meta; +<<<<<<< HEAD if ($tmp['connection']) { $conn = $classes['connections']::get($tmp['connection']); @@ -352,6 +357,18 @@ class Model extends \lithium\core\StaticObject { $name = static::_name(); $local = compact('class', 'name') + $options + array_diff($self->_meta, $base['_meta']); +======= + $source = array('meta' => array(), 'finders' => array(), 'schema' => array()); + + if ($tmp['connection']) { + $conn = $classes['connections']::get($tmp['connection']); + $source = (($conn) ? $conn->configureClass($class) : array()) + $source; + } + static::$_classes = $classes; + $name = static::_name(); + + $local = compact('class', 'name') + $options + $self->_meta; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $self->_meta = ($local + $source['meta'] + $meta); $self->_meta['initialized'] = false; $self->_schema += $schema + $source['schema']; @@ -389,7 +406,7 @@ class Model extends \lithium\core\StaticObject { preg_match('/^findBy(?P<field>\w+)$|^find(?P<type>\w+)By(?P<fields>\w+)$/', $method, $args); if (!$args) { - $message = "Method %s not defined or handled in class %s"; + $message = "Method `%s` not defined or handled in class `%s`."; throw new BadMethodCallException(sprintf($message, $method, get_class($self))); } $field = Inflector::underscore($args['field'] ? $args['field'] : $args['fields']); @@ -445,7 +462,11 @@ class Model extends \lithium\core\StaticObject { $type = 'first'; } +<<<<<<< HEAD $options += ((array) $self->_query + (array) $defaults); +======= + $options = (array) $options + (array) $self->_query + (array) $defaults; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $meta = array('meta' => $self->_meta, 'name' => get_called_class()); $params = compact('type', 'options'); @@ -572,7 +593,7 @@ class Model extends \lithium\core\StaticObject { $self = static::_object(); if (!isset($self->_relationTypes[$type])) { - throw new ConfigException("Invalid relationship type '{$type}' specified."); + throw new ConfigException("Invalid relationship type `{$type}` specified."); } $rel = static::connection()->relationship(get_called_class(), $type, $name, $config); return static::_object()->_relations[$name] = $rel; @@ -770,7 +791,7 @@ class Model extends \lithium\core\StaticObject { }; if (!$options['callbacks']) { - return $filter($entity, $options); + return $filter($entity, $params); } return static::_filter(__FUNCTION__, $params, $filter); } @@ -895,7 +916,17 @@ class Model extends \lithium\core\StaticObject { if ($conn = $connections::get($name)) { return $conn; } - throw new ConfigException("The data connection {$name} is not configured"); + throw new ConfigException("The data connection `{$name}` is not configured."); + } + + /** + * Gets just the class name portion of a fully-name-spaced class name, i.e. + * `app\models\Posts::_name()` returns `'Posts'`. + * + * @return string + */ + protected static function _name() { + return basename(str_replace('\\', '/', get_called_class())); } /** diff --git a/libraries/lithium/data/entity/Document.php b/libraries/lithium/data/entity/Document.php index 34187d2..78a4a5c 100644 --- a/libraries/lithium/data/entity/Document.php +++ b/libraries/lithium/data/entity/Document.php @@ -104,6 +104,7 @@ class Document extends \lithium\data\Entity implements \Iterator, \ArrayAccess { protected function _init() { parent::_init(); +<<<<<<< HEAD $this->_data = (array) $this->_data; if ($model = $this->_model) { @@ -116,6 +117,17 @@ class Document extends \lithium\data\Entity implements \Iterator, \ArrayAccess { $options = compact('pathKey', 'schema'); $this->_data = $model::connection()->cast($this, $this->_data, $options); } +======= + $data = (array) $this->_data; + $this->_data = array(); + $this->set($data); + $exists = $this->_exists; + + $this->_data = $this->_updated; + $this->_updated = array(); + $this->update(); + $this->_exists = $exists; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e unset($this->_autoConfig); } @@ -176,11 +188,15 @@ class Document extends \lithium\data\Entity implements \Iterator, \ArrayAccess { $this->_updated[$key]->_exists = false; } } +<<<<<<< HEAD return parent::export() + array( 'key' => $this->_pathKey, 'remove' => $this->_removed, ); +======= + return parent::export() + array('key' => $this->_pathKey, 'remove' => $this->_removed); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } public function update($id = null, array $data = array()) { @@ -191,6 +207,10 @@ class Document extends \lithium\data\Entity implements \Iterator, \ArrayAccess { $this->_data[$key]->update(null, isset($data[$key]) ? $data[$key] : array()); } } +<<<<<<< HEAD +======= + $this->_removed = array(); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } /** @@ -264,7 +284,11 @@ class Document extends \lithium\data\Entity implements \Iterator, \ArrayAccess { return $this->_setNested($name, $value); } if ($model = $this->_model) { +<<<<<<< HEAD $pathKey = $this->_pathKey ? $this->_pathKey . '.' : ''; +======= + $pathKey = $this->_pathKey; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $options = compact('pathKey') + array('first' => true); $value = $model::connection()->cast($this, array($name => $value), $options); } @@ -287,12 +311,21 @@ class Document extends \lithium\data\Entity implements \Iterator, \ArrayAccess { } else { unset($next); $next = null; +<<<<<<< HEAD } if ($next === null && ($model = $this->_model)) { $current->_data[$key] = $model::connection()->item($model); $next =& $current->_data[$key]; } +======= + } + + if ($next === null && ($model = $this->_model)) { + $current->__set($key, $model::connection()->item($model)); + $next =& $current->{$key}; + } +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $current =& $next; } @@ -327,6 +360,10 @@ class Document extends \lithium\data\Entity implements \Iterator, \ArrayAccess { */ public function __unset($name) { $this->_removed[$name] = true; +<<<<<<< HEAD +======= + unset($this->_updated[$name]); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } /** @@ -481,9 +518,15 @@ class Document extends \lithium\data\Entity implements \Iterator, \ArrayAccess { * @param string $field The name of the field to be incrememnted. * @param string $value The value to increment the field by. Defaults to `1` if this parameter * is not specified. +<<<<<<< HEAD * @return int Returns the current value of `$field`, based on the value retrieved from the data * source when the entity was loaded, plus any increments applied. Note that it may not * reflect the most current value in the persistent backend data source. +======= + * @return integer Returns the current value of `$field`, based on the value retrieved from the + * data source when the entity was loaded, plus any increments applied. Note that it + * may not reflect the most current value in the persistent backend data source. +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e * @throws UnexpectedValueException Throws an exception when `$field` is set to a non-numeric * type. */ @@ -494,7 +537,11 @@ class Document extends \lithium\data\Entity implements \Iterator, \ArrayAccess { $this->_increment[$field] += $value; if (!is_numeric($this->_data[$field])) { +<<<<<<< HEAD throw new UnexpectedValueException("Field '{$field}' cannot be incremented."); +======= + throw new UnexpectedValueException("Field `{$field}` cannot be incremented."); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } $this->_data[$field] += $value; } diff --git a/libraries/lithium/data/model/Query.php b/libraries/lithium/data/model/Query.php index 5989d4f..49a3ee8 100644 --- a/libraries/lithium/data/model/Query.php +++ b/libraries/lithium/data/model/Query.php @@ -84,6 +84,7 @@ class Query extends \lithium\core\Object { 'calculate' => null, 'conditions' => array(), 'fields' => array(), + 'data' => array(), 'model' => null, 'alias' => null, 'source' => null, @@ -117,6 +118,15 @@ class Query extends \lithium\core\Object { if ($this->_config['with']) { $this->_associate($this->_config['with']); } +<<<<<<< HEAD +======= + $joins = $this->_config['joins']; + $this->_config['joins'] = array(); + + foreach ($joins as $i => $join) { + $this->join($i, $join); + } +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e if ($this->_entity && !$this->_config['model']) { $this->model($this->_entity->model()); } @@ -136,6 +146,10 @@ class Query extends \lithium\core\Object { * Generates a schema map of the query's result set, where the keys are fully-namespaced model * class names, and the values are arrays of field names. * +<<<<<<< HEAD +======= + * @param array $map +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e * @return array */ public function map($map = null) { @@ -361,11 +375,21 @@ class Query extends \lithium\core\Object { * @return array of query objects */ public function join($name = null, $join = null) { +<<<<<<< HEAD +======= + if (is_scalar($name) && !$join && isset($this->_config['joins'][$name])) { + return $this->_config['joins'][$name]; + } +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e if ($name && !$join) { $join = $name; $name = null; } if ($join) { +<<<<<<< HEAD +======= + $join = is_array($join) ? $this->_instance(get_class($this), $join) : $join; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $name ? $this->_config['joins'][$name] = $join : $this->_config['joins'][] = $join; return $this; } @@ -397,6 +421,7 @@ class Query extends \lithium\core\Object { if (in_array($item, $keys)) { $results[$item] = $this->_config[$item]; } +<<<<<<< HEAD } $entity =& $this->_entity; $data = $this->_data; @@ -415,6 +440,18 @@ class Query extends \lithium\core\Object { if (!isset($results['fields'])) { return $results; } +======= + } + if (in_array('data', $keys)) { + $results['data'] = $this->_exportData(); + } + if (isset($results['source'])) { + $results['source'] = $dataSource->name($results['source']); + } + if (!isset($results['fields'])) { + return $results; + } +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $created = array('fields', 'values'); if (is_array($results['fields']) && array_keys($results['fields']) == $created) { @@ -423,6 +460,35 @@ class Query extends \lithium\core\Object { return $results; } +<<<<<<< HEAD +======= + /** + * Helper method used by `export()` to extract the data either from a bound entity, or from + * passed configuration, and filter it through a configured whitelist, if present. + * + * @return array + */ + protected function _exportData() { + $data = $this->_entity ? $this->_entity->export() : $this->_data; + + if (!$list = $this->_config['whitelist']) { + return $data; + } + $list = array_combine($list, $list); + + if (!$this->_entity) { + return array_intersect_key($data, $list); + } + foreach ($data as $type => $values) { + if (!is_array($values)) { + continue; + } + $data[$type] = array_intersect_key($values, $list); + } + return $data; + } + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e public function schema($field = null) { if (is_array($field)) { $this->_config['schema'] = $field; @@ -500,7 +566,7 @@ class Query extends \lithium\core\Object { $config = array(); } if (!$relation = $model::relations($name)) { - throw new QueryException("Related model not found"); + throw new QueryException("Related model not found."); } $config += $relation->data(); } diff --git a/libraries/lithium/data/source/Database.php b/libraries/lithium/data/source/Database.php index c1f40fb..1b09dae 100755 --- a/libraries/lithium/data/source/Database.php +++ b/libraries/lithium/data/source/Database.php @@ -43,7 +43,11 @@ abstract class Database extends \lithium\data\Source { 'update' => "UPDATE {:source} SET {:fields} {:conditions};{:comment}", 'delete' => "DELETE {:flags} FROM {:source} {:alias} {:conditions};{:comment}", 'schema' => "CREATE TABLE {:source} (\n{:columns}{:indexes});{:comment}", +<<<<<<< HEAD 'join' => "{:type} JOIN {:source} {:alias} {:constraint} {:conditions}" +======= + 'join' => "{:type} JOIN {:source} {:alias} {:constraint}" +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e ); /** @@ -273,8 +277,13 @@ abstract class Database extends \lithium\data\Source { while ($data = $result->next()) { // @hack: Fix this to support relationships +<<<<<<< HEAD if ((count($columns) != count($data) && isset($columns[0])) || is_array($columns[0])) { $columns = $columns[0]; +======= + if (count($columns) != count($data) && is_array(current($columns))) { + $columns = current($columns); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } $records[] = array_combine($columns, $data); } @@ -390,7 +399,7 @@ abstract class Database extends \lithium\data\Source { $type = $context->type(); } if (!isset($this->_strings[$type])) { - throw new InvalidArgumentException("Invalid query type '{$type}'"); + throw new InvalidArgumentException("Invalid query type `{$type}`."); } $data = array_filter($data); return trim(String::insert($this->_strings[$type], $data, array('clean' => true))); diff --git a/libraries/lithium/data/source/MongoDb.php b/libraries/lithium/data/source/MongoDb.php index 379dba7..c118e1b 100644 --- a/libraries/lithium/data/source/MongoDb.php +++ b/libraries/lithium/data/source/MongoDb.php @@ -374,11 +374,20 @@ class MongoDb extends \lithium\data\Source { return $this->_filter(__METHOD__, $params, function($self, $params) use ($_config, $_exp) { $query = $params['query']; $options = $params['options']; +<<<<<<< HEAD $data = $_exp::get('create', $query->entity()->export()); $gridCol = "{$_config['gridPrefix']}.files"; $source = $query->source(); if ($source == $gridCol && isset($data['create']['file'])) { +======= + + $args = $query->export($self, array('keys' => array('source', 'data'))); + $data = $_exp::get('create', $args['data']); + $source = $args['source']; + + if ($source == "{$_config['gridPrefix']}.files" && isset($data['create']['file'])) { +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $result = array('ok' => true); $data['create']['_id'] = $self->invokeMethod('_saveFile', array($data['create'])); } else { @@ -450,7 +459,6 @@ class MongoDb extends \lithium\data\Source { $query = $params['query']; $options = $params['options']; $args = $query->export($self); - $self->connection->resetError(); $source = $args['source']; if ($group = $args['group']) { @@ -505,7 +513,11 @@ class MongoDb extends \lithium\data\Source { return $this->_filter(__METHOD__, $params, function($self, $params) use ($_config, $_exp) { $options = $params['options']; $query = $params['query']; +<<<<<<< HEAD $args = $query->export($self, array('keys' => array('conditions', 'source'))); +======= + $args = $query->export($self, array('keys' => array('conditions', 'source', 'data'))); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $source = $args['source']; $data = $args['data']; @@ -518,6 +530,12 @@ class MongoDb extends \lithium\data\Source { } $update = $query->entity() ? $_exp::toCommand($data) : $data; +<<<<<<< HEAD +======= + if ($options['multiple'] && !preg_grep('/^\$/', array_keys($update))) { + $update = array('$set' => $update); + } +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e if ($self->connection->{$source}->update($args['conditions'], $update, $options)) { $query->entity() ? $query->entity()->update() : null; return true; diff --git a/libraries/lithium/data/source/database/adapter/MySql.php b/libraries/lithium/data/source/database/adapter/MySql.php index 8533658..69abcae 100755 --- a/libraries/lithium/data/source/database/adapter/MySql.php +++ b/libraries/lithium/data/source/database/adapter/MySql.php @@ -133,6 +133,10 @@ class MySql extends \lithium\data\source\Database { return false; } + if (!$this->connection) { + return false; + } + if (mysql_select_db($config['database'], $this->connection)) { $this->_isConnected = true; } else { @@ -271,10 +275,10 @@ class MySql extends \lithium\data\source\Database { } $result = array(); - $count = mysql_num_fields($resource); + $count = mysql_num_fields($resource->resource()); for ($i = 0; $i < $count; $i++) { - $result[] = mysql_field_name($resource, $i); + $result[] = mysql_field_name($resource->resource(), $i); } return $result; } @@ -301,9 +305,15 @@ class MySql extends \lithium\data\source\Database { /** * @todo Eventually, this will need to rewrite aliases for DELETE and UPDATE queries, same with * order(). +<<<<<<< HEAD * @param string $conditions * @param string $context * @param array $options +======= + * @param string $conditions + * @param string $context + * @param array $options +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e * @return void */ public function conditions($conditions, $context, array $options = array()) { diff --git a/libraries/lithium/data/source/database/adapter/Sqlite3.php b/libraries/lithium/data/source/database/adapter/Sqlite3.php index e381951..e5c43aa 100755 --- a/libraries/lithium/data/source/database/adapter/Sqlite3.php +++ b/libraries/lithium/data/source/database/adapter/Sqlite3.php @@ -12,6 +12,7 @@ namespace lithium\data\source\database\adapter; use SQLite3 as SQLite; use SQLite3Result; use lithium\data\model\QueryException; +use \lithium\data\source\database\adapter\sqlite3\Result; /** * Sqlite database driver @@ -63,7 +64,7 @@ class Sqlite3 extends \lithium\data\source\Database { public function __construct(array $config = array()) { $defaults = array( 'database' => '', - 'flags' => NULL, + 'flags' => SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, 'key' => NULL ); parent::__construct($config + $defaults); @@ -154,7 +155,9 @@ class Sqlite3 extends \lithium\data\source\Database { * @param object $query The given query, usually an instance of `lithium\data\model\Query`. * @return void */ - protected function _insertId($query) {} + protected function _insertId($query) { + return $this->connection->lastInsertRowID(); + } /** * Gets or sets the encoding for the connection. @@ -188,7 +191,7 @@ class Sqlite3 extends \lithium\data\source\Database { if (is_array($value)) { return parent::value($value, $schema); } - return $this->connection->escapeString($value); + return "'" . $this->connection->escapeString($value) . "'"; } /** @@ -255,9 +258,9 @@ class Sqlite3 extends \lithium\data\source\Database { if (!($result = $conn->query($sql)) instanceof SQLite3Result) { list($code, $error) = $self->error(); - throw new QueryException("$sql: $error", $code); + throw new QueryException("{$sql}: {$error}", $code); } - return $result; + return new Result(array('resource' => $result)); }); } diff --git a/libraries/lithium/data/source/database/adapter/sqlite3/Result.php b/libraries/lithium/data/source/database/adapter/sqlite3/Result.php index 6986a49..7214ff9 100644 --- a/libraries/lithium/data/source/database/adapter/sqlite3/Result.php +++ b/libraries/lithium/data/source/database/adapter/sqlite3/Result.php @@ -16,14 +16,32 @@ class Result extends \lithium\data\source\database\Result { if (!$this->_resource instanceof SQLite3Result) { return; } +<<<<<<< HEAD return $resource->fetchArray(SQLITE3_ASSOC); +======= + return $this->_resource->fetchArray(SQLITE3_ASSOC); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } protected function _close() { if (!$this->_resource instanceof SQLite3Result) { return; } +<<<<<<< HEAD $resource->finalize(); +======= + $this->_resource->finalize(); + } + + public function __call($name, $arguments) { + if (!$this->_resource instanceof SQLite3Result) { + return; + } + + if(is_callable(array($this->_resource, $name))) { + return call_user_method_array($name, $this->_resource, $arguments); + } +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } } diff --git a/libraries/lithium/data/source/http/adapter/CouchDb.php b/libraries/lithium/data/source/http/adapter/CouchDb.php index 5004f21..8a1c535 100644 --- a/libraries/lithium/data/source/http/adapter/CouchDb.php +++ b/libraries/lithium/data/source/http/adapter/CouchDb.php @@ -43,8 +43,13 @@ class CouchDb extends \lithium\data\source\Http { protected $_classes = array( 'service' => 'lithium\net\http\Service', 'entity' => 'lithium\data\entity\Document', +<<<<<<< HEAD 'array' => 'lithium\data\collection\DocumentArray', 'set' => 'lithium\data\collection\DocumentSet' +======= + 'set' => 'lithium\data\collection\DocumentSet', + 'array' => 'lithium\data\collection\DocumentArray', +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e ); protected $_handlers = array(); @@ -153,7 +158,7 @@ class CouchDb extends \lithium\data\source\Http { } } if (!$this->_db) { - throw new ConfigException("{$entity} is not available."); + throw new ConfigException("Database `{$entity}` is not available."); } } diff --git a/libraries/lithium/g11n/Locale.php b/libraries/lithium/g11n/Locale.php index 17bb845..3328a70 100644 --- a/libraries/lithium/g11n/Locale.php +++ b/libraries/lithium/g11n/Locale.php @@ -8,8 +8,8 @@ namespace lithium\g11n; -use \BadMethodCallException; -use \InvalidArgumentException; +use BadMethodCallException; +use InvalidArgumentException; /** * The `Locale` class provides methods to deal with locale identifiers. The locale @@ -70,7 +70,7 @@ class Locale extends \lithium\core\StaticObject { $tags = static::invokeMethod('decompose', $params); if (!isset(static::$_tags[$method])) { - throw new BadMethodCallException("Invalid locale tag `{$method}`"); + throw new BadMethodCallException("Invalid locale tag `{$method}`."); } return isset($tags[$method]) ? $tags[$method] : null; } @@ -109,7 +109,7 @@ class Locale extends \lithium\core\StaticObject { $regex .= '(?:[_-](?P<variant>[a-z]{5,}))?'; if (!preg_match("/^{$regex}$/i", $locale, $matches)) { - throw new InvalidArgumentException("Locale `{$locale}` could not be parsed"); + throw new InvalidArgumentException("Locale `{$locale}` could not be parsed."); } return array_filter(array_intersect_key($matches, static::$_tags)); } diff --git a/libraries/lithium/g11n/catalog/adapter/Code.php b/libraries/lithium/g11n/catalog/adapter/Code.php index ccbd7b0..ee90ba9 100644 --- a/libraries/lithium/g11n/catalog/adapter/Code.php +++ b/libraries/lithium/g11n/catalog/adapter/Code.php @@ -48,9 +48,8 @@ class Code extends \lithium\g11n\catalog\Adapter { protected function _init() { parent::_init(); if (!is_dir($this->_config['path'])) { - throw new ConfigException( - "Code directory does not exist at `{$this->_config['path']}`" - ); + $message = "Code directory does not exist at path `{$this->_config['path']}`."; + throw new ConfigException($message); } } diff --git a/libraries/lithium/g11n/catalog/adapter/Gettext.php b/libraries/lithium/g11n/catalog/adapter/Gettext.php index 4ade215..1ecd86e 100644 --- a/libraries/lithium/g11n/catalog/adapter/Gettext.php +++ b/libraries/lithium/g11n/catalog/adapter/Gettext.php @@ -92,7 +92,8 @@ class Gettext extends \lithium\g11n\catalog\Adapter { protected function _init() { parent::_init(); if (!is_dir($this->_config['path'])) { - throw new ConfigException("Gettext directory does not exist at `{$this->_config['path']}`"); + $message = "Gettext directory does not exist at path `{$this->_config['path']}`."; + throw new ConfigException($message); } } @@ -265,7 +266,7 @@ class Gettext extends \lithium\g11n\catalog\Adapter { $stat = fstat($stream); if ($stat['size'] < self::MO_HEADER_SIZE) { - throw new RangeException("MO stream caontent has an invalid format"); + throw new RangeException("MO stream content has an invalid format."); } $magic = unpack('V1', fread($stream, 4)); $magic = hexdec(substr(dechex(current($magic)), -8)); @@ -275,7 +276,7 @@ class Gettext extends \lithium\g11n\catalog\Adapter { } elseif ($magic == self::MO_BIG_ENDIAN_MAGIC) { $isBigEndian = true; } else { - throw new RangeException("MO stream content has an invalid format"); + throw new RangeException("MO stream content has an invalid format."); } $header = array( diff --git a/libraries/lithium/g11n/catalog/adapter/Php.php b/libraries/lithium/g11n/catalog/adapter/Php.php index 8435e62..464fdd4 100644 --- a/libraries/lithium/g11n/catalog/adapter/Php.php +++ b/libraries/lithium/g11n/catalog/adapter/Php.php @@ -71,7 +71,8 @@ class Php extends \lithium\g11n\catalog\Adapter { protected function _init() { parent::_init(); if (!is_dir($this->_config['path'])) { - throw new ConfigException("Php directory does not exist at `{$this->_config['path']}`"); + $message = "Php directory does not exist at path `{$this->_config['path']}`."; + throw new ConfigException($message); } } diff --git a/libraries/lithium/net/http/Media.php b/libraries/lithium/net/http/Media.php index b81ec36..a79b6ea 100755 --- a/libraries/lithium/net/http/Media.php +++ b/libraries/lithium/net/http/Media.php @@ -463,8 +463,8 @@ class Media extends \lithium\core\StaticObject { /** * Gets the physical path to the web assets (i.e. `/webroot`) directory of a library. * - * @param string $library The name of the library for which to find the path, or `true` for the - * default library. + * @param string|boolean $library The name of the library for which to find the path, or `true` + * for the default library. * @return string Returns the physical path to the web assets directory for a library. For * example, the `/webroot` directory of the default library would be * `LITHIUM_APP_PATH . '/webroot'`. @@ -551,6 +551,7 @@ class Media extends \lithium\core\StaticObject { $result = null; $type = $options['type']; +<<<<<<< HEAD if (!isset($handlers[$type])) { throw new MediaException("Unhandled media type '{$type}'"); @@ -559,6 +560,16 @@ class Media extends \lithium\core\StaticObject { $filter = function($v) { return $v !== null; }; $handler = array_filter($handler, $filter) + $handlers['default'] + $defaults; +======= + + if (!isset($handlers[$type])) { + throw new MediaException("Unhandled media type `{$type}`."); + } + $handler = $options + $handlers[$type] + $defaults; + $filter = function($v) { return $v !== null; }; + $handler = array_filter($handler, $filter) + $handlers['default'] + $defaults; + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e if (isset($types[$type])) { $response->headers('Content-type', current((array) $types[$type])); } @@ -574,6 +585,7 @@ class Media extends \lithium\core\StaticObject { * type must have an `'encode'` setting specified in `Media::$_handlers`. * @param mixed $data Arbitrary data you wish to encode. Note that some encoders can only handle * arrays or objects. + * @param object $response A reference to the `Response` object for this dispatch cycle. * @param array $options Handler-specific options. * @return mixed */ @@ -658,8 +670,10 @@ class Media extends \lithium\core\StaticObject { case $handler['encode']: return $self::encode($handler, $data, $response); case class_exists($handler['view']): - $view = new $handler['view']($handler + array('response' => &$response)); - return $view->render('all', $data, $options); + $class = $handler['view']; + unset($handler['view'], $options['view']); + $view = new $class($handler + array('response' => &$response)); + return $view->render("all", (array) $data, $options); case ($handler['template'] === false) && is_string($data): return $data; default: @@ -730,6 +744,10 @@ class Media extends \lithium\core\StaticObject { 'paths' => array( 'template' => '{:library}/views/{:controller}/{:template}.{:type}.php', 'layout' => '{:library}/views/layouts/{:layout}.{:type}.php', +<<<<<<< HEAD +======= + 'element' => '{:library}/views/elements/{:template}.{:type}.php' +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e ) ), 'html' => array(), diff --git a/libraries/lithium/net/http/Router.php b/libraries/lithium/net/http/Router.php index 6e20627..2a87677 100644 --- a/libraries/lithium/net/http/Router.php +++ b/libraries/lithium/net/http/Router.php @@ -194,7 +194,7 @@ class Router extends \lithium\core\StaticObject { return $path; } if (is_string($url = static::_parseString($url, $context))) { - return $url; + return static::_prefix($url, $context, $options); } } if (isset($url[0]) && is_array($params = static::_parseString($url[0], $context))) { diff --git a/libraries/lithium/security/Auth.php b/libraries/lithium/security/Auth.php index c3b2ab8..9230c11 100644 --- a/libraries/lithium/security/Auth.php +++ b/libraries/lithium/security/Auth.php @@ -113,7 +113,7 @@ class Auth extends \lithium\core\Adaptable { $config = $self::invokeMethod('_config', array($name)); if ($config === null) { - throw new ConfigException("Configuration '{$name}' has not been defined."); + throw new ConfigException("Configuration `{$name}` has not been defined."); } $session = $config['session']; diff --git a/libraries/lithium/template/Helper.php b/libraries/lithium/template/Helper.php index 8c69801..b2cbae7 100644 --- a/libraries/lithium/template/Helper.php +++ b/libraries/lithium/template/Helper.php @@ -42,6 +42,13 @@ abstract class Helper extends \lithium\core\Object { protected $_context = null; /** + * This property can be overwritten with any class dependencies a helper subclass has. + * + * @var array + */ + protected $_classes = array(); + + /** * Auto configuration properties. * * @var array @@ -133,7 +140,13 @@ abstract class Helper extends \lithium\core\Object { if ($this->_context) { foreach ($params as $key => $value) { +<<<<<<< HEAD $params[$key] = $this->_context->applyHandler($this, $method, $key, $value, $options); +======= + $params[$key] = $this->_context->applyHandler( + $this, $method, $key, $value, $options + ); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } $strings = $this->_context->strings(); } diff --git a/libraries/lithium/template/View.php b/libraries/lithium/template/View.php index 95bf695..239bfcd 100644 --- a/libraries/lithium/template/View.php +++ b/libraries/lithium/template/View.php @@ -8,36 +8,47 @@ namespace lithium\template; -use RuntimeException; use lithium\core\Libraries; +use lithium\template\TemplateException; /** * As one of the three pillars of the Model-View-Controller design pattern, the `View` class * (along with other supporting classes) is responsible for taking the data passed from the - * request and/or controller, inserting this into the requested view/layout, and then presenting - * the rendered content in the appropriate content-type. + * request and/or controller, inserting this into the requested template/layout, and then returning + * the rendered content. * * The `View` class interacts with a variety of other classes in order to achieve maximum * flexibility and configurability at all points in the view rendering and presentation * process. The `Loader` class is tasked with locating and reading template files which are then * passed to the `Renderer` adapter subclass. * - * It is also possible to instantiate and call `View` directly, in cases where you wish to bypass - * all other parts of the framework and simply return rendered content. + * In the default configuration, the `File` adapter acts as both renderer and loader, loading files + * from paths defined in _process steps_ (described below) and rendering them as plain PHP files, + * augmented with [special syntax](../template). + * + * The `View` class operates on _processes_, which define the steps to render a completed view. For + * example, the default process, which renders a template wrapped in a layout, is comprised of two + * _steps_: the first step renders the main template and captures it to the rendering context, where + * it is embedded in the layout in the second step. See the `$_steps` and `$_processes` properties + * for more information. + * + * Using steps and processes, you can create rendering scenarios to suit very complex needs. + * + * By default, the `View` class is called during the course of the framework's dispatch cycle by the + * `Media` class. However, it is also possible to instantiate and call `View` directly, in cases + * where you wish to bypass all other parts of the framework and simply return rendered content. * * A simple example, using the `Simple` renderer/loader for string templates: * * {{{ * $view = new View(array('loader' => 'Simple', 'renderer' => 'Simple')); - * echo $view->render(array('element' => 'Hello, {:name}!'), array( - * 'name' => "Robert" - * )); + * echo $view->render('element', array('name' => "Robert"), array('element' => 'Hello, {:name}!')); * * // Output: * "Hello, Robert!"; * }}} * - * (note: This is easily adapted for XML templating). + * _Note_: This is easily adapted for XML templating. * * Another example, this time of something that could be used in an appliation * error handler: @@ -50,15 +61,17 @@ use lithium\core\Libraries; * ) * )); * - * echo $View->render('all', array('content' => $info), array( + * $page = $View->render('all', array('content' => $info), array( * 'template' => '404', - * 'type' => 'html', * 'layout' => 'error' * )); * }}} * - * @see lithium\view\Renderer - * @see lithium\view\adapter + * To learn more about processes and process steps, see the `$_processes` and `$_steps` properties, + * respectively. + * + * @see lithium\template\view\Renderer + * @see lithium\template\view\adapter * @see lithium\net\http\Media */ class View extends \lithium\core\Object { @@ -105,21 +118,81 @@ class View extends \lithium\core\Object { protected $_renderer = null; /** + * View processes are aggregated lists of steps taken to to create a complete, rendered view. + * For example, the default process, `'all'`, renders a template, then renders a layout, using + * the rendered template content. A process can be defined using one or more steps defined in + * the `$_steps` property. Each process definition is a simple array of ordered values, where + * each value is a key in the `$_steps` array. + * + * @see lithium\template\View::$_steps + * @see lithium\template\View::render() + * @var array + */ + protected $_processes = array( + 'all' => array('template', 'layout'), + 'template' => array('template'), + 'element' => array('element') + ); + + /** + * The list of available rendering steps. Each step contains instructions for how to render one + * piece of a multi-step view rendering. The `View` class combines multiple steps into + * _processes_ to create the final output. + * + * Each step is named by its key in the `$_steps` array, and can have the following options: + * + * - `'path'` _string_: Indicates the set of paths to use when loading templates. + * + * - `'conditions'` _mixed_: Make the step dependent on a value being present, or on some other + * arbitrary condition. If a `'conditions'` is a string, it indicates that a key with that + * name must be present in the `$options` passed to `render()`, and must be set to a + * non-empty value. If a closure, it will be executed with the rendering parameters, and must + * return `true` or `false`. In either case, if the condition is satisfied, the step is + * processed. Otherwise, it is skipped. See the `_conditions()` method for more information. + * + * - `'capture'` _array_: If specified, allows the results of this rendering step to be assigned + * to a template variable used in subsequent steps, or to the templating context for use in + * subsequent steps. If can be specified in the form of `array('context' => '<var-name>')` or + * `array('data' => '<var-name>')`. If the `'context'` key is used, the results are captured + * to the rendering context. Likewise with the `'data'` key, results are captured to a + * template variable. + * + * - `'multi'` _boolean_: If set to `true`, the rendering parameter matching the name of this + * step can be an array containing multiple values, in which case this step is executed + * multiple times, once for each value of the array. + * + * @see lithium\template\View::$_processes + * @see lithium\template\View::render() + * @var array + */ + protected $_steps = array( + 'template' => array('path' => 'template', 'capture' => array('context' => 'content')), + 'layout' => array( + 'path' => 'layout', 'conditions' => 'layout', 'multi' => true, 'capture' => array( + 'context' => 'content' + ) + ), + 'element' => array('path' => 'element') + ); + + /** * Auto-configuration parameters. * * @var array Objects to auto-configure. */ - protected $_autoConfig = array('request', 'response'); + protected $_autoConfig = array( + 'request', 'response', 'processes' => 'merge', 'steps' => 'merge' + ); /** * Constructor. * * @param array $config Configuration parameters. * The available options are: - * - `loader`: For locating/reading view, layout and element - * templates. Defaults to `File`. - * - `renderer`: Populates the view/layout with the data set from the controller. - * Defaults to `File`. + * - `'loader'` _mixed_: For locating/reading view, layout and element + * templates. Defaults to `File`. + * - `'renderer'` _mixed_: Populates the view/layout with the data set from the + * controller. Defaults to `'File'`. * - `request`: The request object to be made available in the view. Defalts to `null`. * - `vars`: Defaults to `array()`. * @return void @@ -131,6 +204,8 @@ class View extends \lithium\core\Object { 'vars' => array(), 'loader' => 'File', 'renderer' => 'File', + 'steps' => array(), + 'processes' => array(), 'outputFilters' => array() ); parent::__construct($config + $defaults); @@ -140,7 +215,6 @@ class View extends \lithium\core\Object { * Perform initialization of the View. * * @return void - * @throws RuntimeException when template adapter cannot be found. */ protected function _init() { parent::_init(); @@ -166,48 +240,51 @@ class View extends \lithium\core\Object { $this->outputFilters += compact('h') + $this->_config['outputFilters']; } - /** - * Render a layout, template, view or element. - * - * @param string|array $type The view type. Possible values are `element`, `template`, - * `layout` and `all`. - * @param array $data The data to be made available in the rendered view. - * @param array $options Rendering options: - * - `context`: Render context - * - `type`: The media type to render. Defaults to `html`. - * - `layout`: The layout in which the rendered view should be wrapped in. - * @return string The rendered view that was requested. - */ - public function render($type, $data = null, array $options = array()) { - $defaults = array('context' => array(), 'type' => 'html', 'layout' => null); + public function render($process, array $data = array(), array $options = array()) { + $defaults = array( + 'type' => 'html', + 'layout' => null, + 'template' => null, + 'context' => array(), + ); $options += $defaults; - $data = $data ?: array(); - $template = null; + $data += isset($options['data']) ? (array) $options['data'] : array(); + $paths = isset($options['paths']) ? (array) $options['paths'] : array(); + unset($options['data'], $options['paths']); + $params = array_filter($options, function($val) { return $val && is_string($val); }); + $result = null; - if (is_array($type)) { - list($type, $template) = each($type); + foreach ($this->_process($process, $params) as $name => $step) { + if (!$this->_conditions($step, $params, $data, $options)) { + continue; + } + if ($step['multi'] && isset($options[$name])) { + foreach ((array) $options[$name] as $value) { + $params[$name] = $value; + $result = $this->_step($step, $params, $data, $options); + } + continue; + } + $result = $this->_step((array) $step, $params, $data, $options); } - return $this->{"_" . $type}($template, $data, $options); + return $result; } - /** - * The 'all' render type handler. - * - * @param string $template Not used in this handler. Can be specified as null. - * @param array $data Template data. - * @param array $options Layout rendering options. - */ - protected function _all($template, $data, array $options = array()) { - $content = $this->render('template', $data, $options); - - if (!$options['layout']) { - return $content; + protected function _conditions($step, $params, $data, $options) { + if (!$conditions = $step['conditions']) { + return true; + } + if (is_callable($conditions) && !$conditions($params, $data, $options)) { + return false; + } + if (is_string($conditions) && !(isset($options[$conditions]) && $options[$conditions])) { + return false; } - $options['context'] += array('content' => $content); - return $this->_layout($template, $data, $options); + return true; } +<<<<<<< HEAD /** * The 'element' render type handler. * @@ -226,11 +303,39 @@ class View extends \lithium\core\Object { return $_renderer->render($params['template'], $params['data'], $params['options']); }; return $this->_filter(__METHOD__, $params, $filter); +======= + protected function _step(array $step, array $params, array &$data, array &$options = array()) { + $step += array('path' => null, 'capture' => null); + $_renderer = $this->_renderer; + $_loader = $this->_loader; + $filters = $this->outputFilters; + $params = compact('step', 'params', 'options') + array('data' => $data + $filters); + + $filter = function($self, $params) use (&$_renderer, &$_loader) { + $template = $_loader->template($params['step']['path'], $params['params']); + return $_renderer->render($template, $params['data'], $params['options']); + }; + $result = $this->_filter(__METHOD__, $params, $filter); + + if (is_array($step['capture'])) { + switch (key($step['capture'])) { + case 'context': + $options['context'][current($step['capture'])] = $result; + break; + case 'data': + $data[current($step['capture'])] = $result; + break; + } + } + return $result; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } /** - * The 'template' render type handler. + * Converts a process name to an array containing the rendering steps to be executed for each + * process. * +<<<<<<< HEAD * @param string $template Template to be rendered. * @param array $data Template data. * @param array $options Renderer options. @@ -264,6 +369,47 @@ class View extends \lithium\core\Object { return $_renderer->render($params['template'], $params['data'], $params['options']); }; return $this->_filter(__METHOD__, $params, $filter); +======= + * @param string $process A named set of rendering steps. + * @param array $params + * @return array A 2-dimensional array that defines the rendering process. The first dimension + * is a numerically-indexed array containing each rendering step. The second dimension + * represents the parameters for each step. + */ + protected function _process($process, &$params) { + $defaults = array('conditions' => null, 'multi' => false); + + if (!is_array($process)) { + if (!isset($this->_processes[$process])) { + throw new TemplateException("Undefined rendering process '{$process}'."); + } + $process = $this->_processes[$process]; + } + if (is_string(key($process))) { + return $this->_convertSteps($process, $params, $defaults); + } + $result = array(); + + foreach ($process as $step) { + if (is_array($step)) { + $result[] = $step + $defaults; + continue; + } + if (!isset($this->_steps[$step])) { + throw new TemplateException("Undefined rendering step '{$step}'."); + } + $result[$step] = $this->_steps[$step] + $defaults; + } + return $result; + } + + protected function _convertSteps($command, &$params, $defaults) { + if (count($command) == 1) { + $params['template'] = current($command); + return array(array('path' => key($command)) + $defaults); + } + return $command; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } } diff --git a/libraries/lithium/template/helper/Form.php b/libraries/lithium/template/helper/Form.php index 5cfbd2d..837314c 100755 --- a/libraries/lithium/template/helper/Form.php +++ b/libraries/lithium/template/helper/Form.php @@ -19,7 +19,7 @@ use UnexpectedValueException; * * For example, assuming you have created a `Post` model in your application: * {{{// In controller code: - * use \app\models\Post; + * use app\models\Post; * $post = Post::find(1); * return compact('post'); * @@ -144,7 +144,7 @@ class Form extends \lithium\template\Helper { if (in_array($method, array('create', 'end', 'label', 'error'))) { return; } - if (!$name || ($method == 'hidden' && $name = '_method')) { + if (!$name || ($method == 'hidden' && $name == '_method')) { return; } $id = Inflector::camelize(Inflector::slug($name)); @@ -164,7 +164,10 @@ class Form extends \lithium\template\Helper { */ protected function _init() { parent::_init(); - $this->_context->handlers(array('wrap' => '_attributes')); + + if ($this->_context) { + $this->_context->handlers(array('wrap' => '_attributes')); + } } /** @@ -250,8 +253,10 @@ class Form extends \lithium\template\Helper { * attributes passed in `$options`. */ public function create($binding = null, array $options = array()) { + $request = $this->_context ? $this->_context->request() : null; + $defaults = array( - 'url' => $this->_context->request()->params, + 'url' => $request ? $request->params : array(), 'type' => null, 'action' => null, 'method' => $binding ? ($binding->exists() ? 'put' : 'post') : 'post' @@ -387,15 +392,7 @@ class Form extends \lithium\template\Helper { */ public function field($name, array $options = array()) { if (is_array($name)) { - $return = ''; - foreach ($name as $field => $label) { - if (is_numeric($field)) { - $field = $label; - unset($label); - } - $return .= $this->field($field, compact('label') + $options); - } - return $return; + return $this->_fields($name, $options); } $defaults = array( 'label' => null, @@ -419,7 +416,7 @@ class Form extends \lithium\template\Helper { $type = $options['type']; $label = $input = null; - if ($options['label'] === null || $options['label']) { + if (($options['label'] === null || $options['label']) && $options['type'] != 'hidden') { $for = isset($options['id']) ? $options['id'] : ''; $label = $options['label'] ?: $options['label'] = Inflector::humanize($name); $label = $this->label($for, $label); @@ -438,6 +435,28 @@ class Form extends \lithium\template\Helper { } /** + * Helper method used by `Form::field()` for iterating over an array of multiple fields. + * + * @see lithium\template\helper\Form::field() + * @param array $fields An array of fields to render. + * @param array $options The array of options to apply to all fields in the `$fields` array. See + * the `$options` parameter of the `field` method for more information. + * @return string Returns the fields rendered by `field()`, each separated by a newline. + */ + protected function _fields(array $fields, array $options = array()) { + $result = array(); + + foreach ($fields as $field => $label) { + if (is_numeric($field)) { + $field = $label; + unset($label); + } + $result[] = $this->field($field, compact('label') + $options); + } + return join("\n", $result); + } + + /** * Generates an HTML `<input type="submit" />` object. * * @param string $title The title of the submit button. @@ -555,7 +574,7 @@ class Form extends \lithium\template\Helper { } } if ($scope['hidden']) { - $out = $this->hidden($name, array('value' => '')); + $out = $this->hidden($name, array('value' => '', 'id' => false)); } $options['value'] = $scope['value']; return $out . $this->_render(__METHOD__, $template, compact('name', 'options')); @@ -589,7 +608,7 @@ class Form extends \lithium\template\Helper { /** * Generates an HTML `<label></label>` object. * - * @param string $name The name of the field that the label is for. + * @param string $name The DOM ID of the field that the label is for. * @param string $title The content inside the `<label></label>` object. * @param array $options Besides HTML attributes, this parameter allows one additional flag: * - `'escape'` _boolean_: Defaults to `true`. Indicates whether the title of the @@ -661,14 +680,7 @@ class Form extends \lithium\template\Helper { protected function _defaults($method, $name, $options) { $methodConfig = isset($this->_config[$method]) ? $this->_config[$method] : array(); $options += $methodConfig + $this->_config['base']; - - foreach ($this->_config['attributes'] as $key => $generator) { - if (!isset($options[$key]) && $generator) { - if (($attr = $generator($method, $name, $options)) !== null) { - $options[$key] = $attr; - } - } - } + $options = $this->_generators($method, $name, $options); $hasValue = ( (!isset($options['value']) || $options['value'] === null) && @@ -691,6 +703,36 @@ class Form extends \lithium\template\Helper { $template = isset($this->_templateMap[$tplKey]) ? $this->_templateMap[$tplKey] : $tplKey; return array($name, $options, $template); } + + /** + * Iterates over the configured attribute generators, and modifies the settings for a tag. + * + * @param string $method The name of the helper method which was called, i.e. `'text'`, + * `'select'`, etc. + * @param string $name The name of the field whose attributes are being generated. Some helper + * methods, such as `create()` and `end()`, are not field-based, and therefore + * will have no name. + * @param array $options The options and HTML attributes that will be used to generate the + * helper output. + * @return array Returns the value of the `$options` array, modified by the attribute generators + * added in the `'attributes'` key of the helper's configuration. Note that if a + * generator is present for a field whose value is `false`, that field will be removed + * from the array. + */ + protected function _generators($method, $name, $options) { + foreach ($this->_config['attributes'] as $key => $generator) { + if ($generator && !isset($options[$key])) { + if (($attr = $generator($method, $name, $options)) !== null) { + $options[$key] = $attr; + } + continue; + } + if ($generator && $options[$key] === false) { + unset($options[$key]); + } + } + return $options; + } } ?> \ No newline at end of file diff --git a/libraries/lithium/template/helper/Html.php b/libraries/lithium/template/helper/Html.php index 4a243fd..54aab8d 100644 --- a/libraries/lithium/template/helper/Html.php +++ b/libraries/lithium/template/helper/Html.php @@ -25,7 +25,7 @@ class Html extends \lithium\template\Helper { 'block' => '<div{:options}>{:content}</div>', 'block-end' => '</div>', 'block-start' => '<div{:options}>', - 'charset' => '<meta http-equiv="Content-Type" content="{:type}; charset={:charset}" />', + 'charset' => '<meta charset="{:encoding}" />', 'image' => '<img src="{:path}"{:options} />', 'js-block' => '<script type="text/javascript"{:options}>{:content}</script>', 'js-end' => '</script>', @@ -83,15 +83,35 @@ class Html extends \lithium\template\Helper { ); /** - * Returns a charset meta-tag. + * Returns a charset meta-tag for declaring the encoding of the document. * - * @param string $charset The character set to be used in the meta tag. Example: `"utf-8"`. - * @return string A meta tag containing the specified character set. + * The terms character set (here: charset) and character encoding (here: + * encoding) were historically synonymous. The terms now have related but + * distinct meanings. Whenever possible Lithium tries to use precise + * terminology. Since HTML uses the term `charset` we expose this method + * under the exact same name. This caters to the expectation towards a HTML + * helper. However the rest of the framework will use the term `encoding` + * when talking about character encoding. + * + * It is suggested that uppercase letters should be used when specifying + * the encoding. HTML specs don't require it to be uppercase and sites in + * the wild most often use the lowercase variant. On the other hand must + * XML parsers (those may not be relevant in this context anyway) not + * support lowercase encodings. This and the fact that IANA lists only + * encodings with uppercase characters led to the above suggestion. + * + * @see lithium\net\http\Response::$encoding + * @link http://www.iana.org/assignments/character-sets + * @param string $encoding The character encoding to be used in the meta tag. + * Defaults to the encoding of the `Response` object attached to the + * current context. The default encoding of that object is `UTF-8`. + * The string given here is not manipulated in any way, so that + * values are rendered literally. Also see above note about casing. + * @return string A meta tag containing the specified encoding (literally). */ - public function charset($charset = null) { - $options = array('type' => 'text/html'); - $options['charset'] = $charset ?: 'utf-8'; - return $this->_render(__METHOD__, 'charset', $options); + public function charset($encoding = null) { + $encoding = $encoding ?: $this->_context->response()->encoding; + return $this->_render(__METHOD__, 'charset', compact('encoding')); } /** @@ -223,6 +243,34 @@ class Html extends \lithium\template\Helper { } /** + * Creates a tag for the ```<head>``` section of your document. + * + * If there is a rendering context, then it also pushes the resulting tag to it. + * + * The ```$options``` must match the named parameters from ```$_strings``` for the + * given ```$tag```. + * + * @param string $tag the name of a key in ```$_strings``` + * @param array $options the options required by ```$_strings[$tag]``` + * @return mixed a string if successful, otherwise NULL + * @filter This method can be filtered. + */ + public function head($tag, array $options) { + if(!isset($this->_strings[$tag])) { + return NULL; + } + $method = __METHOD__; + $filter = function($self, $options, $chain) use ($method, $tag) { + return $self->invokeMethod('_render', array($method, $tag, $options)); + }; + $head = $this->_filter($method, $options, $filter); + if($this->_context) { + $this->_context->head($head); + } + return $head; + } + + /** * Creates a formatted <img /> element. * * @param string $path Path to the image file, relative to the app/webroot/img/ directory. diff --git a/libraries/lithium/template/view/Compiler.php b/libraries/lithium/template/view/Compiler.php index 5a608eb..9f29304 100644 --- a/libraries/lithium/template/view/Compiler.php +++ b/libraries/lithium/template/view/Compiler.php @@ -77,7 +77,7 @@ class Compiler extends \lithium\core\StaticObject { if ($options['fallback']) { return $file; } - throw new TemplateException("Could not write compiled template {$template} to cache"); + throw new TemplateException("Could not write compiled template `{$template}` to cache."); } /** diff --git a/libraries/lithium/template/view/Renderer.php b/libraries/lithium/template/view/Renderer.php index 31a732e..efc09f3 100644 --- a/libraries/lithium/template/view/Renderer.php +++ b/libraries/lithium/template/view/Renderer.php @@ -33,7 +33,7 @@ abstract class Renderer extends \lithium\core\Object { * @var array */ protected $_autoConfig = array( - 'request', 'context', 'strings', 'handlers', 'view', 'classes' => 'merge' + 'request', 'response', 'context', 'strings', 'handlers', 'view', 'classes' => 'merge' ); /** @@ -92,6 +92,13 @@ abstract class Renderer extends \lithium\core\Object { protected $_request = null; /** + * The `Response` object instance, if applicable. + * + * @var object The response object. + */ + protected $_response = null; + + /** * Automatically matches up template strings by name to output handlers. A handler can either * be a string, which represents a method name of the helper, or it can be a closure or callable * object. A handler takes 3 parameters: the value to be filtered, the name of the helper @@ -144,6 +151,7 @@ abstract class Renderer extends \lithium\core\Object { * - `handlers`: An array of output handlers for string template inputs. * - `request`: The `Request` object associated with this renderer and passed to the * defined handlers. + * - `response`: The `Response` object associated with this renderer. * - `context`: An array of the current rendering context data, including `content`, * `title`, `scripts`, `head` and `styles`. * @@ -156,6 +164,7 @@ abstract class Renderer extends \lithium\core\Object { 'strings' => array(), 'handlers' => array(), 'request' => null, + 'response' => null, 'context' => array( 'content' => '', 'title' => '', 'scripts' => array(), 'styles' => array(), 'head' => array() @@ -182,9 +191,13 @@ abstract class Renderer extends \lithium\core\Object { }, 'path' => function($path, $ref, array $options = array()) use (&$classes, &$request) { $defaults = array('base' => $request ? $request->env('base') : ''); - list($helper, $methodRef) = $ref; - list($class, $method) = explode('::', $methodRef); - $type = $helper->contentMap[$method]; + $type = 'generic'; + + if (is_array($ref) && $ref[0] && $ref[1]) { + list($helper, $methodRef) = $ref; + list($class, $method) = explode('::', $methodRef); + $type = $helper->contentMap[$method]; + } return $classes['media']::asset($path, $type, $options + $defaults); }, 'options' => '_attributes', @@ -281,7 +294,7 @@ abstract class Renderer extends \lithium\core\Object { if ($class = Libraries::locate('helper', ucfirst($name))) { return $this->_helpers[$name] = new $class($config + array('context' => $this)); } - throw new RuntimeException("Helper {$name} not found"); + throw new RuntimeException("Helper `{$name}` not found."); } /** @@ -312,7 +325,7 @@ abstract class Renderer extends \lithium\core\Object { * @return mixed A string or array, depending on whether `$property` is specified. */ public function context($property = null) { - if (!empty($property)) { + if ($property) { return isset($this->_context[$property]) ? $this->_context[$property] : null; } return $this->_context; @@ -394,6 +407,16 @@ abstract class Renderer extends \lithium\core\Object { } /** + * Returns the `Response` object associated with this rendering context. + * + * @return object Returns an instance of `lithium\action\Response`, which provides the i.e. + * the encoding for the document being the result of templates rendered by this context. + */ + public function response() { + return $this->_response; + } + + /** * Retuns the `View` object that controls this rendering context's instance. This can be used, * for example, to render view elements, i.e. `<?=$this->view()->render('element' $name); ?>`. * @@ -426,6 +449,28 @@ abstract class Renderer extends \lithium\core\Object { $this->_data = $data + $this->_data; $this->_vars = $data + $this->_vars; } + + /** + * Shortcut method used to render elements and other nested templates from inside the templating + * layer. + * + * @see lithium\template\View::$_processes + * @see lithium\template\View::render() + * @param string $type The type of template to render, usually either `'element'` or + * `'template'`. Indicates the process used to render the content. See + * `lithium\template\View::$_processes` for more info. + * @param string $template The template file name. For example, if `'header'` is passed, and + * `$type` is set to `'element'`, then the template rendered will be + * `views/elements/header.html.php` (assuming the default configuration). + * @param array $data An array of any other local variables that should be injected into the + * template. By default, only the values used to render the current template will + * be sent. If `$data` is non-empty, both sets of variables will be merged. + * @param array $options Any options accepted by `template\View::render()`. + * @return string Returns a the rendered template content as a string. + */ + protected function _render($type, $template, array $data = array(), array $options = array()) { + return $this->_view->render($type, $data + $this->_data, compact('template') + $options); + } } ?> \ No newline at end of file diff --git a/libraries/lithium/template/view/adapter/File.php b/libraries/lithium/template/view/adapter/File.php index b7d0bf3..30a426f 100644 --- a/libraries/lithium/template/view/adapter/File.php +++ b/libraries/lithium/template/view/adapter/File.php @@ -13,8 +13,9 @@ use lithium\core\Libraries; use lithium\template\TemplateException; /** - * The File adapter implements both template loading and rendering, and uses the `view\Stream` class - * to auto-escape template output with short tags (i.e. <?=). + * The File adapter implements both template loading and rendering, and uses the + * `lithium\template\view\Stream` class or `lithium\template\view\Compiler` class to auto-escape + * template output with short tags (i.e. `<?=`). * * For more information about implementing your own template loaders or renderers, see the * `lithium\template\View` class. @@ -31,7 +32,8 @@ class File extends \lithium\template\view\Renderer implements \ArrayAccess { * @var array */ protected $_autoConfig = array( - 'classes' => 'merge', 'request', 'context', 'strings', 'handlers', 'view', 'compile' + 'classes' => 'merge', 'request', 'response', 'context', + 'strings', 'handlers', 'view', 'compile', 'paths' ); /** @@ -60,6 +62,8 @@ class File extends \lithium\template\view\Renderer implements \ArrayAccess { */ protected $_vars = array(); + protected $_paths = array(); + /** * `File`'s dependencies. These classes are used by the output handlers to generate URLs * for dynamic resources and static assets, as well as compiling the templates. @@ -74,7 +78,9 @@ class File extends \lithium\template\view\Renderer implements \ArrayAccess { ); public function __construct(array $config = array()) { - $defaults = array('classes' => array(), 'compile' => true, 'extract' => true); + $defaults = array( + 'classes' => array(), 'compile' => true, 'extract' => true, 'paths' => array() + ); parent::__construct($config + $defaults); } @@ -110,18 +116,13 @@ class File extends \lithium\template\view\Renderer implements \ArrayAccess { * Returns a template file name * * @param string $type - * @param array $options + * @param array $params * @return string */ - public function template($type, $options) { - if (!isset($this->_config['paths'][$type])) { - return null; - } - $options = array_filter($options, function($item) { return is_string($item); }); - - $library = Libraries::get(isset($options['library']) ? $options['library'] : true); - $options['library'] = $library['path']; - $path = $this->_paths((array) $this->_config['paths'][$type], $options); + public function template($type, array $params) { + $library = Libraries::get(isset($params['library']) ? $params['library'] : true); + $params['library'] = $library['path']; + $path = $this->_paths($type, $params); if ($this->_compile) { $compiler = $this->_classes['compiler']; @@ -153,21 +154,26 @@ class File extends \lithium\template\view\Renderer implements \ArrayAccess { } /** - * Searches a series of path templates for a matching template file, and returns the file name. + * Searches one or more path templates for a matching template file, and returns the file name. * - * @param array $paths The array of path templates to search. - * @param array $options The set of options keys to be interpolated into the path templates + * @param string $type + * @param array $params The set of options keys to be interpolated into the path templates * when searching for the correct file to load. * @return string Returns the first template file found. Throws an exception if no templates * are available. */ - protected function _paths($paths, $options) { - foreach ($paths as $path) { - if (file_exists($path = String::insert($path, $options))) { - return $path; + protected function _paths($type, array $params) { + if (!isset($this->_paths[$type])) { + throw new TemplateException("Invalid template type '{$type}'."); + } + + foreach ((array) $this->_paths[$type] as $path) { + if (!file_exists($path = String::insert($path, $params))) { + continue; } + return $path; } - throw new TemplateException("Template not found at {$path}"); + throw new TemplateException("Template not found at path `{$path}`."); } } diff --git a/libraries/lithium/template/view/adapter/Simple.php b/libraries/lithium/template/view/adapter/Simple.php index ad9dbed..c5e58cc 100644 --- a/libraries/lithium/template/view/adapter/Simple.php +++ b/libraries/lithium/template/view/adapter/Simple.php @@ -8,8 +8,13 @@ namespace lithium\template\view\adapter; +<<<<<<< HEAD use \Closure; use \Exception; +======= +use Closure; +use Exception; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\util\Set; use lithium\util\String; @@ -50,7 +55,10 @@ class Simple extends \lithium\template\view\Renderer { * @return string */ public function template($type, $options) { - return isset($options[$type]) ? $options[$type] : ''; + if (isset($options[$type])) { + return $options[$type]; + } + return isset($options['template']) ? $options['template'] : ''; } protected function _toString($data) { diff --git a/libraries/lithium/test/Group.php b/libraries/lithium/test/Group.php index 47d2e8a..bb473b5 100644 --- a/libraries/lithium/test/Group.php +++ b/libraries/lithium/test/Group.php @@ -57,14 +57,11 @@ class Group extends \lithium\util\Collection { */ public static function all(array $options = array()) { $defaults = array( - 'library' => true, 'filter' => '/cases/', 'exclude' => '/mock/', 'recursive' => true, ); - $options += $defaults; - $classes = Libraries::locate('tests', null, $options); - return $classes; + return Libraries::locate('tests', null, $options + $defaults); } /** @@ -108,7 +105,11 @@ class Group extends \lithium\util\Collection { foreach ($this->_data as $test) { if (!class_exists($test)) { +<<<<<<< HEAD throw new Exception("Test case '{$test}' not found."); +======= + throw new Exception("Test case `{$test}` not found."); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } $tests[] = new $test; } diff --git a/libraries/lithium/test/Report.php b/libraries/lithium/test/Report.php index d08dbe3..7b809d5 100644 --- a/libraries/lithium/test/Report.php +++ b/libraries/lithium/test/Report.php @@ -241,7 +241,7 @@ class Report extends \lithium\core\Object { $results = array(); foreach ($filters as $filter => $options) { if (!$class = Libraries::locate('test.filter', $filter)) { - throw new ClassNotFoundException("{$class} is not a valid test filter."); + throw new ClassNotFoundException("`{$class}` is not a valid test filter."); } $options['name'] = strtolower(join('', array_slice(explode("\\", $class), -1))); $results[$class] = $options + array('apply' => array(), 'analyze' => array()); diff --git a/libraries/lithium/test/Unit.php b/libraries/lithium/test/Unit.php index eb7dab7..50185cc 100644 --- a/libraries/lithium/test/Unit.php +++ b/libraries/lithium/test/Unit.php @@ -8,14 +8,23 @@ namespace lithium\test; +<<<<<<< HEAD use \Exception; +======= +use Exception; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\util\String; use lithium\core\Libraries; use lithium\util\Validator; use lithium\analysis\Debugger; use lithium\analysis\Inspector; +<<<<<<< HEAD use \RecursiveDirectoryIterator; use \RecursiveIteratorIterator; +======= +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e /** * This is the base class for all test cases. Test are performed using an assertion method. If the @@ -121,7 +130,7 @@ class Unit extends \lithium\core\Object { * @param string $message Message to pass if the condition is met. * @return mixed */ - public function skipIf($condition, $message = 'Skipped test {:class}::{:function}()') { + public function skipIf($condition, $message = 'Skipped test `{:class}::{:function}()`.') { if (!$condition) { return; } @@ -212,7 +221,9 @@ class Unit extends \lithium\core\Object { $params['message'] = $this->_message($params); $message = String::insert($message, $params); } - $trace = Debugger::trace(array('start' => 1, 'format' => 'array')); + $trace = Debugger::trace(array( + 'start' => 1, 'depth' => 4, 'format' => 'array', 'closures' => !$expression + )); $methods = $this->methods(); $i = 1; @@ -230,7 +241,7 @@ class Unit extends \lithium\core\Object { 'method' => $trace[$i]['function'], 'assertion' => $trace[$i - 1]['function'], ); - $this->_result(($expression ? 'pass' : 'fail'), $result); + $this->_result($expression ? 'pass' : 'fail', $result); return $expression; } @@ -726,18 +737,23 @@ class Unit extends \lithium\core\Object { * @return array Data with the keys `trace'`, `'expected'` and `'result'`. */ protected function _compare($type, $expected, $result = null, $trace = null) { - $types = array('expected' => gettype($expected), 'result' => gettype($result)); + $compareTypes = function($expected, $result, $trace) { + $types = array('expected' => gettype($expected), 'result' => gettype($result)); - if ($types['expected'] !== $types['result']) { - return array('trace' => $trace, - 'expected' => trim("({$types['expected']}) " . print_r($expected, true)), - 'result' => trim("({$types['result']}) " . print_r($result, true)) - ); + if ($types['expected'] !== $types['result']) { + $expected = trim("({$types['expected']}) " . print_r($expected, true)); + $result = trim("({$types['result']}) " . print_r($result, true)); + return compact('trace', 'expected', 'result'); + } + }; + if ($types = $compareTypes($expected, $result, $trace)) { + return $types; } $data = array(); if (!is_scalar($expected)) { foreach ($expected as $key => $value) { + $newTrace = "{$trace}[{$key}]"; $isObject = false; if (is_object($expected)) { @@ -745,8 +761,13 @@ class Unit extends \lithium\core\Object { $expected = (array) $expected; $result = (array) $result; } - $check = array_key_exists($key, $result) ? $result[$key] : array(); - $newTrace = "{$trace}[{$key}]"; + if (!array_key_exists($key, $result)) { + $trace = (!$key) ? null : $newTrace; + $expected = (!$key) ? $expected : $value; + $result = ($key) ? null : $result; + return compact('trace', 'expected', 'result'); + } + $check = $result[$key]; if ($isObject) { $newTrace = ($trace) ? "{$trace}->{$key}" : $key; @@ -755,6 +776,9 @@ class Unit extends \lithium\core\Object { } if ($type === 'identical') { if ($value === $check) { + if ($types = $compareTypes($value, $check, $trace)) { + return $types; + } continue; } if ($check === array()) { @@ -769,6 +793,9 @@ class Unit extends \lithium\core\Object { } } else { if ($value == $check) { + if ($types = $compareTypes($value, $check, $trace)) { + return $types; + } continue; } if (!is_array($value)) { @@ -785,6 +812,7 @@ class Unit extends \lithium\core\Object { if (!empty($data)) { return $data; } +<<<<<<< HEAD } if (!is_scalar($result)) { @@ -797,15 +825,24 @@ class Unit extends \lithium\core\Object { 'result' => $data['expected'] ); } +======= +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } + if (!is_scalar($result)) { + $data = $this->_compare($type, $result, $expected); - if ($type === 'identical') { - if ($expected === $result) { - return true; + if (!empty($data)) { + return array( + 'trace' => $data['trace'], + 'expected' => $data['result'], + 'result' => $data['expected'] + ); } - return compact('trace', 'expected', 'result'); } - if ($expected == $result) { + if ((($type === 'identical') ? $expected === $result : $expected == $result)) { + if ($types = $compareTypes($expected, $result, $trace)) { + return $types; + } return true; } return compact('trace', 'expected', 'result'); diff --git a/libraries/lithium/test/filter/Affected.php b/libraries/lithium/test/filter/Affected.php index b368476..aea52a9 100644 --- a/libraries/lithium/test/filter/Affected.php +++ b/libraries/lithium/test/filter/Affected.php @@ -20,7 +20,6 @@ use lithium\analysis\Inspector; * 1. Looking at the subject of a test case. * 2. Searching the class tree for any classes that directly depend on that subject. * 3. Assigning test cases to those classes. - * */ class Affected extends \lithium\test\Filter { diff --git a/libraries/lithium/tests/cases/action/ControllerTest.php b/libraries/lithium/tests/cases/action/ControllerTest.php index c2e1d71..7eaa6ed 100644 --- a/libraries/lithium/tests/cases/action/ControllerTest.php +++ b/libraries/lithium/tests/cases/action/ControllerTest.php @@ -316,7 +316,7 @@ class ControllerTest extends \lithium\test\Unit { public function testNonExistentFunction() { $postsController = new MockPostsController(); - $this->expectException("Action 'foo' not found."); + $this->expectException("Action `foo` not found."); $postsController(new Request(), array('action' => 'foo')); } } diff --git a/libraries/lithium/tests/cases/action/DispatcherTest.php b/libraries/lithium/tests/cases/action/DispatcherTest.php index 500b512..081eda8 100644 --- a/libraries/lithium/tests/cases/action/DispatcherTest.php +++ b/libraries/lithium/tests/cases/action/DispatcherTest.php @@ -135,14 +135,14 @@ class DispatcherTest extends \lithium\test\Unit { public function testControllerLookupFail() { Dispatcher::config(array('classes' => array('router' => __CLASS__))); - $this->expectException("/Controller 'SomeNonExistentController' not found/"); + $this->expectException("/Controller `SomeNonExistentController` not found/"); Dispatcher::run(new Request(array('url' => '/'))); } public function testPluginControllerLookupFail() { Dispatcher::config(array('classes' => array('router' => __CLASS__))); - $this->expectException("/Controller 'some_invalid_plugin.Controller' not found/"); + $this->expectException("/Controller `some_invalid_plugin.Controller` not found/"); Dispatcher::run(new Request(array('url' => '/plugin'))); } diff --git a/libraries/lithium/tests/cases/action/ResponseTest.php b/libraries/lithium/tests/cases/action/ResponseTest.php index f3b093c..9bbe435 100644 --- a/libraries/lithium/tests/cases/action/ResponseTest.php +++ b/libraries/lithium/tests/cases/action/ResponseTest.php @@ -91,7 +91,11 @@ class ResponseTest extends \lithium\test\Unit { ); $this->assertEqual($headers, $this->response->testHeaders); +<<<<<<< HEAD $this->expectException('/^Request::disableCache\(\)/'); +======= + $this->expectException('/^`Request::disableCache\(\)`.+`Request::cache\(false\)`/'); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $this->response->disableCache(); } diff --git a/libraries/lithium/tests/cases/analysis/DocblockTest.php b/libraries/lithium/tests/cases/analysis/DocblockTest.php index a326df5..a1bdec3 100644 --- a/libraries/lithium/tests/cases/analysis/DocblockTest.php +++ b/libraries/lithium/tests/cases/analysis/DocblockTest.php @@ -108,7 +108,7 @@ class DocblockTest extends \lithium\test\Unit { /** * This docblock has an extra * in the closing element. * - **/ + */ public function testBadlyClosedDocblock() { $info = Inspector::info(__METHOD__ . '()'); $description = 'This docblock has an extra * in the closing element.'; diff --git a/libraries/lithium/tests/cases/analysis/InspectorTest.php b/libraries/lithium/tests/cases/analysis/InspectorTest.php index ba6586e..f2662a1 100644 --- a/libraries/lithium/tests/cases/analysis/InspectorTest.php +++ b/libraries/lithium/tests/cases/analysis/InspectorTest.php @@ -8,8 +8,13 @@ namespace lithium\tests\cases\analysis; +<<<<<<< HEAD use \ReflectionClass; use \ReflectionMethod; +======= +use ReflectionClass; +use ReflectionMethod; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\core\Libraries; use lithium\analysis\Inspector; diff --git a/libraries/lithium/tests/cases/analysis/LoggerTest.php b/libraries/lithium/tests/cases/analysis/LoggerTest.php index fb2f9b9..66b39c1 100644 --- a/libraries/lithium/tests/cases/analysis/LoggerTest.php +++ b/libraries/lithium/tests/cases/analysis/LoggerTest.php @@ -90,7 +90,7 @@ class LoggerTest extends \lithium\test\Unit { } public function testWriteWithInvalidPriority() { - $this->expectException("Attempted to write log message with invalid priority 'foo'."); + $this->expectException("Attempted to write log message with invalid priority `foo`."); Logger::foo("Test message"); } diff --git a/libraries/lithium/tests/cases/console/DispatcherTest.php b/libraries/lithium/tests/cases/console/DispatcherTest.php index 43eb3cc..fdf275d 100644 --- a/libraries/lithium/tests/cases/console/DispatcherTest.php +++ b/libraries/lithium/tests/cases/console/DispatcherTest.php @@ -57,10 +57,14 @@ class DispatcherTest extends \lithium\test\Unit { public function testRunWithPassed() { $response = Dispatcher::run(new Request(array( +<<<<<<< HEAD 'args' => array( 'lithium\tests\mocks\console\MockDispatcherCommand', 'with param' ) +======= + 'args' => array('lithium\tests\mocks\console\MockDispatcherCommand', 'with param') +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e ))); $expected = 'run'; @@ -74,10 +78,14 @@ class DispatcherTest extends \lithium\test\Unit { public function testRunWithAction() { $response = Dispatcher::run(new Request(array( +<<<<<<< HEAD 'args' => array( 'lithium\tests\mocks\console\MockDispatcherCommand', 'testAction' ) +======= + 'args' => array('lithium\tests\mocks\console\MockDispatcherCommand', 'testAction') +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e ))); $expected = 'testAction'; $result = $response->testAction; @@ -85,7 +93,7 @@ class DispatcherTest extends \lithium\test\Unit { } public function testInvalidCommand() { - $expected = (object) array('status' => "Command `\\this\\command\\is\\fake` not found\n"); + $expected = (object) array('status' => "Command `\\this\\command\\is\\fake` not found.\n"); $result = Dispatcher::run(new Request(array( 'args' => array( '\this\command\is\fake', @@ -97,7 +105,7 @@ class DispatcherTest extends \lithium\test\Unit { } public function testRunWithCamelizingCommand() { - $expected = (object) array('status' => "Command `FooBar` not found\n"); + $expected = (object) array('status' => "Command `FooBar` not found.\n"); $result = Dispatcher::run(new Request(array( 'args' => array( 'foo-bar', @@ -105,7 +113,7 @@ class DispatcherTest extends \lithium\test\Unit { ))); $this->assertEqual($expected, $result); - $expected = (object) array('status' => "Command `FooBar` not found\n"); + $expected = (object) array('status' => "Command `FooBar` not found.\n"); $result = Dispatcher::run(new Request(array( 'args' => array('foo_bar') ))); diff --git a/libraries/lithium/tests/cases/console/command/HelpTest.php b/libraries/lithium/tests/cases/console/command/HelpTest.php index e8023b5..b76c6a2 100644 --- a/libraries/lithium/tests/cases/console/command/HelpTest.php +++ b/libraries/lithium/tests/cases/console/command/HelpTest.php @@ -12,8 +12,6 @@ class HelpTest extends \lithium\test\Unit { protected $_backup = array(); - protected $_testPath = null; - public function setUp() { $this->classes = array('response' => 'lithium\tests\mocks\console\MockResponse'); $this->_backup['cwd'] = getcwd(); @@ -30,116 +28,116 @@ class HelpTest extends \lithium\test\Unit { } public function testRun() { - $help = new Help(array( + $command = new Help(array( 'request' => $this->request, 'classes' => $this->classes )); $expected = true; - $result = $help->run(); + $result = $command->run(); $this->assertEqual($expected, $result); $expected = "COMMANDS\n"; $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $this->assertPattern("/{$expected}/", $result); $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $pattern = "/\s+test\s+Runs a given set of tests and outputs the results\./ms"; $this->assertPattern($pattern, $result); } public function testRunWithName() { - $help = new Help(array( + $command = new Help(array( 'request' => $this->request, 'classes' => $this->classes )); $expected = true; - $result = $help->run('Test'); + $result = $command->run('Test'); $this->assertEqual($expected, $result); $expected = "li3 test --case=CASE --group=GROUP --filters=FILTERS [ARGS]"; $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $this->assertPattern("/{$expected}/", $result); $expected = "OPTIONS\n --case=CASE\n"; $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $this->assertPattern("/{$expected}/", $result); $expected = "missing\n"; $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $this->assertPattern("/{$expected}/", $result); } public function testApiClass() { - $help = new Help(array( + $command = new Help(array( 'request' => $this->request, 'classes' => $this->classes )); $expected = null; - $result = $help->api('lithium.util.Inflector'); + $result = $command->api('lithium.util.Inflector'); $this->assertEqual($expected, $result); $expected = "Utility for modifying format of words"; $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $this->assertPattern("/{$expected}/", $result); } public function testApiMethod() { - $help = new Help(array( + $command = new Help(array( 'request' => $this->request, 'classes' => $this->classes )); $expected = null; - $result = $help->api('lithium.util.Inflector', 'method'); + $result = $command->api('lithium.util.Inflector', 'method'); $this->assertEqual($expected, $result); $expected = "rules [type] [config]"; $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $this->assertPattern("/{$expected}/", $result); } public function testApiMethodWithName() { - $help = new Help(array( + $command = new Help(array( 'request' => $this->request, 'classes' => $this->classes )); $expected = null; - $result = $help->api('lithium.util.Inflector', 'method', 'rules'); + $result = $command->api('lithium.util.Inflector', 'method', 'rules'); $this->assertEqual($expected, $result); $expected = "rules [type] [config]"; $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $this->assertPattern("/{$expected}/", $result); } public function testApiProperty() { - $help = new Help(array( + $command = new Help(array( 'request' => $this->request, 'classes' => $this->classes )); $expected = null; - $result = $help->api('lithium.net.Message', 'property'); + $result = $command->api('lithium.net.Message', 'property'); $this->assertEqual($expected, $result); $expected = " --host=HOST\n The hostname for this endpoint."; $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $this->assertPattern("/{$expected}/", $result); } public function testApiPropertyWithName() { - $help = new Help(array( + $command = new Help(array( 'request' => $this->request, 'classes' => $this->classes )); $expected = null; - $result = $help->api('lithium.net.Message', 'property'); + $result = $command->api('lithium.net.Message', 'property'); $this->assertEqual($expected, $result); $expected = " --host=HOST\n The hostname for this endpoint."; $expected = preg_quote($expected); - $result = $help->response->output; + $result = $command->response->output; $this->assertPattern("/{$expected}/", $result); } } diff --git a/libraries/lithium/tests/cases/console/command/LibraryTest.php b/libraries/lithium/tests/cases/console/command/LibraryTest.php index f6c6a56..bbdf2c5 100644 --- a/libraries/lithium/tests/cases/console/command/LibraryTest.php +++ b/libraries/lithium/tests/cases/console/command/LibraryTest.php @@ -63,9 +63,7 @@ class LibraryTest extends \lithium\test\Unit { $result = $this->library->config('server', 'lab.lithify.me'); $this->assertTrue($result); - $expected = array('servers' => array( - 'lab.lithify.me' => true - )); + $expected = array('servers' => array('lab.lithify.me' => true)); $result = json_decode(file_get_contents($this->testConf), true); $this->assertEqual($expected, $result); diff --git a/libraries/lithium/tests/cases/console/command/create/ControllerTest.php b/libraries/lithium/tests/cases/console/command/create/ControllerTest.php index 5d5d286..b445e89 100644 --- a/libraries/lithium/tests/cases/console/command/create/ControllerTest.php +++ b/libraries/lithium/tests/cases/console/command/create/ControllerTest.php @@ -63,7 +63,7 @@ class ControllerTest extends \lithium\test\Unit { 'request' => $this->request, 'classes' => $this->classes )); - $expected = '\\create_test\\models\\Post'; + $expected = 'create_test\\models\\Post'; $result = $model->invokeMethod('_use', array($this->request)); $this->assertEqual($expected, $result); } @@ -87,7 +87,7 @@ class ControllerTest extends \lithium\test\Unit { namespace create_test\controllers; -use \create_test\models\Post; +use create_test\models\Post; class PostsController extends \lithium\action\Controller { diff --git a/libraries/lithium/tests/cases/console/command/create/TestTest.php b/libraries/lithium/tests/cases/console/command/create/TestTest.php index 9dd5ba3..04add52 100644 --- a/libraries/lithium/tests/cases/console/command/create/TestTest.php +++ b/libraries/lithium/tests/cases/console/command/create/TestTest.php @@ -63,7 +63,7 @@ class TestTest extends \lithium\test\Unit { namespace create_test\tests\cases\models; -use \create_test\models\Post; +use create_test\models\Post; class PostTest extends \lithium\test\Unit { @@ -86,36 +86,44 @@ test; public function testTestModelWithMethods() { $this->_cleanUp(); mkdir($this->_testPath . '/create_test/models/', 0755, true); - file_put_contents($this->_testPath . '/create_test/models/Post.php', + $id = rand(); + $path = "create_test/models/Post{$id}.php"; + file_put_contents("{$this->_testPath}/{$path}", "<?php namespace create_test\models; -class Post { +class Post{$id} { public function someMethod() {} }" ); +<<<<<<< HEAD $this->request->params += array( 'command' => 'create', 'action' => 'test', 'args' => array('model', 'Post') ); $test = new Test(array( 'request' => $this->request, 'classes' => $this->classes +======= + $this->request->params += array('command' => 'create', 'action' => 'test', 'args' => array( + 'model', "Post{$id}" +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e )); + $test = new Test(array('request' => $this->request, 'classes' => $this->classes)); $test->path = $this->_testPath; $test->run('test'); - $expected = "PostTest created in create_test\\tests\\cases\\models.\n"; + $expected = "Post{$id}Test created in create_test\\tests\\cases\\models.\n"; $result = $test->response->output; $this->assertEqual($expected, $result); - $expected = <<<'test' + $expected = <<<test -namespace create_test\tests\cases\models; +namespace create_test\\tests\\cases\\models; -use \create_test\models\Post; +use create_test\\models\\Post{$id}; -class PostTest extends \lithium\test\Unit { +class Post{$id}Test extends \\lithium\\test\\Unit { public function setUp() {} @@ -127,9 +135,8 @@ class PostTest extends \lithium\test\Unit { test; $replace = array("<?php", "?>"); - $result = str_replace($replace, '', - file_get_contents($this->_testPath . '/create_test/tests/cases/models/PostTest.php') - ); + $path = "create_test/tests/cases/models/Post{$id}Test.php"; + $result = str_replace($replace, '', file_get_contents("{$this->_testPath}/{$path}")); $this->assertEqual($expected, $result); } } diff --git a/libraries/lithium/tests/cases/core/AdaptableTest.php b/libraries/lithium/tests/cases/core/AdaptableTest.php index f2bf019..a11cbc4 100644 --- a/libraries/lithium/tests/cases/core/AdaptableTest.php +++ b/libraries/lithium/tests/cases/core/AdaptableTest.php @@ -8,12 +8,21 @@ namespace lithium\tests\cases\core; +<<<<<<< HEAD +======= +use SplDoublyLinkedList; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\util\Collection; use lithium\core\Adaptable; use lithium\storage\cache\adapter\Memory; use lithium\tests\mocks\core\MockAdapter; use lithium\tests\mocks\core\MockStrategy; +<<<<<<< HEAD use \SplDoublyLinkedList; +======= +use lithium\tests\mocks\storage\cache\strategy\MockSerializer; +use lithium\tests\mocks\storage\cache\strategy\MockConfigurizer; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e class AdaptableTest extends \lithium\test\Unit { @@ -25,7 +34,7 @@ class AdaptableTest extends \lithium\test\Unit { $this->assertFalse($this->adaptable->config()); $items = array(array( - 'adapter' => '\some\adapter', + 'adapter' => 'some\adapter', 'filters' => array('filter1', 'filter2') )); $result = $this->adaptable->config($items); @@ -36,7 +45,7 @@ class AdaptableTest extends \lithium\test\Unit { $this->assertEqual($expected, $result); $items = array(array( - 'adapter' => '\some\adapter', + 'adapter' => 'some\adapter', 'filters' => array('filter1', 'filter2') )); $this->adaptable->config($items); @@ -62,17 +71,13 @@ class AdaptableTest extends \lithium\test\Unit { public function testNonExistentConfig() { $adapter = new MockAdapter(); - $this->expectException("Configuration 'non_existent_config' has not been defined."); - $result = $adapter::adapter('non_existent_config'); - $this->assertNull($result); + $this->expectException("Configuration `non_existent_config` has not been defined."); + $adapter::adapter('non_existent_config'); } public function testAdapter() { $adapter = new MockAdapter(); - $items = array('default' => array( - 'adapter' => 'Memory', - 'filters' => array() - )); + $items = array('default' => array('adapter' => 'Memory', 'filters' => array())); $adapter::config($items); $result = $adapter::config(); $expected = $items; @@ -85,10 +90,7 @@ class AdaptableTest extends \lithium\test\Unit { public function testConfigAndAdapter() { $adapter = new MockAdapter(); - $items = array('default' => array( - 'adapter' => 'Memory', - 'filters' => array() - )); + $items = array('default' => array('adapter' => 'Memory', 'filters' => array())); $adapter::config($items); $config = $adapter::config(); @@ -108,7 +110,7 @@ class AdaptableTest extends \lithium\test\Unit { public function testStrategy() { $strategy = new MockStrategy(); $items = array('default' => array( - 'strategies' => array('\lithium\tests\mocks\storage\cache\strategy\MockSerializer'), + 'strategies' => array('lithium\tests\mocks\storage\cache\strategy\MockSerializer'), 'filters' => array(), 'adapter' => null )); @@ -120,9 +122,7 @@ class AdaptableTest extends \lithium\test\Unit { $result = $strategy::strategies('default'); $this->assertTrue($result instanceof SplDoublyLinkedList); $this->assertEqual(count($result), 1); - $this->assertTrue( - $result->top() instanceof \lithium\tests\mocks\storage\cache\strategy\MockSerializer - ); + $this->assertTrue($result->top() instanceof MockSerializer); } public function testInvalidStrategy() { @@ -135,7 +135,7 @@ class AdaptableTest extends \lithium\test\Unit { $strategy::config($items); $class = 'lithium\tests\mocks\core\MockStrategy'; - $message = "Could not find strategy 'InvalidStrategy' in class {$class}."; + $message = "Could not find strategy `InvalidStrategy` in class `{$class}`."; $this->expectException($message); $result = $strategy::strategies('default'); @@ -146,7 +146,7 @@ class AdaptableTest extends \lithium\test\Unit { $strategy = new MockStrategy(); $items = array('default' => array( 'strategies' => array( - '\lithium\tests\mocks\storage\cache\strategy\MockConfigurizer' => array( + 'lithium\tests\mocks\storage\cache\strategy\MockConfigurizer' => array( 'key1' => 'value1', 'key2' => 'value2' ) ), @@ -161,21 +161,19 @@ class AdaptableTest extends \lithium\test\Unit { $result = $strategy::strategies('default'); $this->assertTrue($result instanceof SplDoublyLinkedList); $this->assertEqual(count($result), 1); - $this->assertTrue( - $result->top() instanceof \lithium\tests\mocks\storage\cache\strategy\MockConfigurizer - ); + $this->assertTrue($result->top() instanceof MockConfigurizer); } public function testNonExistentStrategyConfiguration() { $strategy = new MockStrategy(); - $this->expectException("Configuration 'non_existent_config' has not been defined."); + $this->expectException("Configuration `non_existent_config` has not been defined."); $result = $strategy::strategies('non_existent_config'); $this->assertNull($result); } public function testApplyStrategiesNonExistentConfiguration() { $strategy = new MockStrategy(); - $this->expectException("Configuration 'non_existent_config' has not been defined."); + $this->expectException("Configuration `non_existent_config` has not been defined."); $strategy::applyStrategies('method', 'non_existent_config', null); } @@ -184,7 +182,7 @@ class AdaptableTest extends \lithium\test\Unit { $items = array('default' => array( 'filters' => array(), 'adapter' => null, - 'strategies' => array('\lithium\tests\mocks\storage\cache\strategy\MockSerializer'), + 'strategies' => array('lithium\tests\mocks\storage\cache\strategy\MockSerializer'), )); $strategy::config($items); $result = $strategy::config(); @@ -203,7 +201,7 @@ class AdaptableTest extends \lithium\test\Unit { 'filters' => array(), 'adapter' => null, 'strategies' => array( - '\lithium\tests\mocks\storage\cache\strategy\MockConfigurizer' => $params + 'lithium\tests\mocks\storage\cache\strategy\MockConfigurizer' => $params ) )); $strategy::config($items); @@ -221,8 +219,7 @@ class AdaptableTest extends \lithium\test\Unit { 'filters' => array(), 'adapter' => null, 'strategies' => array( - '\lithium\tests\mocks\storage\cache\strategy\MockSerializer', - 'Base64' + 'lithium\tests\mocks\storage\cache\strategy\MockSerializer', 'Base64' ) )); $strategy::config($items); @@ -274,10 +271,7 @@ class AdaptableTest extends \lithium\test\Unit { public function testEnabled() { $adapter = new MockAdapter(); - $items = array('default' => array( - 'adapter' => 'Memory', - 'filters' => array() - )); + $items = array('default' => array('adapter' => 'Memory', 'filters' => array())); $adapter::config($items); $result = $adapter::config(); $expected = $items; @@ -294,16 +288,14 @@ class AdaptableTest extends \lithium\test\Unit { public function testNonExistentAdapter() { $adapter = new MockAdapter(); - $items = array('default' => array( - 'adapter' => 'NonExistent', 'filters' => array() - )); + $items = array('default' => array('adapter' => 'NonExistent', 'filters' => array())); $adapter::config($items); $result = $adapter::config(); $expected = $items; $this->assertEqual($expected, $result); - $message = 'Could not find adapter \'NonExistent\' in '; - $message .= 'class lithium\tests\mocks\core\MockAdapter.'; + $message = 'Could not find adapter `NonExistent` in '; + $message .= 'class `lithium\tests\mocks\core\MockAdapter`.'; $this->expectException($message); $result = $adapter::adapter('default'); @@ -336,7 +328,7 @@ class AdaptableTest extends \lithium\test\Unit { $adapter::config($items); $message = 'No adapter set for configuration in '; - $message .= 'class lithium\tests\mocks\core\MockAdapter.'; + $message .= 'class `lithium\tests\mocks\core\MockAdapter`.'; $this->expectException($message); $result = $adapter::adapter('default'); } diff --git a/libraries/lithium/tests/cases/core/ErrorHandlerTest.php b/libraries/lithium/tests/cases/core/ErrorHandlerTest.php index b8df0c9..ab756db 100644 --- a/libraries/lithium/tests/cases/core/ErrorHandlerTest.php +++ b/libraries/lithium/tests/cases/core/ErrorHandlerTest.php @@ -8,9 +8,15 @@ namespace lithium\tests\cases\core; +<<<<<<< HEAD use \Closure; use \Exception; use \UnexpectedValueException; +======= +use Closure; +use Exception; +use UnexpectedValueException; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\core\ErrorHandler; class ErrorHandlerTest extends \lithium\test\Unit { @@ -69,6 +75,8 @@ class ErrorHandlerTest extends \lithium\test\Unit { } public function testErrorCatching() { + $this->skipIf(true, 'Refactoring original error-handling iteration.'); + $self = $this; ErrorHandler::config(array(array( 'code' => E_WARNING | E_USER_WARNING, diff --git a/libraries/lithium/tests/cases/core/LibrariesTest.php b/libraries/lithium/tests/cases/core/LibrariesTest.php index 5edafa1..7123255 100644 --- a/libraries/lithium/tests/cases/core/LibrariesTest.php +++ b/libraries/lithium/tests/cases/core/LibrariesTest.php @@ -8,7 +8,12 @@ namespace lithium\tests\cases\core; +<<<<<<< HEAD use \SplFileInfo; +======= +use stdClass; +use SplFileInfo; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\util\Inflector; use lithium\core\Libraries; @@ -23,6 +28,11 @@ class LibrariesTest extends \lithium\test\Unit { $this->assertTrue(strpos($result, '/lithium/core/Libraries.php')); $this->assertTrue(file_exists($result)); $this->assertFalse(strpos($result, '\\')); + + $result = Libraries::path('lithium\core\Libraries'); + $this->assertTrue(strpos($result, '/lithium/core/Libraries.php')); + $this->assertTrue(file_exists($result)); + $this->assertFalse(strpos($result, '\\')); } public function testPathTemplate() { @@ -138,7 +148,7 @@ class LibrariesTest extends \lithium\test\Unit { * @return void */ public function testAddInvalidLibrary() { - $this->expectException("Library 'invalid_foo' not found."); + $this->expectException("Library `invalid_foo` not found."); Libraries::add('invalid_foo'); } @@ -215,7 +225,7 @@ class LibrariesTest extends \lithium\test\Unit { * @return void */ public function testLibraryLoad() { - $this->expectException('Failed to load SomeInvalidLibrary from '); + $this->expectException('Failed to load class `SomeInvalidLibrary` from path ``.'); Libraries::load('SomeInvalidLibrary', true); } @@ -325,7 +335,11 @@ class LibrariesTest extends \lithium\test\Unit { public function testServiceLocateInstantiation() { $result = Libraries::instance('adapter.template.view', 'Simple'); $this->assertTrue(is_a($result, 'lithium\template\view\adapter\Simple')); +<<<<<<< HEAD $this->expectException("Class 'Foo' of type 'adapter.template.view' not found."); +======= + $this->expectException("Class `Foo` of type `adapter.template.view` not found."); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $result = Libraries::instance('adapter.template.view', 'Foo'); } @@ -368,7 +382,7 @@ class LibrariesTest extends \lithium\test\Unit { $expected = '\lithium\data\source\Database'; $this->assertEqual($expected, $result); - $expected = new \stdClass(); + $expected = new stdClass(); $result = Libraries::locate(null, $expected); $this->assertEqual($expected, $result); } @@ -549,6 +563,23 @@ class LibrariesTest extends \lithium\test\Unit { $this->_cleanUp(); } +<<<<<<< HEAD +======= + + /** + * Tests that `Libraries::realPath()` correctly resolves paths to files inside Phar archives. + * + * @return void + */ + public function testPathsInPharArchives() { + $config = Libraries::get('lithium'); + $path = "{$config['path']}/console/command/create/template/app.phar.gz"; + + $expected = "phar://{$path}/controllers/HelloWorldController.php"; + $result = Libraries::realPath($expected); + $this->assertEqual($expected, $result); + } +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } ?> \ No newline at end of file diff --git a/libraries/lithium/tests/cases/data/EntityTest.php b/libraries/lithium/tests/cases/data/EntityTest.php index 6c1f597..407e405 100644 --- a/libraries/lithium/tests/cases/data/EntityTest.php +++ b/libraries/lithium/tests/cases/data/EntityTest.php @@ -60,7 +60,11 @@ class EntityTest extends \lithium\test\Unit { public function testMethodDispatch() { $entity = new Entity(array('model' => $this->_model, 'data' => array('foo' => true))); $this->assertTrue($entity->validates()); +<<<<<<< HEAD $this->expectException("/^No model bound or unhandled method call 'foo'.$/"); +======= + $this->expectException("/^No model bound or unhandled method call `foo`.$/"); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $entity->foo(); } diff --git a/libraries/lithium/tests/cases/data/ModelTest.php b/libraries/lithium/tests/cases/data/ModelTest.php index acb5280..37aae72 100644 --- a/libraries/lithium/tests/cases/data/ModelTest.php +++ b/libraries/lithium/tests/cases/data/ModelTest.php @@ -9,20 +9,42 @@ namespace lithium\tests\cases\data; use lithium\data\Model; +<<<<<<< HEAD use lithium\data\model\Query; use lithium\data\Connections; use lithium\analysis\Inspector; use lithium\tests\mocks\data\MockTag; use lithium\tests\mocks\data\MockPost; +======= +use lithium\data\Entity; +use lithium\data\model\Query; +use lithium\data\Connections; +use lithium\analysis\Inspector; +use lithium\data\entity\Record; +use lithium\tests\mocks\data\MockTag; +use lithium\tests\mocks\data\MockPost; +use lithium\tests\mocks\data\MockSource; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\tests\mocks\data\MockComment; use lithium\tests\mocks\data\MockTagging; use lithium\tests\mocks\data\MockCreator; use lithium\tests\mocks\data\MockPostForValidates; +<<<<<<< HEAD +======= +use lithium\tests\mocks\data\source\MockMongoConnection; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e class ModelTest extends \lithium\test\Unit { protected $_configs = array(); + protected $_altSchema = array( + 'id' => array('type' => 'integer'), + 'author_id' => array('type' => 'integer'), + 'title' => array('type' => 'string'), + 'body' => array('type' => 'text') + ); + public function setUp() { $this->_configs = Connections::config(); Connections::config(array('mock-source' => array( @@ -218,7 +240,11 @@ class ModelTest extends \lithium\test\Unit { $this->assertEqual(array('foo' => 13), $result['query']->conditions()); $this->assertEqual(array('created_at' => 'desc'), $result['query']->order()); +<<<<<<< HEAD $this->expectException('/Method findFoo not defined or handled in class/'); +======= + $this->expectException('/Method `findFoo` not defined or handled in class/'); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e MockPost::findFoo(); } @@ -229,7 +255,7 @@ class ModelTest extends \lithium\test\Unit { */ public function testSimpleFindFirst() { $result = MockComment::first(); - $this->assertTrue($result instanceof \lithium\data\entity\Record); + $this->assertTrue($result instanceof Record); $expected = 'First comment'; $this->assertEqual($expected, $result->text); @@ -397,25 +423,23 @@ class ModelTest extends \lithium\test\Unit { $this->assertEqual('mock-source', MockPost::meta('connection')); MockPost::config(array('connection' => false)); $this->assertFalse(MockPost::meta('connection')); + $schema = MockPost::schema(); - $schema = array( - 'id' => array('type' => 'integer'), - 'author_id' => array('type' => 'integer'), - 'title' => array('type' => 'string'), - 'body' => array('type' => 'text') - ); - MockPost::overrideSchema($schema); - $this->assertEqual($schema, MockPost::schema()); + MockPost::overrideSchema($this->_altSchema); + $this->assertEqual($this->_altSchema, MockPost::schema()); $post = MockPost::create(array('title' => 'New post')); - $this->assertTrue($post instanceof \lithium\data\Entity); + $this->assertTrue($post instanceof Entity); $this->assertEqual('New post', $post->title); + MockPost::overrideSchema($schema); $this->expectException('/Connection name not defined/'); $post->save(); } public function testSave() { + $schema = MockPost::schema(); + MockPost::overrideSchema($this->_altSchema); $data = array('title' => 'New post', 'author_id' => 13); $record = MockPost::create($data); $result = $record->save(); @@ -423,6 +447,7 @@ class ModelTest extends \lithium\test\Unit { $this->assertEqual('create', $result['query']->type()); $this->assertEqual($data, $result['query']->data()); $this->assertEqual('lithium\tests\mocks\data\MockPost', $result['query']->model()); + MockPost::overrideSchema($schema); } public function testSaveWithFailedValidation() { @@ -502,6 +527,20 @@ class ModelTest extends \lithium\test\Unit { $result = MockPost::count(array('conditions' => array('email' => 'foo@example.com'))); $this->assertEqual($query, $result['query']); } + + public function testSettingNestedObjectDefaults() { + MockPost::$connection = new MockMongoConnection(); + $schema = MockPost::schema(); + + MockPost::overrideSchema($schema + array('nested.value' => array( + 'type' => 'string', + 'default' => 'foo' + ))); + $this->assertEqual('foo', MockPost::create()->nested->value); + + MockPost::overrideSchema($schema); + MockPost::$connection = null; + } } ?> \ No newline at end of file diff --git a/libraries/lithium/tests/cases/data/collection/DocumentArrayTest.php b/libraries/lithium/tests/cases/data/collection/DocumentArrayTest.php index a0a48c0..4241bdb 100644 --- a/libraries/lithium/tests/cases/data/collection/DocumentArrayTest.php +++ b/libraries/lithium/tests/cases/data/collection/DocumentArrayTest.php @@ -33,8 +33,11 @@ class DocumentArrayTest extends \lithium\test\Unit { 'data' => array('5', '6', '7') )); $array[] = 8; +<<<<<<< HEAD // var_dump($array); // var_dump($array->export()); +======= +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } } diff --git a/libraries/lithium/tests/cases/data/entity/DocumentTest.php b/libraries/lithium/tests/cases/data/entity/DocumentTest.php index caf8e2e..2bbd31e 100644 --- a/libraries/lithium/tests/cases/data/entity/DocumentTest.php +++ b/libraries/lithium/tests/cases/data/entity/DocumentTest.php @@ -349,7 +349,7 @@ class DocumentTest extends \lithium\test\Unit { public function testInvalidCall() { $doc = new Document(); - $this->expectException("No model bound or unhandled method call 'medicin'."); + $this->expectException("No model bound or unhandled method call `medicin`."); $result = $doc->medicin(); $this->assertNull($result); } @@ -560,6 +560,7 @@ class DocumentTest extends \lithium\test\Unit { $doc->nested = array('more' => 'data'); $newData = $doc->export(); +<<<<<<< HEAD $expected = array('foo' => 'bar', 'baz' => 'dib', 'nested.more' => 'data'); $this->assertFalse($newData['exists']); @@ -573,6 +574,21 @@ class DocumentTest extends \lithium\test\Unit { $this->assertFalse($result['update']); $this->assertEqual('nested', $result['key']); +======= + + $expected = array('foo' => 'bar', 'baz' => 'dib', 'nested.more' => 'data'); + $this->assertFalse($newData['exists']); + $this->assertEqual(array('foo' => 'bar', 'baz' => 'dib'), $newData['data']); + $this->assertEqual(1, count($newData['update'])); + $this->assertTrue($newData['update']['nested'] instanceof Document); + + $result = $newData['update']['nested']->export(); + $this->assertFalse($result['exists']); + $this->assertEqual(array('more' => 'data'), $result['data']); + $this->assertFalse($result['update']); + $this->assertEqual('nested', $result['key']); + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $doc = new Document(compact('model') + array('exists' => true, 'data' => array( 'foo' => 'bar', 'baz' => 'dib' ))); @@ -601,6 +617,10 @@ class DocumentTest extends \lithium\test\Unit { $doc->more = 'cowbell'; $doc->nested->evenMore = 'cowbell'; $modified = $doc->export(); +<<<<<<< HEAD +======= + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $expected = array('more' => 'cowbell'); $this->assertEqual($expected, $modified['update']); $this->assertEqual(array('nested', 'foo', 'baz'), array_keys($modified['data'])); @@ -630,6 +650,23 @@ class DocumentTest extends \lithium\test\Unit { $result = $doc->data(); $this->assertPattern('/^[a-f0-9]{24}$/', $result['id']); $this->assertEqual(time(), $result['date']); +<<<<<<< HEAD +======= + } + + public function testInitializationWithNestedFields() { + $doc = new Document(array('model' => $this->_model, 'data' => array( + 'simple' => 'value', + 'nested.foo' => 'first', + 'nested.bar' => 'second', + 'really.nested.key' => 'value' + ))); + $this->assertEqual('value', $doc->simple); + $this->assertEqual('first', $doc->nested->foo); + $this->assertEqual('second', $doc->nested->bar); + $this->assertEqual('value', $doc->really->nested->key); + $this->assertEqual(array('simple', 'nested', 'really'), array_keys($doc->data())); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } } diff --git a/libraries/lithium/tests/cases/data/entity/RecordTest.php b/libraries/lithium/tests/cases/data/entity/RecordTest.php index 055b9c8..da5569c 100644 --- a/libraries/lithium/tests/cases/data/entity/RecordTest.php +++ b/libraries/lithium/tests/cases/data/entity/RecordTest.php @@ -130,7 +130,7 @@ class RecordTest extends \lithium\test\Unit { $this->assertEqual('create', $result['query']->type()); $this->assertEqual(array('title' => 'foo'), $result['query']->data()); - $this->expectException("No model bound or unhandled method call 'invalid'."); + $this->expectException("No model bound or unhandled method call `invalid`."); $this->assertNull($this->record->invalid()); } } diff --git a/libraries/lithium/tests/cases/data/model/QueryTest.php b/libraries/lithium/tests/cases/data/model/QueryTest.php index 0c7c5ae..55da84f 100644 --- a/libraries/lithium/tests/cases/data/model/QueryTest.php +++ b/libraries/lithium/tests/cases/data/model/QueryTest.php @@ -244,6 +244,7 @@ class QueryTest extends \lithium\test\Unit { } public function testExport() { + MockQueryPost::meta('source', 'foo'); $query = new Query($this->_queryArr); $ds = new MockDatabase(); $export = $query->export($ds); @@ -281,9 +282,12 @@ class QueryTest extends \lithium\test\Unit { $result = $export['fields']; $this->assertEqual($expected, $result); - $expected = MockQueryPost::meta('source'); $result = $export['source']; +<<<<<<< HEAD $this->assertEqual("{{$expected}}", $result); +======= + $this->assertEqual("{foo}", $result); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } public function testRestrictedKeyExport() { @@ -321,15 +325,34 @@ class QueryTest extends \lithium\test\Unit { $query = new Query(array('joins' => array(array('foo' => 'bar')))); $query->join(array('bar' => 'baz')); $expected = array(array('foo' => 'bar'), array('bar' => 'baz')); +<<<<<<< HEAD $this->assertEqual($expected, $query->join()); $query->join('zim', array('dib' => 'gir')); +======= + $joins = $query->join(); + + $this->assertEqual('bar', $joins[0]->foo()); + $this->assertNull($joins[0]->bar()); + + $this->assertEqual('baz', $joins[1]->bar()); + $this->assertNull($joins[1]->foo()); + + $query->join('zim', array('dib' => 'gir')); + $this->assertEqual(3, count($query->join())); + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $expected = array( array('foo' => 'bar'), array('bar' => 'baz'), 'zim' => array('dib' => 'gir') ); +<<<<<<< HEAD $this->assertEqual($expected, $query->join()); +======= + $this->assertEqual(3, count($query->join())); + $this->assertEqual('gir', $query->join('zim')->dib()); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } /** @@ -408,6 +431,31 @@ class QueryTest extends \lithium\test\Unit { $this->assertEqual($fields, $query->fields()); $this->assertEqual($order, $query->order()); } +<<<<<<< HEAD +======= + + public function testRenderArrayJoin() { + $model = 'lithium\tests\mocks\data\model\MockQueryComment'; + + $query = new Query(compact('model') + array( + 'type' => 'read', + 'source' => 'comments', + 'alias' => 'Comment', + 'conditions' => array('Comment.id' => 1), + 'joins' => array(array( + 'type' => 'INNER', + 'source' => 'posts', + 'alias' => 'Post', + 'constraint' => array('Comment.post_id' => 'Post.id') + )) + )); + + $expected = "SELECT * FROM AS {Comment} INNER JOIN {posts} AS {Post} ON "; + $expected .= "{Comment}.{post_id} = {Post}.{id} WHERE Comment.id = 1;"; + $result = Connections::get('mock-database-connection')->renderCommand($query); + $this->assertEqual($expected, $result); + } +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } ?> \ No newline at end of file diff --git a/libraries/lithium/tests/cases/data/source/MongoDbTest.php b/libraries/lithium/tests/cases/data/source/MongoDbTest.php index 8052647..9a4b623 100644 --- a/libraries/lithium/tests/cases/data/source/MongoDbTest.php +++ b/libraries/lithium/tests/cases/data/source/MongoDbTest.php @@ -23,6 +23,10 @@ use lithium\data\entity\Document; use lithium\tests\mocks\data\MockPost; use lithium\data\collection\DocumentSet; use lithium\data\collection\DocumentArray; +<<<<<<< HEAD +======= +use lithium\tests\mocks\data\source\MockMongoSource; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\tests\mocks\data\source\MockMongoConnection; class MongoDbTest extends \lithium\test\Unit { @@ -241,37 +245,55 @@ class MongoDbTest extends \lithium\test\Unit { } public function testReadNoConditions() { + $this->db->connect(); + $connection = $this->db->connection; + $this->db->connection = new MockMongoSource(); + $this->db->connection->resultSets = array(array('ok' => true)); + $data = array('title' => 'Test Post'); + $options = array('safe' => false, 'fsync' => false); $this->query->data($data); - $this->db->create($this->query); + $this->assertIdentical(true, $this->db->create($this->query)); + $this->assertEqual(compact('data', 'options'), end($this->db->connection->queries)); + $this->db->connection->resultSets = array(array(array('_id' => new MongoId()) + $data)); $result = $this->db->read($this->query); +<<<<<<< HEAD $this->assertTrue($result instanceof DocumentSet); $this->assertEqual(1, $result->count()); +======= +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e - $expected = $data['title']; - $this->assertEqual($expected, $result->first()->title); + $this->assertTrue($result instanceof DocumentSet); + $this->assertEqual(1, $result->count()); + $this->assertEqual('Test Post', $result->first()->title); + $this->db->connection = $connection; } public function testReadWithConditions() { + $this->db->connect(); + $connection = $this->db->connection; + $this->db->connection = new MockMongoSource(); + $this->db->connection->resultSets = array(array('ok' => true)); + $data = array('title' => 'Test Post'); + $options = array('safe' => false, 'fsync' => false); $this->query->data($data); - $this->db->create($this->query); + $this->assertTrue($this->db->create($this->query)); $this->query->data(null); + $this->db->connection->resultSets = array(array()); $this->query->conditions(array('title' => 'Nonexistent Post')); $result = $this->db->read($this->query); $this->assertTrue($result == true); + $this->assertEqual(0, $result->count()); - $expected = 0; - $this->assertEqual($expected, $result->count()); - + $this->db->connection->resultSets = array(array($data)); $this->query->conditions($data); $result = $this->db->read($this->query); $this->assertTrue($result == true); - - $expected = 1; - $this->assertEqual($expected, $result->count()); + $this->assertEqual(1, $result->count()); + $this->db->connection = $connection; } public function testUpdate() { @@ -293,6 +315,7 @@ class MongoDbTest extends \lithium\test\Unit { 'conditions' => array('_id' => $original['_id']) )); $this->assertTrue($this->db->update($this->query)); +<<<<<<< HEAD $result = $this->db->read(new Query(compact('model') + array( 'conditions' => array('_id' => $original['_id']) @@ -300,6 +323,16 @@ class MongoDbTest extends \lithium\test\Unit { $this->assertEqual(1, $result->count()); $updated = $result->first()->to('array'); +======= + + $result = $this->db->read(new Query(compact('model') + array( + 'conditions' => array('_id' => $original['_id']) + ))); + $this->assertEqual(1, $result->count()); + + $updated = $result->first(); + $updated = $updated ? $updated->to('array') : array(); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $this->assertEqual($original['_id'], $updated['_id']); $this->assertEqual('New Post Title', $updated['title']); } @@ -449,17 +482,19 @@ class MongoDbTest extends \lithium\test\Unit { $to = 'lithium\tests\mocks\data\MockPost'; $from::config(array('connection' => 'mock-source')); - $to::config(array('connection' => 'mock-source')); + $to::config(array('connection' => 'mock-source', 'key' => '_id')); $result = $this->db->relationship($from, 'belongsTo', 'MockPost'); - $expected = compact('from', 'to') + array( + $expected = array( 'name' => 'MockPost', 'type' => 'belongsTo', 'keys' => array('mockComment' => '_id'), + 'from' => $from, 'link' => 'contained', - 'conditions' => null, + 'to' => $to, 'fields' => true, 'fieldName' => 'mockPost', + 'constraint' => null, 'init' => true ); $this->assertEqual($expected, $result->data()); @@ -505,7 +540,7 @@ class MongoDbTest extends \lithium\test\Unit { $duplicate = $model::create(array('_id' => $document->_id), array('exists' => true)); $duplicate->values = 'new'; - $duplicate->save(); + $this->assertTrue($duplicate->save()); $document = $model::find((string) $duplicate->_id); $expected = array( @@ -545,7 +580,11 @@ class MongoDbTest extends \lithium\test\Unit { $result = $this->db->conditions($conditions, $query); $this->assertEqual(array_keys($conditions), array_keys($result)); +<<<<<<< HEAD $this->assertTrue($result['_id'] instanceOf MongoId); +======= + $this->assertTrue($result['_id'] instanceof MongoId); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $this->assertEqual($conditions['_id'], (string) $result['_id']); $conditions = array('_id' => array( @@ -553,9 +592,15 @@ class MongoDbTest extends \lithium\test\Unit { )); $result = $this->db->conditions($conditions, $query); $this->assertEqual(3, count($result['_id']['$in'])); +<<<<<<< HEAD $this->assertTrue($result['_id']['$in'][0] instanceOf MongoId); $this->assertTrue($result['_id']['$in'][1] instanceOf MongoId); $this->assertTrue($result['_id']['$in'][2] instanceOf MongoId); +======= + $this->assertTrue($result['_id']['$in'][0] instanceof MongoId); + $this->assertTrue($result['_id']['$in'][1] instanceof MongoId); + $this->assertTrue($result['_id']['$in'][2] instanceof MongoId); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $conditions = array('voters' => array('$all' => array( "4c8f86167675abfabdbf0300", "4c8f86167675abfabdc00300" @@ -563,8 +608,13 @@ class MongoDbTest extends \lithium\test\Unit { $result = $this->db->conditions($conditions, $query); $this->assertEqual(2, count($result['voters']['$all'])); +<<<<<<< HEAD $this->assertTrue($result['voters']['$all'][0] instanceOf MongoId); $this->assertTrue($result['voters']['$all'][1] instanceOf MongoId); +======= + $this->assertTrue($result['voters']['$all'][0] instanceof MongoId); + $this->assertTrue($result['voters']['$all'][1] instanceof MongoId); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $conditions = array('$or' => array( array('_id' => "4c8f86167675abfabdbf0300"), @@ -573,8 +623,13 @@ class MongoDbTest extends \lithium\test\Unit { $result = $this->db->conditions($conditions, $query); $this->assertEqual(array('$or'), array_keys($result)); $this->assertEqual(2, count($result['$or'])); +<<<<<<< HEAD $this->assertTrue($result['$or'][0]['_id'] instanceOf MongoId); $this->assertTrue($result['$or'][1]['guid'] instanceOf MongoId); +======= + $this->assertTrue($result['$or'][0]['_id'] instanceof MongoId); + $this->assertTrue($result['$or'][1]['guid'] instanceof MongoId); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } public function testMultiOperationConditions() { diff --git a/libraries/lithium/tests/cases/data/source/mongo_db/ExporterTest.php b/libraries/lithium/tests/cases/data/source/mongo_db/ExporterTest.php index 1a0f062..f5d9db6 100644 --- a/libraries/lithium/tests/cases/data/source/mongo_db/ExporterTest.php +++ b/libraries/lithium/tests/cases/data/source/mongo_db/ExporterTest.php @@ -245,6 +245,7 @@ class ExporterTest extends \lithium\test\Unit { $result = Exporter::cast($data, $this->_schema, $model::connection(), $options); $this->assertEqual(array_keys($data), array_keys($result)); +<<<<<<< HEAD $this->assertTrue($result['_id'] instanceOf MongoId); $this->assertEqual('4c8f86167675abfabd970300', (string) $result['_id']); @@ -254,6 +255,17 @@ class ExporterTest extends \lithium\test\Unit { $this->assertTrue($result['comments'][0] instanceOf MongoId); $this->assertTrue($result['comments'][1] instanceOf MongoId); $this->assertTrue($result['comments'][2] instanceOf MongoId); +======= + $this->assertTrue($result['_id'] instanceof MongoId); + $this->assertEqual('4c8f86167675abfabd970300', (string) $result['_id']); + + $this->assertTrue($result['comments'] instanceof DocumentArray); + $this->assertEqual(3, count($result['comments'])); + + $this->assertTrue($result['comments'][0] instanceof MongoId); + $this->assertTrue($result['comments'][1] instanceof MongoId); + $this->assertTrue($result['comments'][2] instanceof MongoId); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $this->assertEqual('4c8f86167675abfabdbe0300', (string) $result['comments'][0]); $this->assertEqual('4c8f86167675abfabdbf0300', (string) $result['comments'][1]); $this->assertEqual('4c8f86167675abfabdc00300', (string) $result['comments'][2]); @@ -261,10 +273,17 @@ class ExporterTest extends \lithium\test\Unit { $this->assertEqual($data['comments'], $result['comments']->data()); $this->assertEqual(array('test'), $result['tags']->data()); $this->assertEqual(array('4c8f86167675abfabdb00300'), $result['authors']->data()); +<<<<<<< HEAD $this->assertTrue($result['authors'][0] instanceOf MongoId); $this->assertTrue($result['modified'] instanceOf MongoDate); $this->assertTrue($result['created'] instanceOf MongoDate); +======= + $this->assertTrue($result['authors'][0] instanceof MongoId); + + $this->assertTrue($result['modified'] instanceof MongoDate); + $this->assertTrue($result['created'] instanceof MongoDate); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $this->assertTrue($result['created']->usec > 0); $this->assertEqual($time, $result['modified']->sec); diff --git a/libraries/lithium/tests/cases/g11n/CatalogTest.php b/libraries/lithium/tests/cases/g11n/CatalogTest.php index f761dc6..90fcea4 100644 --- a/libraries/lithium/tests/cases/g11n/CatalogTest.php +++ b/libraries/lithium/tests/cases/g11n/CatalogTest.php @@ -306,78 +306,70 @@ class CatalogTest extends \lithium\test\Unit { $data = array('house' => 'Haus'); Catalog::write('runtime', 'message', 'de', $data); $result = Catalog::read('runtime', 'message', 'de', array('lossy' => false)); - $expected = array( - 'house' => array( - 'id' => 'house', - 'ids' => array(), - 'translated' => 'Haus', - 'flags' => array(), - 'comments' => array(), - 'occurrences' => array() + $expected = array('house' => array( + 'id' => 'house', + 'ids' => array(), + 'translated' => 'Haus', + 'flags' => array(), + 'comments' => array(), + 'occurrences' => array() )); $this->assertEqual($expected, $result); - $data = array( - 'house' => array( - 'id' => 'house', - 'ids' => array(), - 'translated' => 'Haus', - 'flags' => array(), - 'comments' => array(), - 'occurrences' => array() + $data = array('house' => array( + 'id' => 'house', + 'ids' => array(), + 'translated' => 'Haus', + 'flags' => array(), + 'comments' => array(), + 'occurrences' => array() )); Catalog::write('runtime', 'message', 'de', $data); $result = Catalog::read('runtime', 'message', 'de', array('lossy' => false)); - $expected = array( - 'house' => array( - 'id' => 'house', - 'ids' => array(), - 'translated' => 'Haus', - 'flags' => array(), - 'comments' => array(), - 'occurrences' => array() + $expected = array('house' => array( + 'id' => 'house', + 'ids' => array(), + 'translated' => 'Haus', + 'flags' => array(), + 'comments' => array(), + 'occurrences' => array() )); $this->assertEqual($expected, $result); } public function testOutputLossyFormat() { - $data = array( - 'house' => array( - 'id' => 'house', - 'ids' => array('singular' => 'house'), - 'translated' => 'Haus', - 'flags' => array(), - 'comments' => array(), - 'occurrences' => array() + $data = array('house' => array( + 'id' => 'house', + 'ids' => array('singular' => 'house'), + 'translated' => 'Haus', + 'flags' => array(), + 'comments' => array(), + 'occurrences' => array() )); Catalog::write('runtime', 'message', 'de', $data); $result = Catalog::read('runtime', 'message', 'de'); - $expected = array( - 'house' => 'Haus' - ); + $expected = array('house' => 'Haus'); $this->assertEqual($expected, $result); } public function testOutputLosslessFormat() { - $data = array( - 'house' => array( - 'id' => 'house', - 'ids' => array('singular' => 'house'), - 'translated' => 'Haus', - 'flags' => array(), - 'comments' => array(), - 'occurrences' => array() + $data = array('house' => array( + 'id' => 'house', + 'ids' => array('singular' => 'house'), + 'translated' => 'Haus', + 'flags' => array(), + 'comments' => array(), + 'occurrences' => array() )); Catalog::write('runtime', 'message', 'de', $data); $result = Catalog::read('runtime', 'message', 'de', array('lossy' => false)); - $expected = array( - 'house' => array( - 'id' => 'house', - 'ids' => array('singular' => 'house'), - 'translated' => 'Haus', - 'flags' => array(), - 'comments' => array(), - 'occurrences' => array() + $expected = array('house' => array( + 'id' => 'house', + 'ids' => array('singular' => 'house'), + 'translated' => 'Haus', + 'flags' => array(), + 'comments' => array(), + 'occurrences' => array() )); $this->assertEqual($expected, $result); } @@ -385,7 +377,7 @@ class CatalogTest extends \lithium\test\Unit { public function testInvalidWrite() { Catalog::reset(); $data = array('house' => array('id' => 'house')); - $this->expectException("Configuration 'runtime' has not been defined."); + $this->expectException("Configuration `runtime` has not been defined."); $this->assertFalse(Catalog::write('runtime', 'message', 'de', $data)); } } diff --git a/libraries/lithium/tests/cases/g11n/catalog/AdapterTest.php b/libraries/lithium/tests/cases/g11n/catalog/AdapterTest.php index 1a144dc..c3475eb 100644 --- a/libraries/lithium/tests/cases/g11n/catalog/AdapterTest.php +++ b/libraries/lithium/tests/cases/g11n/catalog/AdapterTest.php @@ -8,7 +8,11 @@ namespace lithium\tests\cases\g11n\catalog; +<<<<<<< HEAD use \Exception; +======= +use Exception; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\tests\mocks\g11n\catalog\MockAdapter; class AdapterTest extends \lithium\test\Unit { diff --git a/libraries/lithium/tests/cases/g11n/catalog/adapter/CodeTest.php b/libraries/lithium/tests/cases/g11n/catalog/adapter/CodeTest.php index f414d48..6228fa6 100644 --- a/libraries/lithium/tests/cases/g11n/catalog/adapter/CodeTest.php +++ b/libraries/lithium/tests/cases/g11n/catalog/adapter/CodeTest.php @@ -8,7 +8,11 @@ namespace lithium\tests\cases\g11n\catalog\adapter; +<<<<<<< HEAD use \Exception; +======= +use Exception; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\g11n\catalog\adapter\Code; class CodeTest extends \lithium\test\Unit { diff --git a/libraries/lithium/tests/cases/g11n/catalog/adapter/GettextTest.php b/libraries/lithium/tests/cases/g11n/catalog/adapter/GettextTest.php index 0c8d70d..b58ed43 100644 --- a/libraries/lithium/tests/cases/g11n/catalog/adapter/GettextTest.php +++ b/libraries/lithium/tests/cases/g11n/catalog/adapter/GettextTest.php @@ -8,7 +8,11 @@ namespace lithium\tests\cases\g11n\catalog\adapter; +<<<<<<< HEAD use \Exception; +======= +use Exception; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\tests\mocks\g11n\catalog\adapter\MockGettext; class GettextTest extends \lithium\test\Unit { diff --git a/libraries/lithium/tests/cases/g11n/catalog/adapter/PhpTest.php b/libraries/lithium/tests/cases/g11n/catalog/adapter/PhpTest.php index 37246c3..ca89bb0 100644 --- a/libraries/lithium/tests/cases/g11n/catalog/adapter/PhpTest.php +++ b/libraries/lithium/tests/cases/g11n/catalog/adapter/PhpTest.php @@ -8,7 +8,11 @@ namespace lithium\tests\cases\g11n\catalog\adapter; +<<<<<<< HEAD use \Exception; +======= +use Exception; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\g11n\catalog\adapter\Php; class PhpTest extends \lithium\test\Unit { diff --git a/libraries/lithium/tests/cases/net/http/MediaTest.php b/libraries/lithium/tests/cases/net/http/MediaTest.php index 635c469..b045fb4 100644 --- a/libraries/lithium/tests/cases/net/http/MediaTest.php +++ b/libraries/lithium/tests/cases/net/http/MediaTest.php @@ -319,7 +319,7 @@ class MediaTest extends \lithium\test\Unit { $response = new Response(); $response->type('bad'); - $this->expectException("Unhandled media type 'bad'"); + $this->expectException("Unhandled media type `bad`."); Media::render($response, array('foo' => 'bar')); $result = $response->body; @@ -336,7 +336,7 @@ class MediaTest extends \lithium\test\Unit { $response = new Response(); $response->type('xml'); - $this->expectException("Unhandled media type 'xml'"); + $this->expectException("Unhandled media type `xml`."); Media::render($response, array('foo' => 'bar')); $result = $response->body; diff --git a/libraries/lithium/tests/cases/net/http/RouterTest.php b/libraries/lithium/tests/cases/net/http/RouterTest.php index 5cf5616..03a58a5 100644 --- a/libraries/lithium/tests/cases/net/http/RouterTest.php +++ b/libraries/lithium/tests/cases/net/http/RouterTest.php @@ -406,6 +406,13 @@ class RouterTest extends \lithium\test\Unit { $expected = '/my/web/path/posts'; $this->assertEqual($expected, $result); + $request = new Request(array('base' => '/my/web/path')); + $result = Router::match('/some/where', $request, array('absolute' => true)); + $prefix = $this->request->env('HTTPS') ? 'https://' : 'http://'; + $prefix .= $this->request->env('HTTP_HOST'); + $this->assertEqual($prefix . '/my/web/path/some/where', $result); + + $result = Router::match('mailto:foo@localhost'); $expected = 'mailto:foo@localhost'; $this->assertEqual($expected, $result); diff --git a/libraries/lithium/tests/cases/security/AuthTest.php b/libraries/lithium/tests/cases/security/AuthTest.php index bb4e4b2..32eac7b 100644 --- a/libraries/lithium/tests/cases/security/AuthTest.php +++ b/libraries/lithium/tests/cases/security/AuthTest.php @@ -20,7 +20,7 @@ class AuthTest extends \lithium\test\Unit { Auth::config(array( 'test' => array( - 'adapter' => '\lithium\tests\mocks\security\auth\adapter\MockAuthAdapter' + 'adapter' => 'lithium\tests\mocks\security\auth\adapter\MockAuthAdapter' ) )); } @@ -73,7 +73,7 @@ class AuthTest extends \lithium\test\Unit { public function testNoConfigurations() { Auth::reset(); $this->assertIdentical(array(), Auth::config()); - $this->expectException("Configuration 'user' has not been defined."); + $this->expectException("Configuration `user` has not been defined."); Auth::check('user'); } } diff --git a/libraries/lithium/tests/cases/storage/CacheTest.php b/libraries/lithium/tests/cases/storage/CacheTest.php index e227d16..30cafda 100644 --- a/libraries/lithium/tests/cases/storage/CacheTest.php +++ b/libraries/lithium/tests/cases/storage/CacheTest.php @@ -10,7 +10,11 @@ namespace lithium\tests\cases\storage; use lithium\storage\Cache; use lithium\util\Collection; +<<<<<<< HEAD use \SplFileInfo; +======= +use SplFileInfo; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e class CacheTest extends \lithium\test\Unit { diff --git a/libraries/lithium/tests/cases/storage/SessionTest.php b/libraries/lithium/tests/cases/storage/SessionTest.php index 79af4bd..726c4b3 100644 --- a/libraries/lithium/tests/cases/storage/SessionTest.php +++ b/libraries/lithium/tests/cases/storage/SessionTest.php @@ -189,7 +189,7 @@ class SessionTest extends \lithium\test\Unit { public function testSessionState() { $this->assertTrue(Session::isStarted()); $this->assertTrue(Session::isStarted('default')); - $this->expectException("Configuration 'invalid' has not been defined."); + $this->expectException("Configuration `invalid` has not been defined."); $this->assertFalse(Session::isStarted('invalid')); } @@ -200,7 +200,7 @@ class SessionTest extends \lithium\test\Unit { public function testSessionStateResetNamed() { Session::reset(); - $this->expectException("Configuration 'default' has not been defined."); + $this->expectException("Configuration `default` has not been defined."); $this->assertFalse(Session::isStarted('default')); } diff --git a/libraries/lithium/tests/cases/storage/cache/adapter/FileTest.php b/libraries/lithium/tests/cases/storage/cache/adapter/FileTest.php index eef9aa2..9aba8ec 100644 --- a/libraries/lithium/tests/cases/storage/cache/adapter/FileTest.php +++ b/libraries/lithium/tests/cases/storage/cache/adapter/FileTest.php @@ -9,7 +9,11 @@ namespace lithium\tests\cases\storage\cache\adapter; use lithium\storage\cache\adapter\File; +<<<<<<< HEAD use \SplFileInfo; +======= +use SplFileInfo; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e class FileTest extends \lithium\test\Unit { diff --git a/libraries/lithium/tests/cases/template/ViewTest.php b/libraries/lithium/tests/cases/template/ViewTest.php index 5bbde6f..7e50180 100644 --- a/libraries/lithium/tests/cases/template/ViewTest.php +++ b/libraries/lithium/tests/cases/template/ViewTest.php @@ -9,6 +9,7 @@ namespace lithium\tests\cases\template; use lithium\template\View; +<<<<<<< HEAD use lithium\g11n\catalog\adapter\Memory; use lithium\template\view\adapter\Simple; @@ -18,6 +19,12 @@ class TestViewClass extends \lithium\template\View { return $this->_config['renderer']; } } +======= +use lithium\action\Response; +use lithium\g11n\catalog\adapter\Memory; +use lithium\template\view\adapter\Simple; +use lithium\tests\mocks\template\MockView; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e class ViewTest extends \lithium\test\Unit { @@ -29,15 +36,18 @@ class ViewTest extends \lithium\test\Unit { public function testInitialization() { $expected = new Simple(); - $this->_view = new TestViewClass(array('renderer' => $expected)); + $this->_view = new MockView(array('renderer' => $expected)); $result = $this->_view->renderer(); $this->assertEqual($expected, $result); } - public function testInitializationWithBadClasses() { - $this->expectException("Class 'Badness' of type 'adapter.template.view' not found."); + public function testInitializationWithBadLoader() { + $this->expectException("Class `Badness` of type `adapter.template.view` not found."); new View(array('loader' => 'Badness')); - $this->expectException("Class 'Badness' of type 'adapter.template.view' not found."); + } + + public function testInitializationWithBadRenderer() { + $this->expectException("Class `Badness` of type `adapter.template.view` not found."); new View(array('renderer' => 'Badness')); } @@ -48,6 +58,31 @@ class ViewTest extends \lithium\test\Unit { $this->assertEqual($expected, $result); } + /** + * Tests that the output-escaping handler correctly inherits its encoding from the `Response` + * object, if provided. + * + * @return void + */ + public function testEscapeOutputFilterWithInjectedEncoding() { + $message = "Multibyte string support must be enabled to test character encodings."; + $this->skipIf(!function_exists('mb_convert_encoding'), $message); + + $string = "Joël"; + + $response = new Response(); + $response->encoding = 'UTF-8'; + $view = new View(compact('response')); + $handler = $view->outputFilters['h']; + $this->assertTrue(mb_check_encoding($handler($string), "UTF-8")); + + $response = new Response(); + $response->encoding = 'ISO-8859-1'; + $view = new View(compact('response')); + $handler = $view->outputFilters['h']; + $this->assertTrue(mb_check_encoding($handler($string), "ISO-8859-1")); + } + public function testBasicRenderModes() { $view = new View(array('loader' => 'Simple', 'renderer' => 'Simple')); @@ -63,6 +98,12 @@ class ViewTest extends \lithium\test\Unit { $expected = "Logged in as: Cap'n Crunch."; $this->assertEqual($expected, $result); + $result = $view->render('element', array('name' => "Cap'n Crunch"), array( + 'element' => 'Logged in as: {:name}.' + )); + $expected = "Logged in as: Cap'n Crunch."; + $this->assertEqual($expected, $result); + $xmlHeader = '<' . '?xml version="1.0" ?' . '>' . "\n"; $result = $view->render('all', array('type' => 'auth', 'success' => 'true'), array( 'layout' => $xmlHeader . "\n{:content}\n", @@ -72,6 +113,20 @@ class ViewTest extends \lithium\test\Unit { $this->assertEqual($expected, $result); } + public function testTwoStepRenderWithVariableCapture() { + $view = new View(array('loader' => 'Simple', 'renderer' => 'Simple')); + + $result = $view->render( + array( + array('path' => 'element', 'capture' => array('data' => 'foo')), + array('path' => 'template') + ), + array('name' => "Cap'n Crunch"), + array('element' => 'Logged in as: {:name}.', 'template' => '--{:foo}--') + ); + $this->assertEqual('--Logged in as: Cap\'n Crunch.--', $result); + } + public function testFullRenderNoLayout() { $view = new View(array('loader' => 'Simple', 'renderer' => 'Simple')); $result = $view->render('all', array('type' => 'auth', 'success' => 'true'), array( diff --git a/libraries/lithium/tests/cases/template/helper/FormTest.php b/libraries/lithium/tests/cases/template/helper/FormTest.php index 573a92b..56e7aae 100755 --- a/libraries/lithium/tests/cases/template/helper/FormTest.php +++ b/libraries/lithium/tests/cases/template/helper/FormTest.php @@ -96,7 +96,11 @@ class FormTest extends \lithium\test\Unit { $result = $this->form->create(null, array('id' => 'Registration')); $this->assertTags($result, array( - 'form' => array('action' => "{$this->base}posts", 'method' => 'post', 'id' => 'Registration') + 'form' => array( + 'action' => "{$this->base}posts", + 'method' => 'post', + 'id' => 'Registration' + ) )); } @@ -267,6 +271,13 @@ class FormTest extends \lithium\test\Unit { ))); } + public function testHiddenFieldWithId() { + $result = $this->form->hidden('my_field'); + $this->assertTags($result, array('input' => array( + 'type' => 'hidden', 'name' => 'my_field', 'id' => 'MyField' + ))); + } + public function testLabelGeneration() { $result = $this->form->label('next', 'Enter the next value >>'); $this->assertTags($result, array( @@ -648,6 +659,20 @@ class FormTest extends \lithium\test\Unit { $this->assertEqual(join('', $expected), $result); } + /** + * Verifies that calls to `field()` with `'type' => 'hidden'` do not produce `<label />`s. + * + * @return void + */ + public function testHiddenFieldWithNoLabel() { + $result = $this->form->field('foo', array('type' => 'hidden')); + $this->assertTags($result, array( + 'div' => array(), + 'input' => array('type' => 'hidden', 'name' => 'foo', 'id' => 'Foo'), + '/div' + )); + } + public function testFormFieldWithCustomTemplate() { $result = $this->form->field('name', array( 'template' => '<div{:wrap}>{:label}: {:input}{:error}</div>' @@ -880,6 +905,7 @@ class FormTest extends \lithium\test\Unit { 'label' => array('for' => 'Name'), 'Name', '/label', 'input' => array('type' => 'text', 'name' => 'name', 'id' => 'Name'), )); +<<<<<<< HEAD } /** @@ -895,6 +921,29 @@ class FormTest extends \lithium\test\Unit { $this->assertTags($result, array('input' => array( 'type' => 'text', 'name' => 'foo[bar]', 'id' => 'FooBar', 'value' => 'value' ))); +======= + } + + /** + * Tests that inputs for nested objects can be assigned using dot syntax. + * + * @return void + */ + public function testNestedFieldAccess() { + $doc = new Document(array('data' => array('foo' => array('bar' => 'value')))); + $this->form->create($doc); + + $result = $this->form->text('foo.bar'); + $this->assertTags($result, array('input' => array( + 'type' => 'text', 'name' => 'foo[bar]', 'id' => 'FooBar', 'value' => 'value' + ))); + } + + public function testFormCreationWithNoContext() { + $this->form = new Form(array('context' => new MockFormRenderer())); + $result = $this->form->create(null, array('url' => '/foo')); + $this->assertTags($result, array('form' => array('action' => "/foo", 'method'=> "post"))); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } } diff --git a/libraries/lithium/tests/cases/template/helper/HtmlTest.php b/libraries/lithium/tests/cases/template/helper/HtmlTest.php index ac06d93..34119cc 100644 --- a/libraries/lithium/tests/cases/template/helper/HtmlTest.php +++ b/libraries/lithium/tests/cases/template/helper/HtmlTest.php @@ -10,6 +10,11 @@ namespace lithium\tests\cases\template\helper; use lithium\net\http\Router; use lithium\template\helper\Html; +<<<<<<< HEAD +======= +use lithium\action\Request; +use lithium\action\Response; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\tests\mocks\template\helper\MockHtmlRenderer; class HtmlTest extends \lithium\test\Unit { @@ -34,7 +39,12 @@ class HtmlTest extends \lithium\test\Unit { Router::connect('/{:controller}/{:action}/{:id}.{:type}'); Router::connect('/{:controller}/{:action}.{:type}'); - $this->context = new MockHtmlRenderer(); + $this->context = new MockHtmlRenderer(array( + 'request' => new Request(array( + 'base' => '', 'env' => array('HTTP_HOST' => 'foo.local') + )), + 'response' => new Response() + )); $this->html = new Html(array('context' => &$this->context)); } @@ -53,21 +63,25 @@ class HtmlTest extends \lithium\test\Unit { } /** - * Tests that character set declarations render the correct character set and meta tag. + * Tests that character set declarations render the + * correct character set and short meta tag. * * @return void */ public function testCharset() { $result = $this->html->charset(); + $this->assertTags($result, array('meta' => array( + 'charset' => 'UTF-8' + ))); + $result = $this->html->charset('utf-8'); $this->assertTags($result, array('meta' => array( - 'http-equiv' => 'Content-Type', 'content' => 'text/html; charset=utf-8' + 'charset' => 'utf-8' ))); $result = $this->html->charset('UTF-7'); - $this->assertTags($result, array('meta' => array( - 'http-equiv' => 'Content-Type', 'content' => 'text/html; charset=UTF-7' + 'charset' => 'UTF-7' ))); } @@ -350,7 +364,13 @@ class HtmlTest extends \lithium\test\Unit { $expected = array('meta' => array('author' => 'foo')); $this->assertTags($result, $expected); +<<<<<<< HEAD $result = $this->html->head('unexisting-name', array('options' => array('author' => 'foo'))); +======= + $result = $this->html->head('unexisting-name', array( + 'options' => array('author' => 'foo') + )); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $this->assertNull($result); } diff --git a/libraries/lithium/tests/cases/template/view/RendererTest.php b/libraries/lithium/tests/cases/template/view/RendererTest.php index a005033..815585b 100644 --- a/libraries/lithium/tests/cases/template/view/RendererTest.php +++ b/libraries/lithium/tests/cases/template/view/RendererTest.php @@ -10,6 +10,7 @@ namespace lithium\tests\cases\template\view; use lithium\template\View; use lithium\action\Request; +use lithium\action\Response; use lithium\template\Helper; use lithium\template\helper\Html; use lithium\template\view\adapter\Simple; @@ -22,9 +23,12 @@ class RendererTest extends \lithium\test\Unit { $this->_routes = Router::get(); Router::reset(); Router::connect('/{:controller}/{:action}'); - $this->subject = new Simple(array('request' => new Request(array( - 'base' => '', 'env' => array('HTTP_HOST' => 'foo.local') - )))); + $this->subject = new Simple(array( + 'request' => new Request(array( + 'base' => '', 'env' => array('HTTP_HOST' => 'foo.local') + )), + 'response' => new Response() + )); } public function tearDown() { @@ -82,6 +86,7 @@ class RendererTest extends \lithium\test\Unit { $class = get_class($helper); $path = $this->subject->applyHandler($helper, "{$class}::script", 'path', 'foo/file'); $this->assertEqual('/js/foo/file.js', $path); + $this->assertEqual('/some/generic/path', $this->subject->path('some/generic/path')); } public function testHandlerInsertion() { @@ -180,8 +185,10 @@ class RendererTest extends \lithium\test\Unit { public function testGetters() { $this->assertTrue($this->subject->request() instanceof Request); + $this->assertTrue($this->subject->response() instanceof Response); $this->subject = new Simple(); $this->assertNull($this->subject->request()); + $this->assertNull($this->subject->response()); } public function testSetAndData() { diff --git a/libraries/lithium/tests/cases/template/view/adapter/FileTest.php b/libraries/lithium/tests/cases/template/view/adapter/FileTest.php index b70296b..800366e 100644 --- a/libraries/lithium/tests/cases/template/view/adapter/FileTest.php +++ b/libraries/lithium/tests/cases/template/view/adapter/FileTest.php @@ -83,14 +83,20 @@ class FileTest extends \lithium\test\Unit { )); $this->assertPattern('/\/views\/pages\/home\.html\.php$/', $template); - $template = $file->template('invalid', array('template' => 'foo')); - $this->assertNull($template); - $this->expectException('/Template not found/'); $file->template('template', array( 'controller' => 'pages', 'template' => 'foo', 'type' => 'html' )); } + + public function testInvalidTemplateType() { + $file = new File(array('compile' => false, 'paths' => array( + 'template' => '{:library}/views/{:controller}/{:template}.{:type}.php' + ))); + + $this->expectException("Invalid template type 'invalid'."); + $template = $file->template('invalid', array('template' => 'foo')); + } } ?> \ No newline at end of file diff --git a/libraries/lithium/tests/cases/test/UnitTest.php b/libraries/lithium/tests/cases/test/UnitTest.php index 3493f85..0a7147b 100644 --- a/libraries/lithium/tests/cases/test/UnitTest.php +++ b/libraries/lithium/tests/cases/test/UnitTest.php @@ -11,7 +11,7 @@ namespace lithium\tests\cases\test; use lithium\tests\mocks\test\MockUnitTest; use lithium\tests\mocks\test\cases\MockSkipThrowsException; use lithium\tests\mocks\test\cases\MockTestErrorHandling; -use \Exception; +use Exception; class UnitTest extends \lithium\test\Unit { @@ -65,19 +65,12 @@ class UnitTest extends \lithium\test\Unit { 'result' => 'fail', 'file' => __FILE__, 'line' => __LINE__ - 3, 'method' => 'testAssertEqualNumericFail', 'assertion' => 'assertEqual', 'class' => __CLASS__, 'message' => - "trace: [2]\nexpected: array (\n 0 => 1,\n 1 => 2,\n 2 => 3,\n)\n" - . "result: array (\n 0 => 1,\n 1 => 2,\n)\n", + "trace: [2]\nexpected: 3\n" + . "result: NULL\n", 'data' => array( 'trace' => '[2]', - 'expected' => array( - 0 => 1, - 1 => 2, - 2 => 3, - ), - 'result' => array( - 0 => 1, - 1 => 2, - ) + 'expected' => 3, + 'result' => null ) ); $result = array_pop($this->_results); @@ -111,32 +104,22 @@ class UnitTest extends \lithium\test\Unit { 'result' => 'fail', 'file' => __FILE__, 'line' => __LINE__ - 3, 'method' => 'testAssertEqualThreeDFail', 'assertion' => 'assertEqual', 'class' => __CLASS__, 'message' => - "trace: [0][1][1]\nexpected: array (\n 0 => 1,\n 1 => 2,\n)\n" - . "result: array (\n 0 => 1,\n)\n" - . "trace: [1][1][1]\nexpected: array (\n 0 => 1,\n 1 => 2,\n)\n" - . "result: array (\n 0 => 1,\n)\n", + "trace: [0][1][1]\nexpected: 2\n" + . "result: NULL\n" + . "trace: [1][1][1]\nexpected: 2\n" + . "result: NULL\n", 'data' => array( array( array( 'trace' => '[0][1][1]', - 'expected' => array( - 0 => 1, - 1 => 2, - ), - 'result' => array( - 0 => 1, - ) + 'expected' => 2, + 'result' => null ), ), array( array('trace' => '[1][1][1]', - 'expected' => array( - 0 => 1, - 1 => 2, - ), - 'result' => array( - 0 => 1, - ) + 'expected' => 2, + 'result' => null ) ) ) @@ -493,11 +476,11 @@ class UnitTest extends \lithium\test\Unit { public function testCompareWithEmptyResult() { $result = $this->compare('equal', array('key' => array('val1', 'val2')), array()); - $expected = array(array( - 'trace' => '[key][0]', + $expected = array( + 'trace' => '[key]', 'expected' => array('val1', 'val2'), 'result' => array() - )); + ); $this->assertEqual($expected, $result); } @@ -542,7 +525,11 @@ class UnitTest extends \lithium\test\Unit { public function testCompareIdenticalArray() { $expected = array( +<<<<<<< HEAD 'trace' => '[0]', +======= + 'trace' => null, +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e 'expected' => array(), 'result' => array('two', 'values') ); @@ -550,12 +537,35 @@ class UnitTest extends \lithium\test\Unit { $this->assertEqual($expected, $result); } +<<<<<<< HEAD +======= + public function imethods() { + return array('testCompareIdenticalArray'); + } + + public function testCompareEqualNullArray() { + $expected = array('trace' => null, 'expected' => array(), 'result' => array(null)); + $result = $this->compare('equal', array(), array(null)); + $this->assertEqual($expected, $result); + } + + public function testCompareIdenticalNullArray() { + $expected = array('trace' => null, 'expected' => array(), 'result' => array(null)); + $result = $this->compare('identical', array(), array(null)); + $this->assertEqual($expected, $result); + } + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e /** * Always keep second to last. * */ public function testResults() { +<<<<<<< HEAD $expected = 87; +======= + $expected = 89; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $result = count($this->results()); $this->assertEqual($expected, $result); } @@ -581,6 +591,10 @@ class UnitTest extends \lithium\test\Unit { 'testCompareWithEmptyResult', 'testExceptionCatching', 'testErrorHandling', 'testAssertObjects', 'testAssertArrayIdentical', 'testCompareIdenticalArray', +<<<<<<< HEAD +======= + 'testCompareEqualNullArray', 'testCompareIdenticalNullArray', +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e 'testResults', 'testTestMethods' ); $this->assertIdentical($expected, $this->methods()); diff --git a/libraries/lithium/tests/cases/util/CollectionTest.php b/libraries/lithium/tests/cases/util/CollectionTest.php index b5e4922..ea60b27 100644 --- a/libraries/lithium/tests/cases/util/CollectionTest.php +++ b/libraries/lithium/tests/cases/util/CollectionTest.php @@ -8,7 +8,11 @@ namespace lithium\tests\cases\util; +<<<<<<< HEAD use \stdClass; +======= +use stdClass; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e use lithium\util\Collection; use lithium\tests\mocks\util\MockCollectionMarker; use lithium\tests\mocks\util\MockCollectionObject; diff --git a/libraries/lithium/tests/cases/util/InflectorTest.php b/libraries/lithium/tests/cases/util/InflectorTest.php index 9c6a0b3..6a72eb0 100644 --- a/libraries/lithium/tests/cases/util/InflectorTest.php +++ b/libraries/lithium/tests/cases/util/InflectorTest.php @@ -356,9 +356,18 @@ class InflectorTest extends \lithium\test\Unit { /** * This is a helper method for testStorageMechanism to fetch a private * property of the Inflector class. +<<<<<<< HEAD */ private function getProtectedValue($property) { $info = Inspector::info("lithium\util\Inflector::$property"); +======= + * + * @param string $property + * @return string The value of the property. + */ + private function getProtectedValue($property) { + $info = Inspector::info("lithium\util\Inflector::{$property}"); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e return $info['value']; } } diff --git a/libraries/lithium/tests/cases/util/StringTest.php b/libraries/lithium/tests/cases/util/StringTest.php index dec17dc..ec2cf1d 100644 --- a/libraries/lithium/tests/cases/util/StringTest.php +++ b/libraries/lithium/tests/cases/util/StringTest.php @@ -17,7 +17,11 @@ class StringTest extends \lithium\test\Unit { * testRandomGenerator method * * @return void +<<<<<<< HEAD **/ +======= + */ +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e public function testRandomGenerator() { // Disallow allow seeding twice $this->assertFalse(String::seed() && String::seed()); diff --git a/libraries/lithium/tests/cases/util/ValidatorTest.php b/libraries/lithium/tests/cases/util/ValidatorTest.php index 480f165..3de3334 100644 --- a/libraries/lithium/tests/cases/util/ValidatorTest.php +++ b/libraries/lithium/tests/cases/util/ValidatorTest.php @@ -78,7 +78,7 @@ class ValidatorTest extends \lithium\test\Unit { $this->assertTrue(in_array('foo', Validator::rules())); $this->assertEqual('/^foo$/', Validator::rules('foo')); - $this->expectException("Rule 'bar' is not a validation rule"); + $this->expectException("Rule `bar` is not a validation rule."); $this->assertNull(Validator::isBar('foo')); } diff --git a/libraries/lithium/tests/integration/analysis/LoggerTest.php b/libraries/lithium/tests/integration/analysis/LoggerTest.php index 456add4..491b492 100644 --- a/libraries/lithium/tests/integration/analysis/LoggerTest.php +++ b/libraries/lithium/tests/integration/analysis/LoggerTest.php @@ -40,7 +40,10 @@ class LoggerTest extends \lithium\test\Unit { unlink($base . '/info.log'); } +<<<<<<< HEAD +======= +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } ?> \ No newline at end of file diff --git a/libraries/lithium/tests/integration/data/FieldsTest.php b/libraries/lithium/tests/integration/data/FieldsTest.php index 548959d..b4bfe7b 100644 --- a/libraries/lithium/tests/integration/data/FieldsTest.php +++ b/libraries/lithium/tests/integration/data/FieldsTest.php @@ -4,6 +4,7 @@ namespace lithium\tests\integration\data; use lithium\data\Connections; use lithium\data\Entity; +<<<<<<< HEAD class MockCompany extends \lithium\data\Model { @@ -13,11 +14,18 @@ class MockCompany extends \lithium\data\Model { ); } +======= +use lithium\tests\mocks\data\Company; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e class FieldsTest extends \lithium\test\Unit { public function setUp() { - MockCompany::config(); + Company::config(); + } + + public function tearDown() { + Company::remove(); } public function tearDown() { @@ -33,12 +41,17 @@ class FieldsTest extends \lithium\test\Unit { } public function testSingleField() { +<<<<<<< HEAD $new = MockCompany::create(array('name' => 'Acme, Inc.')); $key = MockCompany::meta('key'); +======= + $new = Company::create(array('name' => 'Acme, Inc.')); + $key = Company::meta('key'); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $new->save(); $id = is_object($new->{$key}) ? (string) $new->{$key} : $new->{$key}; - $entity = MockCompany::first($id); + $entity = Company::first($id); $this->assertTrue($entity instanceof Entity); $this->skipIf(!$entity instanceof Entity, 'Queried object is not an entity.'); @@ -47,7 +60,11 @@ class FieldsTest extends \lithium\test\Unit { $result = $entity->data(); $this->assertEqual($expected, $result); +<<<<<<< HEAD $entity = MockCompany::first(array('fields' => array($key))); +======= + $entity = Company::first(array('fields' => array($key))); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $this->assertTrue($entity instanceof Entity); $this->skipIf(!$entity instanceof Entity, 'Queried object is not an entity.'); @@ -56,7 +73,11 @@ class FieldsTest extends \lithium\test\Unit { $result = $entity->data(); $this->assertEqual($expected, $result); +<<<<<<< HEAD $entity = MockCompany::find('first',array( +======= + $entity = Company::find('first',array( +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e 'conditions' => array($key => $id), 'fields' => array($key) )); diff --git a/libraries/lithium/tests/integration/data/source/CouchDbTest.php b/libraries/lithium/tests/integration/data/source/CouchDbTest.php index eba1614..b10e3be 100644 --- a/libraries/lithium/tests/integration/data/source/CouchDbTest.php +++ b/libraries/lithium/tests/integration/data/source/CouchDbTest.php @@ -9,6 +9,7 @@ namespace lithium\tests\integration\data\source; use lithium\data\Connections; +<<<<<<< HEAD class MockCouchModel extends \lithium\data\Model { protected $_schema = array( @@ -16,6 +17,10 @@ class MockCouchModel extends \lithium\data\Model { ); } +======= +use lithium\tests\mocks\MockCouchModel; + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e class CouchDbTest extends \lithium\test\Integration { public function setUp() { @@ -98,7 +103,6 @@ class CouchDbTest extends \lithium\test\Integration { $this->assertEqual($data['id'], $updated['id']); $this->assertNotEqual($data['rev'], $updated['rev']); } - } ?> \ No newline at end of file diff --git a/libraries/lithium/tests/integration/net/http/ServiceTest.php b/libraries/lithium/tests/integration/net/http/ServiceTest.php index 067611d..cbf9ada 100644 --- a/libraries/lithium/tests/integration/net/http/ServiceTest.php +++ b/libraries/lithium/tests/integration/net/http/ServiceTest.php @@ -17,7 +17,11 @@ class ServiceTest extends \lithium\test\Integration { 'classes' => array('socket' => '\lithium\net\socket\Stream') )); $service->head(); +<<<<<<< HEAD +======= + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $expected = array('code' => 200, 'message' => 'OK'); $result = $service->last->response->status; $this->assertEqual($expected, $result); @@ -28,7 +32,11 @@ class ServiceTest extends \lithium\test\Integration { 'classes' => array('socket' => '\lithium\net\socket\Context') )); $service->head(); +<<<<<<< HEAD +======= + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $expected = array('code' => 200, 'message' => 'OK'); $result = $service->last->response->status; $this->assertEqual($expected, $result); @@ -39,9 +47,15 @@ class ServiceTest extends \lithium\test\Integration { 'classes' => array('socket' => '\lithium\net\socket\Curl') )); $service->head(); +<<<<<<< HEAD +======= + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e $expected = array('code' => 200, 'message' => 'OK'); $result = $service->last->response->status; $this->assertEqual($expected, $result); } } + +?> \ No newline at end of file diff --git a/libraries/lithium/tests/mocks/action/MockDispatcher.php b/libraries/lithium/tests/mocks/action/MockDispatcher.php index a7df326..8387a0c 100644 --- a/libraries/lithium/tests/mocks/action/MockDispatcher.php +++ b/libraries/lithium/tests/mocks/action/MockDispatcher.php @@ -8,7 +8,7 @@ namespace lithium\tests\mocks\action; -use \stdClass; +use stdClass; class MockDispatcher extends \lithium\action\Dispatcher { diff --git a/libraries/lithium/tests/mocks/data/Employees.php b/libraries/lithium/tests/mocks/data/Employees.php new file mode 100644 index 0000000..5245ddb --- /dev/null +++ b/libraries/lithium/tests/mocks/data/Employees.php @@ -0,0 +1,23 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace lithium\tests\mocks\data; + +class Employees extends \lithium\data\Model { + + public $belongsTo = array('Companies'); + + protected $_meta = array('connection' => 'test'); + + public function lastName($entity) { + $name = explode(' ', $entity->name); + return $name[1]; + } +} + +?> \ No newline at end of file diff --git a/libraries/lithium/tests/mocks/data/MockCouchModel.php b/libraries/lithium/tests/mocks/data/MockCouchModel.php new file mode 100644 index 0000000..239be89 --- /dev/null +++ b/libraries/lithium/tests/mocks/data/MockCouchModel.php @@ -0,0 +1,18 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace lithium\tests\mocks\data; + +class MockCouchModel extends \lithium\data\Model { + + protected $_schema = array( + 'someKey' => array() + ); +} + +?> \ No newline at end of file diff --git a/libraries/lithium/tests/mocks/data/MockPost.php b/libraries/lithium/tests/mocks/data/MockPost.php index c7a445b..6a03eb4 100644 --- a/libraries/lithium/tests/mocks/data/MockPost.php +++ b/libraries/lithium/tests/mocks/data/MockPost.php @@ -12,6 +12,8 @@ class MockPost extends \lithium\tests\mocks\data\MockBase { public $hasMany = array('MockComment'); + public static $connection = null; + public static function resetSchema() { static::_object()->_schema = array(); } @@ -23,6 +25,13 @@ class MockPost extends \lithium\tests\mocks\data\MockBase { public static function instances() { return array_keys(static::$_instances); } + + public static function &connection() { + if (static::$connection) { + return static::$connection; + } + return parent::connection(); + } } ?> \ No newline at end of file diff --git a/libraries/lithium/tests/mocks/data/MockSource.php b/libraries/lithium/tests/mocks/data/MockSource.php index 137016d..1024a6e 100644 --- a/libraries/lithium/tests/mocks/data/MockSource.php +++ b/libraries/lithium/tests/mocks/data/MockSource.php @@ -13,9 +13,9 @@ use lithium\util\Inflector; class MockSource extends \lithium\data\Source { protected $_classes = array( - 'entity' => '\lithium\data\entity\Record', - 'set' => '\lithium\data\collection\RecordSet', - 'relationship' => '\lithium\data\model\Relationship' + 'entity' => 'lithium\data\entity\Record', + 'set' => 'lithium\data\collection\RecordSet', + 'relationship' => 'lithium\data\model\Relationship' ); private $_mockPosts = array( diff --git a/libraries/lithium/tests/mocks/data/source/MockMongoSource.php b/libraries/lithium/tests/mocks/data/source/MockMongoSource.php new file mode 100644 index 0000000..45ac538 --- /dev/null +++ b/libraries/lithium/tests/mocks/data/source/MockMongoSource.php @@ -0,0 +1,40 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace lithium\tests\mocks\data\source; + +use MongoId; +use lithium\tests\mocks\data\source\mongo_db\MockResult; + +class MockMongoSource extends \lithium\core\Object { + + public $resultSets = array(); + + public $queries = array(); + + public function __get($name) { + return $this; + } + + public function insert(&$data, $options) { + $this->queries[] = compact('data', 'options'); + $result = current($this->resultSets); + next($this->resultSets); + $data['_id'] = new MongoId(); + return $result; + } + + public function find($conditions, $fields) { + $this->queries[] = compact('conditions', 'fields'); + $result = new MockResult(array('data' => current($this->resultSets))); + next($this->resultSets); + return $result; + } +} + +?> \ No newline at end of file diff --git a/libraries/lithium/tests/mocks/data/source/mongo_db/MockResult.php b/libraries/lithium/tests/mocks/data/source/mongo_db/MockResult.php index f4b6b65..47dcf09 100644 --- a/libraries/lithium/tests/mocks/data/source/mongo_db/MockResult.php +++ b/libraries/lithium/tests/mocks/data/source/mongo_db/MockResult.php @@ -10,22 +10,57 @@ namespace lithium\tests\mocks\data\source\mongo_db; class MockResult extends \lithium\data\source\mongo_db\Result { +<<<<<<< HEAD protected $_data = array( false, +======= + protected $_autoConfig = array('data'); + + protected $_data = array( +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e array('_id' => '4c8f86167675abfabdbf0300', 'title' => 'bar'), array('_id' => '5c8f86167675abfabdbf0301', 'title' => 'foo'), array('_id' => '6c8f86167675abfabdbf0302', 'title' => 'dib') ); +<<<<<<< HEAD +======= + public function hasNext() { + if (!is_array($this->_data)) { + return false; + } + return key($this->_data) !== null && key($this->_data) < count($this->_data); + } + + public function getNext() { + $result = current($this->_data); + next($this->_data); + return $result; + } + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e public function next() { return $this->_next(); } +<<<<<<< HEAD +======= + public function __call($method, array $params) { + return $this; + } + +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e protected function _close() { } protected function _next() { +<<<<<<< HEAD return next($this->_data) ?: null; +======= + $result = current($this->_data) ?: null; + next($this->_data); + return $result; +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } } diff --git a/libraries/lithium/tests/mocks/template/MockHelper.php b/libraries/lithium/tests/mocks/template/MockHelper.php index d67a5aa..0b48229 100644 --- a/libraries/lithium/tests/mocks/template/MockHelper.php +++ b/libraries/lithium/tests/mocks/template/MockHelper.php @@ -11,6 +11,7 @@ namespace lithium\tests\mocks\template; class MockHelper extends \lithium\template\Helper { protected $_strings = array('link' => '<a href="{:url}"{:options}>{:title}</a>'); + /** * Hack to expose protected properties for testing. * diff --git a/libraries/lithium/tests/mocks/template/MockView.php b/libraries/lithium/tests/mocks/template/MockView.php new file mode 100644 index 0000000..dfeacd4 --- /dev/null +++ b/libraries/lithium/tests/mocks/template/MockView.php @@ -0,0 +1,18 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2010, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace lithium\tests\mocks\template; + +class MockView extends \lithium\template\View { + + public function renderer() { + return $this->_config['renderer']; + } +} + +?> \ No newline at end of file diff --git a/libraries/lithium/tests/mocks/test/cases/MockSkipThrowsException.php b/libraries/lithium/tests/mocks/test/cases/MockSkipThrowsException.php index ee8fdb1..75719e1 100644 --- a/libraries/lithium/tests/mocks/test/cases/MockSkipThrowsException.php +++ b/libraries/lithium/tests/mocks/test/cases/MockSkipThrowsException.php @@ -8,7 +8,7 @@ namespace lithium\tests\mocks\test\cases; -use \Exception; +use Exception; class MockSkipThrowsException extends \lithium\test\Unit { diff --git a/libraries/lithium/tests/mocks/test/cases/MockTestErrorHandling.php b/libraries/lithium/tests/mocks/test/cases/MockTestErrorHandling.php index 8caa9f4..31f5cdb 100644 --- a/libraries/lithium/tests/mocks/test/cases/MockTestErrorHandling.php +++ b/libraries/lithium/tests/mocks/test/cases/MockTestErrorHandling.php @@ -8,7 +8,7 @@ namespace lithium\tests\mocks\test\cases; -use \Exception; +use Exception; class MockTestErrorHandling extends \lithium\test\Unit { diff --git a/libraries/lithium/util/Set.php b/libraries/lithium/util/Set.php index 156eeff..07a6ff1 100644 --- a/libraries/lithium/util/Set.php +++ b/libraries/lithium/util/Set.php @@ -547,7 +547,11 @@ class Set { return true; } if (is_string($conditions)) { +<<<<<<< HEAD return (bool) static::extract($data, $conditions); +======= + return (boolean) static::extract($data, $conditions); +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e } foreach ($conditions as $condition) { if ($condition === ':last') { diff --git a/libraries/lithium/util/String.php b/libraries/lithium/util/String.php index 4f0ed4e..c0a56dc 100644 --- a/libraries/lithium/util/String.php +++ b/libraries/lithium/util/String.php @@ -9,8 +9,8 @@ namespace lithium\util; -use \Closure; -use \Exception; +use Closure; +use Exception; /** * String manipulation utility class. Includes functionality for hashing, UUID generation, @@ -25,6 +25,7 @@ class String { const version4 = 64; // 01000000 Sets the version bit const clearVar = 63; // 00111111 Clears relevant bits of variant byte const varRFC = 128; // 10000000 The RFC 4122 variant +<<<<<<< HEAD /** * A file pointer towards urandom if available, else false @@ -411,6 +412,396 @@ class String { * * @return string The MD5 salt. **/ +======= + + /** + * A file pointer towards urandom if available, else false + * + * @var resource|false + */ + protected static $_urandom; + + /** + * Seeds the random generator if it has yet to be done. + * + * @return boolean Success. + */ + public static function seed() { + // Seeding more than once means less entropy, not more, so bail + if (isset(static::$_urandom)) { + return false; + } + + // Use urandom if the device is available + if (is_readable('/dev/urandom')) { + static::$_urandom = fopen('/dev/urandom', 'rb'); + // Else seed PHP's mt_rand() + } else { + $seed = function() { + list($usec, $sec) = explode(' ', microtime()); + $seed = (float) $sec + ((float) $usec * 100000); + if (function_exists('getmypid')) { + $seed .= getmypid(); + } + return $seed; + }; + mt_srand($seed()); + static::$_urandom = false; + } + + return true; + } + + /** + * Generates random bytes for use in UUIDs and password salts. + * + * The method seeds the random source automatically. It uses + * /dev/urandom if the latter is available; `md_rand()` if not. + * + * It can also be used to generate arbitrary bits: + * + * {{{ + * $bits = bin2hex(String::random(8)); // 64 bits + * }}} + * + * @param integer $bytes The number of bytes to generate + * @param string Random bytes + */ + public static function random($bytes) { + if (!isset(static::$_urandom)) { + static::seed(); + } + + if (static::$_urandom) { + $rand = fread(static::$_urandom, $bytes); + } else { + $rand = ''; + for ($i = 0; $i < $bytes; $i++) { + $rand .= chr(mt_rand(0, 255)); + } + } + + return $rand; + } + + /** + * Generates an RFC 4122-compliant version 4 UUID. + * + * @return string The string representation of an RFC 4122-compliant, version 4 UUID. + * @link http://www.ietf.org/rfc/rfc4122.txt + */ + public static function uuid() { + $uuid = static::random(16); + + // Set version + $uuid[6] = chr(ord($uuid[6]) & static::clearVer | static::version4); + + // Set variant + $uuid[8] = chr(ord($uuid[8]) & static::clearVar | static::varRFC); + + // Return the uuid's string representation + return bin2hex(substr($uuid, 0, 4)) . '-' + . bin2hex(substr($uuid, 4, 2)) . '-' + . bin2hex(substr($uuid, 6, 2)) . '-' + . bin2hex(substr($uuid, 8, 2)) . '-' + . bin2hex(substr($uuid, 10, 6)); + } + + /** + * Uses PHP's hashing functions to create a hash of the string provided, using the options + * specified. The default hash algorithm is SHA-512. + * + * @link http://php.net/manual/en/function.hash.php PHP Manual: hash() + * @link http://php.net/manual/en/function.hash-hmac.php PHP Manual: hash_hmac() + * @link http://php.net/manual/en/function.hash-algos.php PHP Manual: hash_algos() + * @param string $string The string to hash. + * @param array $options Supported options: + * - `'type'` _string_: Any valid hashing algorithm. See the `hash_algos()` function to + * determine which are available on your system. + * - `'salt'` _string_: A _salt_ value which, if specified, will be prepended to the + * string. + * - `'key'` _string_: If specified `hash_hmac()` will be used to hash the string, + * instead of `hash()`, with `'key'` being used as the message key. + * - `'raw'` _boolean_: If `true`, outputs the raw binary result of the hash operation. + * Defaults to `false`. + * @return string Returns a hashed string. + */ + public static function hash($string, array $options = array()) { + $defaults = array( + 'type' => 'sha512', + 'salt' => false, + 'key' => false, + 'raw' => false, + ); + $options += $defaults; + + if ($options['salt']) { + $string = $options['salt'] . $string; + } + + if ($options['key']) { + return hash_hmac($options['type'], $string, $options['key'], $options['raw']); + } + return hash($options['type'], $string, $options['raw']); + } + + /** + * Hashes a password using PHP's `crypt()` and an optional salt. If no + * salt is supplied, a cryptographically strong salt will be generated + * using `String::genSalt()`. + * + * Using this function is the proper way to hash a password. Using naive + * methods such as `String::hash()` is fine to check a file's integrity, + * but fundamentally insecure for passwords, due to the invariable lack + * of a cryptographically strong salt. + * + * Moreover, `String::hashPassword()`'s cryptographically strong salts + * ensure that: + * + * - Two identical passwords will not be hashed the same way. + * - `String::genSalt()`'s count interator can later be increased (assuming + * BF or XDES is available) within Lithium or your application, without + * invalidating existing password hashes. + * + * Usage: + * + * {{{ + * // Hash a password before storing it: + * $hashed = String::hashPassword($password); + * + * // Check a password by comparing it to its hashed value: + * $check = String::checkPassword($password, $hashed); + * + * // Use a stronger custom salt: + * $salt = String::genSalt('bf', 16); // 2^16 iterations + * $hashed = String::hashPassword($password, $salt); // Very slow + * $check = String::checkPassword($password, $hashed); // Very slow + * + * // Forward/backward compatibility + * $salt1 = String::genSalt('bf', 6); + * $salt2 = String::genSalt('bf', 12); + * $hashed1 = String::hashPassword($password, $salt1); // Fast + * $hashed2 = String::hashPassword($password, $salt2); // Slow + * $check1 = String::checkPassword($password, $hashed1); // True + * $check2 = String::checkPassword($password, $hashed2); // True + * }}} + * + * @see lithium\util\String::genSalt() + * @param string $password The password to hash. + * @param string $salt Optional. The salt string. + * @return string The hashed password. + * The result's length will be: + * - 60 chars for Blowfish hashes + * - 20 chars for XDES hashes + * - 34 chars for MD5 hashes + **/ + public static function hashPassword($password, $salt = null) { + return crypt($password, $salt ?: static::genSalt()); + } + + /** + * Compares a password and its hashed value using PHP's `crypt()`. + * + * @see lithium\util\String::hashPassword() + * @see lithium\util\String::genSalt() + * @param string $password The password to check + * @param string $hash The hashed password to compare + * @return boolean Whether the password is correct or not + */ + public static function checkPassword($password, $hash) { + return $hash == crypt($password, $hash); + } + + /** + * Generates a cryptographically strong salt, using the best available + * method (tries Blowfish, then XDES, and fallbacks to MD5), for use in + * `String::hashPassword()`. + * + * Blowfish and XDES are adaptive hashing algorithms. MD5 is not. Adaptive + * hashing algorithms are designed in such a way that when computers get + * faster, you can tune the algorithm to be slower by increasing the number + * of hash iterations, without introducing incompatibility with existing + * passwords. + * + * To pick an appropriate iteration count for adaptive algorithms, consider + * that the original DES crypt was designed to have the speed of 4 hashes + * per second on the hardware of that time. Slower than 4 hashes per second + * would probably dampen usability. Faster than 100 hashes per second is + * probably too fast. The defaults generate about 10 hashes per second + * using a dual-core 2.2GHz CPU. + * + * Note1: this salt generator is different from naive salt implementations + * (e.g. `md5(microtime())`) that are invariably found in OSS PHP applications, + * in that it uses all of the available bits of entropy for the supplied salt + * method. + * + * Note2: this method should not be used as custom salts, for instance in a + * custom password hasher. Indeed, salts are prefixed with information expected + * by PHP's `crypt()`. To get an arbitrarily long, cryptographically strong salt + * consisting in random sequences of alpha numeric characters, combine + * `String::random()` and `String::encode64()` instead. + * + * @link http://php.net/manual/en/function.crypt.php + * @link http://www.postgresql.org/docs/9.0/static/pgcrypto.html + * @see lithium\util\String::hashPassword() + * @param string $type The hash type. Optional. Defaults to '`bf`'. + * Supported values include: + * - `'bf'`: Blowfish (128 salt bits, adaptive, max 72 chars) + * - `'xdes'`: XDES (24 salt bits, adaptive, max 8 chars) + * - `'md5'`: MD5 (48 salt bits, non-adaptive, unlimited length) + * @param integer $count Optional. The base-2 logarithm of the iteration + * count, for adaptive algorithms. Defaults to: + * - `10` for Blowfish + * - `18` for XDES + * @return string The salt string. + */ + public static function genSalt($type = null, $count = null) { + switch (true) { + case CRYPT_BLOWFISH == 1 && (!$type || $type === 'bf'): + return static::_genSaltBF($count); + case CRYPT_EXT_DES == 1 && (!$type || $type === 'xdes'): + return static::_genSaltXDES($count); + default: + return static::_genSaltMD5(); + } + } + + /** + * Encodes bytes into an `./0-9A-Za-z` alphabet, for use as salt when + * hashing passwords. + * + * Note: this is not the same as RFC 1421, or `base64_encode()`, which + * uses an `+/0-9A-Za-z` alphabet. + * + * This function can be combined with `String::random()` to generate random + * sequences of `./0-9A-Za-z` characters: + * + * {{{ + * $salt = String::encode64(String::random(8)); // 64 bits + * }}} + * + * @see lithium\util\String::random() + * @param string $input The input bytes. + * @return string The same bytes in the `/.0-9A-Za-z` alphabet. + */ + public static function encode64($input) { + $base64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + $i = 0; + + $count = strlen($input); + $output = ''; + + do { + $value = ord($input[$i++]); + $output .= $base64[$value & 0x3f]; + + if ($i < $count) { + $value |= ord($input[$i]) << 8; + } + $output .= $base64[($value >> 6) & 0x3f]; + + if ($i++ >= $count) { + break; + } + if ($i < $count) { + $value |= ord($input[$i]) << 16; + } + $output .= $base64[($value >> 12) & 0x3f]; + + if ($i++ >= $count) { + break; + } + $output .= $base64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + /** + * Generates a Blowfish salt for use in `String::hashPassword()`. + * + * @param integer $count The base-2 logarithm of the iteration count. + * Defaults to `10`. Can be `4` to `31`. + * @return string $salt + */ + protected static function _genSaltBf($count = 10) { + $count = (integer) $count; + if ($count < 4 || $count > 31) { + $count = 10; + } + + // We don't use the encode64() method here because it could result + // in 2 bits less of entropy depending on the last char. + $base64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + $i = 0; + + $input = static::random(16); // 128 bits of salt + $output = ''; + + do { + $c1 = ord($input[$i++]); + $output .= $base64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $base64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $base64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $base64[$c1]; + $output .= $base64[$c2 & 0x3f]; + } while (1); + + return '$2a$' + // zeroize $count + . chr(ord('0') + $count / 10) . chr(ord('0') + $count % 10) + . '$' . $output; + } + + /** + * Generates an Extended DES salt for use in `String::hashPassword()`. + * + * @param integer $count The base-2 logarithm of the iteration count. + * Defaults to `18`. Can be `1` to `24`. 1 will be stripped + * from the non-log value, e.g. 2^18 - 1, to ensure we don't + * use a weak DES key. + * @return string The XDES salt. + */ + protected static function _genSaltXDES($count = 18) { + $count = (integer) $count; + if ($count < 1 || $count > 24) { + $count = 16; + } + + // Count should be odd to not reveal weak DES keys + $count = (1 << $count) - 1; + + $base64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + $output = '_' + // iterations + . $base64[$count & 0x3f] + . $base64[($count >> 6) & 0x3f] + . $base64[($count >> 12) & 0x3f] + . $base64[($count >> 18) & 0x3f] + // 24 bits of salt + . static::encode64(static::random(3)); + + return $output; + } + + /** + * Generates an MD5 salt for use in `String::hashPassword()`. + * + * @return string The MD5 salt. + */ +>>>>>>> fbe85ef24e87a52a74ec9569f00666225c0a117e protected static function _genSaltMD5() { $output = '$1$' // 48 bits of salt diff --git a/libraries/lithium/util/Validator.php b/libraries/lithium/util/Validator.php index dc195f0..48ad2d7 100644 --- a/libraries/lithium/util/Validator.php +++ b/libraries/lithium/util/Validator.php @@ -546,7 +546,7 @@ class Validator extends \lithium\core\StaticObject { */ public static function rule($rule, $value, $format = 'any', array $options = array()) { if (!isset(static::$_rules[$rule])) { - throw new InvalidArgumentException("Rule '{$rule}' is not a validation rule"); + throw new InvalidArgumentException("Rule `{$rule}` is not a validation rule."); } $defaults = isset(static::$_options[$rule]) ? static::$_options[$rule] : array(); $options = (array) $options + $defaults + static::$_options['defaults'];