diff --git a/.travis.yml b/.travis.yml index c27f36326..684b2b774 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,8 @@ language: php env: - - PHALCON_VERSION="1.3.5" - - PHALCON_VERSION="1.3.4" - - PHALCON_VERSION="1.3.3" - - PHALCON_VERSION="1.3.2" + - PHALCON_VERSION="2.0.x" + - PHALCON_VERSION="2.0.0" install: - tests/memcached.sh @@ -30,4 +28,4 @@ after_script: - php ocular.phar code-coverage:upload --format=php-clover coverage.clover services: - - memcached \ No newline at end of file + - memcached diff --git a/Library/Phalcon/Acl/Adapter/Redis.php b/Library/Phalcon/Acl/Adapter/Redis.php new file mode 100644 index 000000000..110aa9058 --- /dev/null +++ b/Library/Phalcon/Acl/Adapter/Redis.php @@ -0,0 +1,424 @@ +redis = $redis; + } + + public function setRedis($redis, $chainRedis = false) + { + $this->redis = $redis; + return $chainRedis ? $redis : $this; + } + + public function getRedis() + { + return $this->redis; + } + + /** + * {@inheritdoc} + * Example: + * $acl->addRole(new Phalcon\Acl\Role('administrator'), 'consultor'); + * $acl->addRole('administrator', 'consultor'); + * + * @param \Phalcon\Acl\Role|string $role + * @param string $accessInherits + * @return boolean + */ + public function addRole($role, $accessInherits = null) + { + if (!is_object($role)) { + $role = new Role($role, ucwords($role) . " Role"); + } + + $this->redis->hMset("roles", array($role->getName() => $role->getDescription())); + $this->redis->sAdd("accessList:$role:*:{$this->getDefaultAction()}}", "*"); + + if ($accessInherits) { + $this->addInherit($role->getName(), $accessInherits); + } + + return true; + } + + /** + * {@inheritdoc} + * + * @param string $roleName + * @param string $roleToInherit + * @throws \Phalcon\Acl\Exception + */ + public function addInherit($roleName, $roleToInherit) + { + $exists = $this->redis->hGet("roles", $roleName); + if (!$exists) { + throw new Exception("Role '" . $roleName . "' does not exist in the role list"); + } + + $this->redis->sAdd("rolesInherits:$roleName", $roleToInherit); + } + + /** + * {@inheritdoc} + * Example: + * + * //Add a resource to the the list allowing access to an action + * $acl->addResource(new Phalcon\Acl\Resource('customers'), 'search'); + * $acl->addResource('customers', 'search'); + * //Add a resource with an access list + * $acl->addResource(new Phalcon\Acl\Resource('customers'), array('create', 'search')); + * $acl->addResource('customers', array('create', 'search')); + * + * + * @param \Phalcon\Acl\Resource|string $resource + * @param array|string $accessList + * @return boolean + */ + public function addResource($resource, $accessList = null) + { + if (!is_object($resource)) { + $resource = new Resource($resource, ucwords($resource) . " Resource"); + } + $this->redis->hMset("resources", array($resource->getName() => $resource->getDescription())); + + if ($accessList) { + return $this->addResourceAccess($resource->getName(), $accessList); + } + + return true; + } + + /** + * {@inheritdoc} + * + * @param string $resourceName + * @param array|string $accessList + * @return boolean + * @throws \Phalcon\Acl\Exception + */ + public function addResourceAccess($resourceName, $accessList) + { + if (!$this->isResource($resourceName)) { + throw new Exception("Resource '" . $resourceName . "' does not exist in ACL"); + } + + $accessList = (is_string($accessList)) ? explode(' ', $accessList) : $accessList; + foreach ($accessList as $accessName) { + $this->redis->sAdd("resourcesAccesses:$resourceName", $accessName); + } + + return true; + } + + /** + * {@inheritdoc} + * + * @param string $roleName + * @return boolean + */ + public function isRole($roleName) + { + return $this->redis->hExists("roles", $roleName); + } + + /** + * {@inheritdoc} + * + * @param string $resourceName + * @return boolean + */ + public function isResource($resourceName) + { + return $this->redis->hExists("resources", $resourceName); + } + + public function isResourceAccess($resource, $access) + { + return $this->redis->sIsMember("resourcesAccesses:$resource", $access); + } + + /** + * {@inheritdoc} + * + * @return \Phalcon\Acl\Resource[] + */ + public function getResources() + { + $resources = array(); + + foreach ($this->redis->hGetAll("resources") as $name => $desc) { + $resources[] = new Resource($name, $desc); + } + + return $resources; + } + + /** + * {@inheritdoc} + * + * @return \Phalcon\Acl\Role[] + */ + public function getRoles() + { + $roles = array(); + + foreach ($this->redis->hGetAll("roles") as $name => $desc) { + $roles[] = new Role($name, $desc); + } + + return $roles; + } + + /** + * @param $role + * @return array + */ + public function getRoleInherits($role) + { + return $this->redis->sMembers("rolesInherits:$role"); + } + + public function getResourceAccess($resource) + { + return $this->redis->sMembers("resourcesAccesses:$resource"); + } + + /** + * {@inheritdoc} + * + * @param string $resource + * @param array|string $accessList + */ + public function dropResourceAccess($resource, $accessList) + { + if (!is_array($accessList)) { + $accessList = (array)$accessList; + } + array_unshift($accessList, "resourcesAccesses:$resource"); + call_user_func_array(array($this->redis, 'sRem'), $accessList); + } + + /** + * {@inheritdoc} + * You can use '*' as wildcard + * Example: + * + * //Allow access to guests to search on customers + * $acl->allow('guests', 'customers', 'search'); + * //Allow access to guests to search or create on customers + * $acl->allow('guests', 'customers', array('search', 'create')); + * //Allow access to any role to browse on products + * $acl->allow('*', 'products', 'browse'); + * //Allow access to any role to browse on any resource + * $acl->allow('*', '*', 'browse'); + * + * + * @param string $role + * @param string $resource + * @param array|string $access + */ + public function allow($role, $resource, $access) + { + if ($role !== '*' && $resource !== '*') { + $this->allowOrDeny($role, $resource, $access, Acl::ALLOW); + } + if ($role === '*' || empty($role)) { + $this->rolePermission($resource, $access, Acl::ALLOW); + } + if ($resource === '*' || empty($resource)) { + $this->resourcePermission($role, $access, Acl::ALLOW); + } + } + + /** + * @param $role + * @param $access + * @param $allowOrDeny + * @throws Exception + */ + protected function resourcePermission($role, $access, $allowOrDeny) + { + foreach ($this->getResources() as $resource) { + if ($role === '*' || empty($role)) { + $this->rolePermission($resource, $access, $allowOrDeny); + } else { + $this->allowOrDeny($role, $resource, $access, $allowOrDeny); + } + } + } + + /** + * @param $resource + * @param $access + * @param $allowOrDeny + * @throws Exception + */ + protected function rolePermission($resource, $access, $allowOrDeny) + { + foreach ($this->getRoles() as $role) { + if ($resource === '*' || empty($resource)) { + $this->resourcePermission($role, $access, $allowOrDeny); + } else { + $this->allowOrDeny($role, $resource, $access, $allowOrDeny); + } + } + } + + /** + * {@inheritdoc} + * You can use '*' as wildcard + * Example: + * + * //Deny access to guests to search on customers + * $acl->deny('guests', 'customers', 'search'); + * //Deny access to guests to search or create on customers + * $acl->deny('guests', 'customers', array('search', 'create')); + * //Deny access to any role to browse on products + * $acl->deny('*', 'products', 'browse'); + * //Deny access to any role to browse on any resource + * $acl->deny('*', '*', 'browse'); + * + * + * @param string $roleName + * @param string $resourceName + * @param array|string $access + * @return boolean + */ + public function deny($role, $resource, $access) + { + if ($role === '*' || empty($role)) { + $this->rolePermission($resource, $access, Acl::DENY); + } elseif ($resource === '*' || empty($resource)) { + $this->resourcePermission($role, $access, Acl::DENY); + } else { + $this->allowOrDeny($role, $resource, $access, Acl::DENY); + } + } + + /** + * {@inheritdoc} + * Example: + * + * //Does Andres have access to the customers resource to create? + * $acl->isAllowed('Andres', 'Products', 'create'); + * //Do guests have access to any resource to edit? + * $acl->isAllowed('guests', '*', 'edit'); + * + * + * @param string $role + * @param string $resource + * @param string $access + * + * @return bool + */ + public function isAllowed($role, $resource, $access) + { + if ($this->redis->sIsMember("accessList:$role:$resource:" . Acl::ALLOW, $access)) { + return Acl::ALLOW; + } + + if ($this->redis->exists("rolesInherits:$role")) { + $rolesInherits = $this->redis->sMembers("rolesInherits:$role"); + foreach ($rolesInherits as $role) { + if ($this->redis->sIsMember("accessList:$role:$resource:" . Acl::ALLOW, $access)) { + return Acl::ALLOW; + } + } + } + + /** + * Return the default access action + */ + + return $this->getDefaultAction(); + } + + /** + * @param $roleName + * @param $resourceName + * @param $accessName + * @param $action + * @return bool + * @throws Exception + */ + protected function setAccess($roleName, $resourceName, $accessName, $action) + { + /** + * Check if the access is valid in the resource + */ + if ($this->isResourceAccess($resourceName, $accessName)) { + if (!$this->setNXAccess) { + throw new Exception( + "Access '" . $accessName . "' does not exist in resource '" . $resourceName . "' in ACL" + ); + } + $this->addResourceAccess($resourceName, $accessName); + } + $this->redis->sAdd("accessList:$roleName:$resourceName:$action", $accessName); + $accessList = "accessList:$roleName:$resourceName"; + + // remove first if exists + foreach (array(1, 2) as $act) { + $this->redis->sRem("$accessList:$act", $accessName); + $this->redis->sRem("$accessList:$act", "*"); + } + + $this->redis->sAdd("$accessList:$action", $accessName); + + $this->redis->sAdd("$accessList:{$this->getDefaultAction()}", "*"); + + return true; + } + + /** + * Inserts/Updates a permission in the access list + * + * @param string $roleName + * @param string $resourceName + * @param array|string $access + * @param integer $action + * @throws \Phalcon\Acl\Exception + */ + protected function allowOrDeny($roleName, $resourceName, $access, $action) + { + if (!$this->isRole($roleName)) { + throw new Exception('Role "' . $roleName . '" does not exist in the list'); + } + if (!$this->isResource($resourceName)) { + throw new Exception('Resource "' . $resourceName . '" does not exist in the list'); + } + $access = ($access === '*' || empty($access)) ? $this->getResourceAccess($resourceName) : $access; + if (is_array($access)) { + foreach ($access as $accessName) { + $this->setAccess($roleName, $resourceName, $accessName, $action); + } + } else { + $this->setAccess($roleName, $resourceName, $access, $action); + } + } +} diff --git a/Library/Phalcon/Config/Adapter/Json.php b/Library/Phalcon/Config/Adapter/Json.php deleted file mode 100644 index 113863e1a..000000000 --- a/Library/Phalcon/Config/Adapter/Json.php +++ /dev/null @@ -1,32 +0,0 @@ -phalcon->controllersDir; echo $config->database->username; echo $config->database->password; -``` - -Json ----- -Reads Json markup files and converts it to Phalcon\Config objects. For the following configuration file: - -``` - -{ - "database": { - "adapter": "Mysql", - "host": "localhost", - "username": "scott", - "password": "dbpassword", - "dbname": "test_db" - }, - - "phalcon": { - "controllersDir": "../app/controllers/", - "modelsDir": "../app/models/", - "viewsDir": "../app/views/" - } -} - -``` - -You can read it as follows: - -```php - -$config = new Phalcon\Config\Adapter\Json('path/config.json'); - -echo $config->phalcon->controllersDir; -echo $config->database->username; -echo $config->database->password; - -``` +``` \ No newline at end of file diff --git a/Library/Phalcon/Debug/Dump.php b/Library/Phalcon/Debug/Dump.php deleted file mode 100644 index 7809f6163..000000000 --- a/Library/Phalcon/Debug/Dump.php +++ /dev/null @@ -1,149 +0,0 @@ -flushBuffer = $flushBuffer; - } - - /** - * Get the current value of the debug output environment. - * This defaults to the value of PHP_SAPI. - * - * @return string; - */ - public static function getSapi() - { - if (static::$sapi === null) { - static::$sapi = PHP_SAPI; - } - - return static::$sapi; - } - - /** - * Sets sapi value - * - * @param string $sapi - */ - public static function setSapi($sapi) - { - static::$sapi = $sapi; - } - - /** - * Sets output flag. - * - * @param boolean $flag - */ - public static function setOutput($flag) - { - static::$output = $flag; - } - - /** - * Gets current value of output flag. - * - * @return boolean - */ - public static function getOutput() - { - return static::$output; - } - - /** - * Debug helper function. This is a wrapper for var_dump|xdebug_var_dump that adds - * the
 tags, cleans up newlines and indents, adds file name and line number info
-     * and runs htmlentities() before output.
-     *
-     * @param  mixed   $var        The variable to dump.
-     * @param  boolean $outputDump Overrides self::$output flag
-     * @return string
-     */
-    public function dump($var, $outputDump = null)
-    {
-        // add file and line on which Dump was called
-        $backtrace = debug_backtrace();
-        $label = 'Dump - File: ' . $backtrace[0]['file'] . ', Line: ' . $backtrace[0]['line'];
-
-        // var_dump the variable into a buffer and keep the output
-        ob_start();
-        if ($this->xdebugDumpExists()) {
-            xdebug_var_dump($var);
-        } else {
-            var_dump($var);
-        }
-
-        $output = ob_get_clean();
-
-        // neaten the newlines and indents
-        $output = preg_replace("/\]\=\>\n(\s+)/m", "] => ", $output);
-        if (static::getSapi() == 'cli') {
-            $label = $label . PHP_EOL;
-            $output = PHP_EOL . $label
-                    . PHP_EOL . $output
-                    . PHP_EOL;
-        } else {
-            $label = $label . PHP_EOL;
-            $output = htmlentities($output, ENT_QUOTES, 'UTF-8');
-            $output = '
'
-                    . $label
-                    . $output
-                    . '
'; - } - - $echo = self::$output; - if (is_bool($outputDump)) { - $echo = $outputDump; - } - - if ($echo) { - echo $output; - if ($this->flushBuffer) { - ob_flush(); - } - } - - return $output; - } - - /** - * Checks if xdebug_var_dump function is available - * - * @return bool - */ - protected function xdebugDumpExists() - { - return function_exists('xdebug_var_dump'); - } -} diff --git a/Library/Phalcon/Debug/README.md b/Library/Phalcon/Debug/README.md deleted file mode 100644 index 6a96cb4f4..000000000 --- a/Library/Phalcon/Debug/README.md +++ /dev/null @@ -1,95 +0,0 @@ -Phalcon\Debug -=================== - -Debug help - -Dump --------- -This utility class is meant to be used for dumping variables, heavily inspired by [Zend Framework's \Zend\Debug\Debug class](http://framework.zend.com/apidoc/2.1/classes/Zend.Debug.Debug.html). -Outputs var using var_dump() or xdebug_var_dump() and, if outputted, flushes Phalcons default output buffer. -Also, writes name of file and line from which it was called. - -Basic usage: - -```php - -(new \Phalcon\Debug\Dump())->dump($varToDump); - -``` - -Can be set to return output instead of echoing it using \Phalcon\Debug\Dump::setOutput() method - -```php - -if (ENVIRONMENT === 'production') { - \Phalcon\Debug\Dump::setOutput(false); -} - -// will return dump instead of echoing it -(new \Phalcon\Debug\Dump())->dump($varToDump); - -``` - -If, for any reason, there is need to override \Phalcon\Debug\Dump::$output value, -behavior can be overriden by setting second argument of \Phalcon\Debug\Dump::dump method to true or false - -```php - -\Phalcon\Debug\Dump::setOutput(false); - -// will return dump instead of echoing it -(new \Phalcon\Debug\Dump())->dump($varToDump); - -// this will echo dump -(new \Phalcon\Debug\Dump())->dump($varToDump, true); - -``` - -and - -```php - -\Phalcon\Debug\Dump::setOutput(true); - -// this will echo dump -(new \Phalcon\Debug\Dump())->dump($varToDump); - -// will return dump instead of echoing it -(new \Phalcon\Debug\Dump())->dump($varToDump, false); - -``` - -Convenient way of setting dump application wide (instead of making instance every time its called) can be using Phalcons DI: - -```php - -$this->getDI()->setShared('dump', function() { - return new \Phalcon\Debug\Dump(); -}); - -// ... later in application ... -$this->getDI()->getShared('dump')->dump($varToDump); // echoes dump - -``` - -... or set \Phalcon\Debug\Dump as a service to be able to easily consume it within controller ... - -```php - -// services.php -$di['debug'] = new \Phalcon\Debug\Dump(); - -// later in controller ... -$this->debug->dump($varToDump); - -``` - -If calling ob_flush() every time after var is dumped is not wanted behaviour it can be changed by setting false in object constructor: -(WARNING: if dump is echoed anywhere in Phalcon application, except in view, and ob_flush() is not called, it will not be seen due to Phalcons output buffering) - -```php - -// ob_flush() will not be called -(new \Phalcon\Debug\Dump(false))->dump($varToDump); - -``` diff --git a/Library/Phalcon/Logger/README.md b/Library/Phalcon/Logger/README.md index 4ff6994ae..e7f243582 100644 --- a/Library/Phalcon/Logger/README.md +++ b/Library/Phalcon/Logger/README.md @@ -41,20 +41,6 @@ CREATE TABLE `logs` ( ) ``` -Firephp -------- -Adapter to send messages to [Firebug](https://getfirebug.com/). You need -the [Firephp](http://www.firephp.org/) extension installed in your browser. - -```php -$logger = new Phalcon\Logger\Adapter\Firephp('debug', null); - -$logger->log('Plain Message'); -$logger->info('Info Message'); -$logger->warning('Warn Message'); -$logger->error('Error Message'); -``` - Firelogger ---------- Adapter to send messages to the [Firelogger](http://firelogger.binaryage.com/) console inside the [Firebug](https://getfirebug.com/) in your browser. diff --git a/Library/Phalcon/Mvc/Model/Behavior/NestedSet.php b/Library/Phalcon/Mvc/Model/Behavior/NestedSet.php index 84c2f8e39..cd0f186f0 100644 --- a/Library/Phalcon/Mvc/Model/Behavior/NestedSet.php +++ b/Library/Phalcon/Mvc/Model/Behavior/NestedSet.php @@ -5,6 +5,7 @@ use Phalcon\Mvc\Model\Behavior; use Phalcon\Mvc\Model\BehaviorInterface; use Phalcon\Mvc\Model; +use Phalcon\Mvc\ModelInterface; class NestedSet extends Behavior implements BehaviorInterface { @@ -19,7 +20,7 @@ class NestedSet extends Behavior implements BehaviorInterface private $deleted = false; - public function __construct($options) + public function __construct($options = null) { if (isset($options['hasManyRoots'])) { $this->hasManyRoots = (bool) $options['hasManyRoots']; @@ -46,7 +47,7 @@ public function __construct($options) } } - public function notify($eventType, $model) + public function notify($eventType, ModelInterface $model) { $message = 'You should not use this method when NestedSetBehavior attached. Use the methods of behavior.'; switch ($eventType) { @@ -60,7 +61,7 @@ public function notify($eventType, $model) } } - public function missingMethod($model, $method, $arguments = null) + public function missingMethod(ModelInterface $model, $method, $arguments = null) { if (!method_exists($this, $method)) { return null; diff --git a/Library/Phalcon/Mvc/Model/Validator/Between.php b/Library/Phalcon/Mvc/Model/Validator/Between.php index ada6f7e38..ec4dcf217 100644 --- a/Library/Phalcon/Mvc/Model/Validator/Between.php +++ b/Library/Phalcon/Mvc/Model/Validator/Between.php @@ -1,7 +1,7 @@ */ -class Between extends ModelValidator implements ValidatorInterface +class Between extends Validator implements ValidatorInterface { /** * {@inheritdoc} @@ -44,12 +44,8 @@ class Between extends ModelValidator implements ValidatorInterface * @return boolean * @throws Exception */ - public function validate($record) + public function validate(ModelInterface $record) { - if (false === is_object($record) || false === $record instanceof ModelInterface) { - throw new Exception('Invalid parameter type.'); - } - $field = $this->getOption('field'); if (false === is_string($field)) { diff --git a/Library/Phalcon/Mvc/Model/Validator/CardNumber.php b/Library/Phalcon/Mvc/Model/Validator/CardNumber.php index aeaef4202..f7d89dad8 100644 --- a/Library/Phalcon/Mvc/Model/Validator/CardNumber.php +++ b/Library/Phalcon/Mvc/Model/Validator/CardNumber.php @@ -1,9 +1,10 @@ */ -class CardNumber extends \Phalcon\Mvc\Model\Validator +class CardNumber extends Validator implements ValidatorInterface { const AMERICAN_EXPRESS = 0; // 34, 37 const MASTERCARD = 1; // 51-55 const VISA = 2; // 4 - public function validate($record) + public function validate(ModelInterface $record) { - if (false === $record instanceof ModelInterface && false === $record instanceof CollectionInterface) { - throw new Exception('Invalid parameter type.'); - } - $field = $this->getOption('field'); if (false === is_string($field)) { diff --git a/Library/Phalcon/Mvc/Model/Validator/ConfirmationOf.php b/Library/Phalcon/Mvc/Model/Validator/ConfirmationOf.php index 3e78e1551..1b6d4627f 100644 --- a/Library/Phalcon/Mvc/Model/Validator/ConfirmationOf.php +++ b/Library/Phalcon/Mvc/Model/Validator/ConfirmationOf.php @@ -1,13 +1,16 @@ getOption('field'); $fieldConfirmation = $this->getOption('field_confirmation'); diff --git a/Library/Phalcon/Mvc/Model/Validator/Decimal.php b/Library/Phalcon/Mvc/Model/Validator/Decimal.php index b7c270a70..1c846b04f 100644 --- a/Library/Phalcon/Mvc/Model/Validator/Decimal.php +++ b/Library/Phalcon/Mvc/Model/Validator/Decimal.php @@ -1,7 +1,7 @@ */ -class Decimal extends ModelValidator implements ValidatorInterface +class Decimal extends Validator implements ValidatorInterface { /** * {@inheritdoc} @@ -50,10 +50,6 @@ class Decimal extends ModelValidator implements ValidatorInterface */ public function validate(ModelInterface $record) { - if (false === is_object($record) || false === $record instanceof ModelInterface) { - throw new Exception('Invalid parameter type.'); - } - $field = $this->getOption('field'); if (false === is_string($field)) { diff --git a/Library/Phalcon/Mvc/Model/Validator/IP.php b/Library/Phalcon/Mvc/Model/Validator/IP.php index f55de556e..769df3d9a 100644 --- a/Library/Phalcon/Mvc/Model/Validator/IP.php +++ b/Library/Phalcon/Mvc/Model/Validator/IP.php @@ -1,9 +1,10 @@ */ -class IP extends \Phalcon\Mvc\Model\Validator +class IP extends Validator implements ValidatorInterface { const VERSION_4 = FILTER_FLAG_IPV4; const VERSION_6 = FILTER_FLAG_IPV6; - public function validate($record) + public function validate(ModelInterface $record) { - if (false === is_object($record)) { - throw new Exception('Invalid parameter type.'); - } - - if (false === ($record instanceof ModelInterface || $record instanceof CollectionInterface)) { - throw new Exception('Invalid parameter type.'); - } - $field = $this->getOption('field'); if (false === is_string($field)) { diff --git a/Library/Phalcon/Paginator/Pager.php b/Library/Phalcon/Paginator/Pager.php index e142efdbc..8950296fd 100644 --- a/Library/Phalcon/Paginator/Pager.php +++ b/Library/Phalcon/Paginator/Pager.php @@ -11,8 +11,6 @@ */ namespace Phalcon\Paginator; -use Phalcon\Paginator\AdapterInterface; - /** * \Phalcon\Paginator\Pager * Pager object is a navigation menu renderer based on doctrine1 pager object. @@ -29,13 +27,6 @@ class Pager implements \IteratorAggregate, \Countable */ protected $paginateResult = null; - /** - * Object that detects pages range. - * - * @var \Phalcon\Paginator\Range - */ - protected $range = null; - /** * Array with options. * @@ -46,18 +37,24 @@ class Pager implements \IteratorAggregate, \Countable /** * Current rows limit (if provided) * - * @var int|null + * @var integer|null */ protected $limit = null; /** * Class constructor. * - * @param \Phalcon\Paginator\AdapterInterface $adapter - * @param array $options { - * @type string $rangeType How to make the range: Jumping or Sliding - * @type integer $rangeChunkLength Range window size - * } + * Consumes Phalcon paginator adapter and options array. + * Option keys: + * - rangeClass: Class name which determines scrolling style type (e.g. Phalcon\Paginator\Pager\Range\Sliding). + * Defaults to "Phalcon\Paginator\Pager\Range\Sliding". + * - rangeLength: Size of range to be used. Default size is 10. + * - layoutClass: Used with getLayout() method. Defaults to "Phalcon\Paginator\Pager\Layout". + * - urlMask: Required with getLayout() method. + * + * @param \Phalcon\Paginator\AdapterInterface $adapter Phalcon paginator adapter + * @param array $options options array + * */ public function __construct(AdapterInterface $adapter, array $options = array()) { @@ -73,7 +70,7 @@ public function __construct(AdapterInterface $adapter, array $options = array()) /** * Get current rows limit (if provided) * - * @return int|null + * @return integer|null */ public function getLimit() { @@ -143,19 +140,11 @@ public function getLastPage() /** * Returns the layout object. * - * @return \Phalcon\Paginator\Layout - * @throws \RuntimeException + * @return \Phalcon\Paginator\Pager\Layout + * @throws \RuntimeException in case options are not properly set */ public function getLayout() { - if (!array_key_exists('rangeClass', $this->options)) { - $this->options['rangeClass'] = 'Phalcon\Paginator\Pager\Range\Sliding'; - } - - if (!array_key_exists('rangeLength', $this->options)) { - $this->options['rangeLength'] = 10; - } - if (!array_key_exists('layoutClass', $this->options)) { $this->options['layoutClass'] = 'Phalcon\Paginator\Pager\Layout'; } @@ -164,22 +153,37 @@ public function getLayout() throw new \RuntimeException('You must provide option "urlMask"'); } - $range = null; - try { - $range = new $this->options['rangeClass']($this, $this->options['rangeLength']); - } catch (\Exception $e) { - throw new \RuntimeException(sprintf('Unable to find range class "%s"', $this->options['rangeClass'])); - } + $range = null; + $rangeClass = $this->getRangeClass(); + $rangeLength = $this->getRangeLength(); - $layout = null; + if (!class_exists($rangeClass)) { + throw new \RuntimeException(sprintf('Unable to find range class "%s"', $rangeClass)); + } - try { - $layout = new $this->options['layoutClass']($this, $range, $this->options['urlMask']); - } catch (\Exception $e) { + if (!class_exists($this->options['layoutClass'])) { throw new \RuntimeException(sprintf('Unable to find layout "%s"', $this->options['layoutClass'])); } - return $layout; + return new $this->options['layoutClass']( + $this, + new $rangeClass($this, $rangeLength), + $this->options['urlMask'] + ); + } + + /** + * Returns array of page numbers that are in range of slider. + * + * @return array array of page numbers + */ + public function getPagesInRange() + { + /** @var \Phalcon\Paginator\Pager\Range $range */ + $rangeClass = $this->getRangeClass(); + $range = new $rangeClass($this, $this->getRangeLength()); + + return $range->getRange(); } /** @@ -205,4 +209,32 @@ public function count() { return intval($this->paginateResult->total_items); } + + /** + * RangeClass option getter. + * + * @return string range class name + */ + protected function getRangeClass() + { + if (!array_key_exists('rangeClass', $this->options)) { + $this->options['rangeClass'] = 'Phalcon\Paginator\Pager\Range\Sliding'; + } + + return $this->options['rangeClass']; + } + + /** + * RangeLength option getter. + * + * @return integer range length + */ + protected function getRangeLength() + { + if (!array_key_exists('rangeLength', $this->options)) { + $this->options['rangeLength'] = 10; + } + + return (int) $this->options['rangeLength']; + } } diff --git a/Library/Phalcon/Paginator/Pager/Layout.php b/Library/Phalcon/Paginator/Pager/Layout.php index 874fe9a33..d4f471222 100644 --- a/Library/Phalcon/Paginator/Pager/Layout.php +++ b/Library/Phalcon/Paginator/Pager/Layout.php @@ -12,7 +12,6 @@ namespace Phalcon\Paginator\Pager; use Phalcon\Paginator\Pager; -use Phalcon\Paginator\Pager\Range; /** * \Phalcon\Paginator\Pager\Layout @@ -31,7 +30,7 @@ class Layout /** * Ranges generator. * - * @var \Phalcon\Paginator\Range + * @var \Phalcon\Paginator\Pager\Range */ protected $range = null; diff --git a/Library/Phalcon/Paginator/Pager/Range/Sliding.php b/Library/Phalcon/Paginator/Pager/Range/Sliding.php index 6e4834404..2f43bd79a 100644 --- a/Library/Phalcon/Paginator/Pager/Range/Sliding.php +++ b/Library/Phalcon/Paginator/Pager/Range/Sliding.php @@ -39,8 +39,8 @@ public function getRange() $chunk = $pages; } - $chunkStart = $page - (floor($chunk / 2)); - $chunkEnd = $page + (ceil($chunk / 2) - 1); + $chunkStart = (int) ($page - (floor($chunk / 2))); + $chunkEnd = (int) ($page + (ceil($chunk / 2) - 1)); if ($chunkStart < 1) { $adjust = 1 - $chunkStart; diff --git a/Library/Phalcon/Session/Adapter/Memcache.php b/Library/Phalcon/Session/Adapter/Memcache.php deleted file mode 100755 index 2a202fabf..000000000 --- a/Library/Phalcon/Session/Adapter/Memcache.php +++ /dev/null @@ -1,226 +0,0 @@ - - */ -namespace Phalcon\Session\Adapter; - -use Phalcon; - -/** - * Memcache session adapter for Phalcon framework - * - * @category Phalcon - * @package Phalcon_Session_Adapter_Memcache - */ -class Memcache extends Phalcon\Session\Adapter implements Phalcon\Session\AdapterInterface -{ - /** - * Default option for memcache port - * - * @var integer - */ - const DEFAULT_OPTION_PORT = 11211; - - /** - * Default option for session lifetime - * - * @var integer - */ - const DEFAULT_OPTION_LIFETIME = 8600; - - /** - * Default option for persistent session - * - * @var boolean - */ - const DEFAULT_OPTION_PERSISTENT = false; - - /** - * Default option for prefix of sessionId's - * - * @var string - */ - const DEFAULT_OPTION_PREFIX = ''; - - /** - * Contains the memcache instance - * - * @var \Phalcon\Cache\Backend\Memcache - */ - protected $memcacheInstance = null; - - /** - * Class constructor. - * - * @param null|array $options - * @throws Phalcon\Session\Exception - */ - public function __construct($options = null) - { - if (!is_array($options)) { - throw new Phalcon\Session\Exception("No configuration given"); - } - - if (!isset($options["host"])) { - throw new Phalcon\Session\Exception("No session host given in options"); - } - - if (!isset($options["port"])) { - $options["port"] = self::DEFAULT_OPTION_PORT; - } - - if (!isset($options["lifetime"])) { - $options["lifetime"] = self::DEFAULT_OPTION_LIFETIME; - } - - if (!isset($options["persistent"])) { - $options["persistent"] = self::DEFAULT_OPTION_PERSISTENT; - } - - if (!isset($options["prefix"])) { - $options["prefix"] = self::DEFAULT_OPTION_PREFIX; - } - - session_set_save_handler( - array($this, 'open'), - array($this, 'close'), - array($this, 'read'), - array($this, 'write'), - array($this, 'destroy'), - array($this, 'gc') - ); - - parent::__construct($options); - } - - /** - * {@inheritdoc} - * - * @return boolean - */ - public function open() - { - return true; - } - - /** - * {@inheritdoc} - * - * @return boolean - */ - public function close() - { - return true; - } - - /** - * {@inheritdoc} - * - * @param string $sessionId - * @return mixed - */ - public function read($sessionId) - { - return $this->getMemcacheInstance()->get( - $this->getSessionId($sessionId), - $this->getOption('lifetime') - ); - } - - /** - * {@inheritdoc} - * - * @param string $sessionId - * @param string $data - */ - public function write($sessionId, $data) - { - $this->getMemcacheInstance()->save( - $this->getSessionId($sessionId), - $data, - $this->getOption('lifetime') - ); - } - - /** - * {@inheritdoc} - * - * @param string $sessionId - * @return boolean - */ - public function destroy($session_id = null) - { - if (is_null($session_id)) { - $session_id = $this->getId(); - } - - return $this->getMemcacheInstance()->delete($this->getSessionId($session_id)); - } - - /** - * {@inheritdoc} - */ - public function gc() - { - } - - /** - * {@inheritdoc} - * - * @param string $key - * @return mixed - */ - public function getOption($key) - { - $options = $this->getOptions(); - if (!isset($options[$key])) { - return null; - } - - return $options[$key]; - } - - /** - * Returns the memcache instance. - * - * @return \Phalcon\Cache\Backend\Memcache - */ - protected function getMemcacheInstance() - { - if ($this->memcacheInstance === null) { - $this->memcacheInstance = new Phalcon\Cache\Backend\Memcache( - new Phalcon\Cache\Frontend\Data(array("lifetime" => $this->getOption("lifetime"))), - array( - 'host' => $this->getOption('host'), - 'port' => $this->getOption('port'), - 'persistent' => $this->getOption('persistent') - ) - ); - } - - return $this->memcacheInstance; - } - - /** - * Returns the sessionId with prefix - * - * @param string $sessionId - * @return string - */ - protected function getSessionId($sessionId) - { - return (strlen($this->getOption('prefix')) > 0) - ? $this->getOption('prefix') . '_' . $sessionId - : $sessionId; - } -} diff --git a/Library/Phalcon/Session/Adapter/README.md b/Library/Phalcon/Session/Adapter/README.md index 73bb5c279..fdf6e2fd5 100644 --- a/Library/Phalcon/Session/Adapter/README.md +++ b/Library/Phalcon/Session/Adapter/README.md @@ -43,29 +43,6 @@ This adapter uses the following table to store the data: ) ``` - -Memcache ---------- -This adapter uses a Memcache backend to store session data: - -```php - -$di->set('session', function(){ - - $memcache = new Phalcon\Session\Adapter\Memcache(array( - 'host' => '127.0.0.1', // mandatory - 'port' => 11211, // optional (standard: 11211) - 'lifetime' => 8600, // optional (standard: 8600) - 'prefix' => 'my-app' // optional (standard: [empty_string]), means memcache key is my-app_31231jkfsdfdsfds3 - 'persistent' => false // optional (standard: false) - )); - $memcache->start(); - return $memcache; -}); - -``` - - Mongo ----- This adapter uses a Mongo database backend to store session data: diff --git a/Library/Phalcon/Translate/Adapter/Csv.php b/Library/Phalcon/Translate/Adapter/Csv.php deleted file mode 100644 index f6d1a57f3..000000000 --- a/Library/Phalcon/Translate/Adapter/Csv.php +++ /dev/null @@ -1,75 +0,0 @@ - ';', - 'length' => 0, - 'enclosure' => '"', - ); - - $options = array_merge($default, $options); - if (false === ($file = @fopen($options['file'], 'rb'))) { - throw new \Exception('Error opening translation file "' . $options['file'] . '".'); - } - - while (false !== ($data = fgetcsv($file, $options['length'], $options['delimiter'], $options['enclosure']))) { - if (substr($data[0], 0, 1) === '#' || !isset($data[1])) { - continue; - } - - $this->translate[$data[0]] = $data[1]; - } - - @fclose($file); - } - - /** - * {@inheritdoc} - * - * @param string $index - * @param array $placeholders - * @return string - */ - public function query($index, $placeholders = null) - { - if (!$this->exists($index)) { - return $index; - } - - return self::setPlaceholders($this->translate[$index], $placeholders); - } - - /** - * {@inheritdoc} - * - * @param string $index - * @return boolean - */ - public function exists($index) - { - return isset($this->translate[$index]); - } -} diff --git a/Library/Phalcon/Translate/Adapter/Gettext.php b/Library/Phalcon/Translate/Adapter/Gettext.php deleted file mode 100644 index 99b0f0515..000000000 --- a/Library/Phalcon/Translate/Adapter/Gettext.php +++ /dev/null @@ -1,380 +0,0 @@ - | - | Eduar Carvajal | - +------------------------------------------------------------------------+ -*/ -namespace Phalcon\Translate\Adapter; - -use Phalcon\Translate\Adapter; -use Phalcon\Translate\AdapterInterface; -use Phalcon\Translate\Exception; - -class Gettext extends Base implements AdapterInterface -{ - - /** - * @var array - */ - protected $domains = array(); - - /** - * @var string - */ - protected $defaultDomain; - - /** - * Class constructor. - * - * @param array $options Required options: - * (string) locale - * (string|array) file - * (string) directory - * ~ or ~ - * (array) domains (instead of file and directory), - * where keys are domain names and - * values their respective directories. - * - * @throws \Phalcon\Translate\Exception - */ - public function __construct($options) - { - if (!is_array($options)) { - throw new Exception('Invalid options'); - } - - if (!isset($options['locale'])) { - throw new Exception('Parameter "locale" is required'); - } - - putenv("LC_ALL=" . $options['locale']); - setlocale(LC_ALL, $options['locale']); - - $this->prepareOptions($options); - - textdomain($this->defaultDomain); - } - - /** - * Validator for constructor - * - * @param array $options - * - */ - protected function prepareOptions($options) - { - if (isset($options['domains'])) { - $this->prepareOptionsWithDomain($options); - } else { - $this->prepareOptionsWithoutDomain($options); - } - } - - /** - * Validator for gettext with domains - * - * @param array $options - * - * @throws \Phalcon\Translate\Exception - */ - private function prepareOptionsWithDomain($options) - { - if (!is_array($options['domains'])) { - throw new Exception('Parameter "domains" must be an array.'); - } - unset($options['file']); - unset($options['directory']); - - foreach ($options['domains'] as $domain => $dir) { - bindtextdomain($domain, $dir); - } - // set the first domain as default - reset($options['domains']); - $this->defaultDomain = key($options['domains']); - // save list of domains - $this->domains = array_keys($options['domains']); - } - - /** - * Validator for gettext without domains - * - * @param array $options - * - * @throws \Phalcon\Translate\Exception - */ - private function prepareOptionsWithoutDomain($options) - { - self::validateOptionsWithoutDomain($options); - if (!is_array($options['file'])) { - $options['file'] = array($options['file']); - } - - foreach ($options['file'] as $domain) { - bindtextdomain($domain, $options['directory']); - } - - // set the first domain as default - $this->defaultDomain = reset($options['file']); - $this->domains = $options['file']; - - return $options; - } - - /** - * {@inheritdoc} - * - * @param string $index - * @param array $placeholders - * @param string $domain - * - * @return string - */ - public function query($index, $placeholders = null, $domain = null) - { - if ($domain === null) { - $translation = gettext($index); - } else { - $translation = dgettext($domain, $index); - } - - return self::setPlaceholders($translation, $placeholders); - } - - /** - * {@inheritdoc} - * - * @param string $msgid - * @param string $msgctxt Optional. If ommitted or NULL, - * this method behaves as query(). - * @param array $placeholders Optional. - * @param string $category Optional. Specify the locale category. - * Defaults to LC_MESSAGES - * - * @return string - * @throws \InvalidArgumentException - */ - public function cquery($msgid, $msgctxt = null, $placeholders = null, $category = LC_MESSAGES, $domain = null) - { - if ($msgctxt === null) { - return $this->query($msgid, $placeholders, $domain); - } - - $this->setDomain($domain); - - $contextString = "{$msgctxt}\004{$msgid}"; - $translation = dcgettext($domain, $contextString, $category); - - if ($translation == $contextString) { - $translation = $msgid; - } - - return self::setPlaceholders($translation, $placeholders); - } - - /** - * Returns the translation related to the given key and context (msgctxt). - * This is an alias to cquery(). - * - * @param string $msgid - * @param string $msgctxt Optional. - * @param array $placeholders Optional. - * @param integer $category Optional. Specify the locale category. - * Defaults to LC_MESSAGES - * - * @return string - */ - // @codingStandardsIgnoreStart - public function __($msgid, $msgctxt = null, $placeholders = null, $category = LC_MESSAGES) - { - return $this->cquery($msgid, $msgctxt, $placeholders, $category); - } - // @codingStandardsIgnoreEnd - - /** - * Returns the translation related to the given key - * and context (msgctxt) from a specific domain. - * - * @param string $domain - * @param string $msgid - * @param string $msgctxt Optional. - * @param array $placeholders Optional. - * @param integer $category Optional. Specify the locale category. Defaults to LC_MESSAGES - * - * @return string - */ - public function dquery($domain, $msgid, $msgctxt = null, $placeholders = null, $category = LC_MESSAGES) - { - return $this->cquery($msgid, $msgctxt, $placeholders, $category, $domain); - } - - /** - * {@inheritdoc} - * - * @param string $msgid1 - * @param string $msgid2 - * @param integer $count - * @param array $placeholders - * @param string $domain - * - * @return string - */ - public function nquery($msgid1, $msgid2, $count, $placeholders = null, $domain = null) - { - self::validateCount($count); - if ($domain === null) { - $translation = ngettext($msgid1, $msgid2, $count); - } else { - $translation = dngettext($domain, $msgid1, $msgid2, $count); - } - - return self::setPlaceholders($translation, $placeholders); - } - - /** - * {@inheritdoc} - * - * @param string $msgid1 - * @param string $msgid2 - * @param integer $count - * @param string $msgctxt Optional. If ommitted or NULL, this method behaves as nquery(). - * @param array $placeholders Optional. - * @param string $category Optional. Specify the locale category. Defaults to LC_MESSAGES - * @param string $domain Optional. - * - * @return string - * @throws \InvalidArgumentException - */ - public function cnquery( - $msgid1, - $msgid2, - $count, - $msgctxt = null, - $placeholders = null, - $category = LC_MESSAGES, - $domain = null - ) { - self::validateCount($count); - if ($msgctxt === null) { - return $this->nquery($msgid1, $msgid2, $count, $placeholders, $domain); - } - - $this->setDomain($domain); - - $context = "{$msgctxt}\004"; - $translation = dcngettext($domain, $context . $msgid1, $context . $msgid2, $count, $category); - - if (strpos($translation, $context, 0) === 0) { - $translation = substr($translation, strlen($context)); - } - - return self::setPlaceholders($translation, $placeholders); - } - - /** - * Returns the translation related to the given key and context (msgctxt) - * from a specific domain with plural form support. - * - * @param string $domain - * @param string $msgid1 - * @param string $msgid2 - * @param integer $count - * @param string $msgctxt Optional. - * @param array $placeholders Optional. - * @param integer $category Optional. Specify the locale category. Defaults to LC_MESSAGES - * - * @return string - */ - public function dnquery( - $domain, - $msgid1, - $msgid2, - $count, - $msgctxt = null, - $placeholders = null, - $category = LC_MESSAGES - ) { - return $this->cnquery($msgid1, $msgid2, $count, $msgctxt, $placeholders, $category, $domain); - } - - - /** - * {@inheritdoc} - * - * @param string $index - * - * @return boolean - */ - public function exists($index) - { - return gettext($index) !== ''; - } - - /** - * Changes the current domain (i.e. the translation file). The passed domain must be one - * of those passed to the constructor. - * - * @param string $domain - * - * @return string Returns the new current domain. - * @throws \InvalidArgumentException - */ - public function setDomain($domain) - { - if (!in_array($domain, $this->domains)) { - throw new \InvalidArgumentException($domain . ' is invalid translation domain'); - } - - return textdomain($domain); - } - - /** - * Sets the default domain. The default domain is the first item in the array of domains - * passed tot he constructor. Obviously, this method is irrelevant if Gettext was configured with a single - * domain only. - * - * @access public - * @return string Returns the new current domain. - */ - public function resetDomain() - { - return textdomain($this->defaultDomain); - } - - /** - * Count parameter validation - * - * @access public - * @throws \InvalidArgumentException - */ - public static function validateCount($count) - { - if (!is_int($count) || $count < 0) { - throw new \InvalidArgumentException("Count must be a nonnegative integer. $count given."); - } - } - - /** - * Validate required fields in $options - * - * @access public - * @throws \InvalidArgumentException - */ - public static function validateOptionsWithoutDomain($options) - { - if (!isset($options['file'], $options['directory'])) { - throw new \InvalidArgumentException('Parameters "file" and "directory" are required.'); - } - } -} diff --git a/Library/Phalcon/Translate/Adapter/README.md b/Library/Phalcon/Translate/Adapter/README.md index 55a482edd..dba2d64bb 100644 --- a/Library/Phalcon/Translate/Adapter/README.md +++ b/Library/Phalcon/Translate/Adapter/README.md @@ -3,175 +3,6 @@ Phalcon\Translate\Adapter Usage examples of the adapters available here: -Gettext -------- -This adapter uses gettext as translation frontend. - -The extension [gettext](http://www.php.net/manual/en/book.gettext.php) must be installed in PHP. - -Let's pretend your application have the following translation structure: - -```bash -app/ - lang/ - en_US/ - LC_MESSAGES/ - messages.po - messages.mo - fr_FR/ - LC_MESSAGES/ - messages.po - messages.mo -``` - -A translation file (fr_FR/LC_MESSAGES/messages.po) contains these definitions: - -```gettext -msgid "Hello" -msgstr "Bonjour" - -msgid "My name is %name%" -msgstr "Je m'appelle %name%" -``` - -A .po file is compiled using msgfmt: - -```bash -msgfmt -o messages.mo messages.po -``` - -It may be necessary to restart the web server after compile the .po files - -The adapter can be used as follows: - -```php -$translate = new Phalcon\Translate\Adapter\Gettext(array( - 'locale' => 'fr_FR', - 'file' => 'messages', - 'directory' => '../app/lang' -)); -``` - -```php -echo $translate->_('Hello'); //Bonjour -echo $translate->_('My name is %name%', array('name' => 'Peter')); //Je m'appelle Peter -``` - -Alternatively, multiple domains, each with a different directory can be specified: - -```php -$translate = new Phalcon\Translate\Adapter\Gettext(array( - 'locale' => 'fr_FR', - 'domains' => array( - 'messages' => '../app/lang', - 'warnings' => '../app/lang-warnings' - ) -)); -``` - -### Translation contexts - -Use the __() (alias to cquery()) method if you have multiple translations of a string in different contexts: - -```gettext -msgid "Hello" -msgstr "Bonjour" - -msgctxt "informal" -msgid "Hello" -msgstr "Salut" - -msgctxt "evening" -msgid "Hello" -msgstr "Bonsoir" - -msgid "Hello %name%" -msgstr "Salut %name%" -``` - -```php -echo $translate->_('Hello'); //Bonjour -echo $translate->__('Hello'); //Bonjour -echo $translate->__('Hello', 'informal'); //Salut -echo $translate->__('Hello', 'evening'); //Bonsoir -echo $translate->cquery('Hello', 'evening'); //Bonsoir -// placeholders are supported as well -echo $translate->__('Hello %name%', NULL, array('name' => 'Bob')); //Salut Bob -``` - -### Translation domains - -Multiple translations domains are supported by the dquery() method. Let's say you have two files with translations: - -```gettext -# frontend.po -msgid "Hello" -msgstr "Hello, visitor" -``` -Additionally, you have a file named *backend.po*: - -```gettext -# backend.po -msgid "Hello" -msgstr "Hello, admin" - -msgctxt "evening" -msgid "Hello" -msgstr "Bonsoir, admin" - -msgid "Hello %name%" -msgstr "Salut %name%" -``` - -```php -echo $translate->dquery('frontend', 'Hello'); //Hello, visitor -echo $translate->dquery('backend', 'Hello'); //Hello, admin -// contexts supported -echo $translate->dquery('backend', 'Hello', 'evening'); //Bonsoir, admin -// placeholders are supported as well -echo $translate->dquery('backend', 'Hello %name%', NULL, array('name' => 'Bob')); //Salut Bob -``` - -### Multiple plural forms - -Some languages require multiple plural forms of nouns depending on the object count. In gettext catalogs, plural forms need to be specified as such: - -```gettext -"Plural-Forms: nplurals=3; plural=n>4 ? 2 : n>1 ? 1 : 0;\n" - -msgid "banana" -msgid_plural "bananas" -msgstr[0] "banán" -msgstr[1] "banány" -msgstr[2] "banánov" -``` - -We can then leverage the multi-plural form support offered by the Gettext adapter: - -```php -for ($i = 1; $i < 7; $i++) { - echo "I have $i " . $translate->nquery('banana', 'bananas', $i); -} -// 1 banán -// 2 banány -// 3 banány -// 4 banány -// 5 banánov -// 6 banánov -``` - -Method cnquery() is a plural-form counterpart to cquery(). - -```php -(string) public function cnquery($msgid1, $msgid2, $count, $msgctxt = null, $placeholders = null, $category = LC_MESSAGES, $domain = null) -``` - -Method dnquery() is a plural-form counterpart to dquery(). - -```php -(string) public function dnquery($domain, $msgid1, $msgid2, $count, $msgctxt = null, $placeholders = null, $category = LC_MESSAGES) -``` - Database -------- You can use your database to store the translations, too. @@ -301,4 +132,4 @@ root { copyright { "© 2001-{currentYear, date, Y}. Foobar" } } } -``` \ No newline at end of file +``` diff --git a/README.md b/README.md index dcad55c9c..bebfea813 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ Phalcon is a web framework delivered as a C extension providing high performance and lower resource consumption. -This is a repository to publish/share/experiment with new adapters, prototypes or functionality that potentially can be incorporated to the C-framework. +This is a repository to publish/share/experiment with new adapters, prototypes or functionality that can potentially be incorporated into the framework. -Also we welcome submissions from the community of snippets that could extend the framework more. +We also welcome submissions of snippets from the community, to further extend the framework. The code in this repository is written in PHP. @@ -22,26 +22,25 @@ Install composer in a common location or in your project: curl -s http://getcomposer.org/installer | php ``` -Create the composer.json file as follows: - +If you are using Phalcon 2.0.x, create a composer.json file as follows: ```json { "require": { - "phalcon/incubator": "v1.3.4" + "phalcon/incubator": "v2.0.0" } } ``` -For 2.0.x branch please use: - +If you are still using Phalcon 1.3.x, create a composer.json with the following instead: ```json { "require": { - "phalcon/incubator": "2.0.*@dev" + "phalcon/incubator": "v1.3.5" } } ``` + Run the composer installer: ```bash @@ -56,10 +55,10 @@ Just clone the repository in a common location or inside your project: git clone https://github.com/phalcon/incubator.git ``` -For a specific Git branch (eg 2.0.x) please use: +For a specific Git branch (eg 1.3.5) please use: ``` -git clone -b 2.0.x git@github.com:phalcon/incubator.git +git clone -b 1.3.5 git@github.com:phalcon/incubator.git ``` ## Autoloading from the Incubator @@ -82,7 +81,7 @@ $loader->register(); Incubator is built under Travis CI service. Every commit pushed to this repository will queue a build into the continuous integration service and will run all PHPUnit tests to ensure that everything is going well and the project is stable. The current build status is: -[![Build Status](https://img.shields.io/travis/phalcon/incubator/master.svg?style=flat-square)](https://travis-ci.org/phalcon/incubator) +[![Build Status](https://img.shields.io/travis/phalcon/incubator/v2.0.0.svg?style=flat-square)](https://travis-ci.org/phalcon/incubator) # Contributing @@ -93,6 +92,7 @@ See CONTRIBUTING.md ### Acl * [Phalcon\Acl\Adapter\Database](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Acl/Adapter) - ACL lists stored in database tables * [Phalcon\Acl\Adapter\Mongo](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Acl/Adapter) - ACL lists stored in Mongo collections +* [Phalcon\Acl\Adapter\Redis](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Acl/Adapter) - ACL lists stored in a Redis cluster * [Phalcon\Acl\Factory\Memory](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Acl/Factory) - ACL factory class intended for use with Memory adapter (digitronac) ### Annotations @@ -108,8 +108,7 @@ See CONTRIBUTING.md * [Phalcon\Cache\Backend\Wincache](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Cache/Backend) - Wincache backend for caching data (nazwa) ### Config -* [Phalcon\Config\Adapter\Json](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Config/Adapter) - Json adapter (ofpiyush) -* [Phalcon\Config\Adapter\Yaml](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Config/Adapter) - YAML adapter (freekzy) +* [Phalcon\Config\Adapter\ExtendedYaml](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Config/Adapter) - YAML adapter (freekzy) ### Database * [Phalcon\Db\Adapter\Cacheable\Mysql](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Db) - MySQL adapter that agressively caches all the queries executed (phalcon) @@ -118,9 +117,9 @@ See CONTRIBUTING.md * [Phalcon\Debug\Dump](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Debug) - Variable dumper (digitronac) ### Logger -* [Phalcon\Logger\Adapter\Firephp](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Logger) - Adapter to log messages in Firebug (phalcon) * [Phalcon\Logger\Adapter\Database](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Logger) - Adapter to store logs in a database table (phalcon) * [Phalcon\Logger\Adapter\Firelogger](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Logger) - Adapter to log messages in the Firelogger console in Firebug (phalcon) +* [Phalcon\Logger\Adapter\File\Multiple](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Logger) - Adapter to log to multiple files (Richard Laffers) ### Mailer * [Phalcon\Mailer\Manager](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Mailer) - Mailer wrapper over SwiftMailer (KorsaR-ZN) @@ -133,7 +132,7 @@ See CONTRIBUTING.md ### ORM Validators * [Phalcon\Mvc\Model\Validator\ConfirmationOf](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Mvc/Model/Validator) - Allows to validate if a field has a confirmation field with the same value (suxxes) * [Phalcon\Mvc\Model\Validator\CardNumber](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Mvc/Model/Validator) - Allows to validate credit cadrd number using Luhn algorithm (parshikov) -* [Phalcon\Mvc\Model\Validator\IPv4](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Mvc/Model/Validator) - Validates that a value is ipv4 address in valid range (parshikov) +* [Phalcon\Mvc\Model\Validator\IP](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Mvc/Model/Validator) - Validates that a value is ip (v4 or v6) address in valid range (parshikov) * [Phalcon\Mvc\Model\Validator\Decimal](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Mvc/Model/Validator) - Allows to validate if a field has a valid number in proper decimal format (negative and decimal numbers allowed) (sergeyklay) * [Phalcon\Mvc\Model\Validator\Between](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Mvc/Model/Validator) - Validates that a value is between a range of two values (sergeyklay) @@ -152,7 +151,6 @@ See CONTRIBUTING.md ### Translate * [Phalcon\Translate\Adapter\Gettext](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Translate/Adapter) - Translation adapter for Gettext (phalcon) * [Phalcon\Translate\Adapter\Database](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Translate/Adapter) - Translation adapter using relational databases (phalcon) -* [Phalcon\Translate\Adapter\Csv](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Translate/Adapter) - Translation adapter using CSV (phalcon) * [Phalcon\Translate\Adapter\ResourceBundle](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Translate/Adapter) - Translation adapter using ResourceBundle (phalcon) ### Session diff --git a/composer.json b/composer.json index 0e458ee8e..2e094974e 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "phalcon/incubator", "type": "library", "description": "Adapters, prototypes or functionality that can be potentially incorporated to the C-framework.", - "keywords": ["framework", "phalcon"], + "keywords": ["framework", "phalcon", "incubator"], "homepage": "http://phalconphp.com", "license": "BSD-3", "authors": [ @@ -12,12 +12,12 @@ } ], "require": { - "php": ">=5.3.6", - "ext-phalcon": ">=1.2.4", + "php": ">=5.3.9", + "ext-phalcon": "~2.0", "swiftmailer/swiftmailer": ">=5.2" }, "require-dev": { - "phpunit/phpunit": "4.5.*", + "phpunit/phpunit": "4.6.*", "squizlabs/php_codesniffer": "2.*", "codeception/codeception": "*", "php-mock/php-mock": "<0.6" @@ -27,5 +27,4 @@ "Phalcon": "Library/" } } - } diff --git a/tests/Phalcon/Acl/Factory/MemoryTest.php b/tests/Phalcon/Acl/Factory/MemoryTest.php index 99fc4e2a9..24269927a 100644 --- a/tests/Phalcon/Acl/Factory/MemoryTest.php +++ b/tests/Phalcon/Acl/Factory/MemoryTest.php @@ -43,6 +43,8 @@ public function testFactoryShouldThrowExceptionIfResourceOptionIsMissing() */ public function testFactoryShouldThrowExceptionIfActionsKeyIsMissing() { + $this->markTestSkipped('Fails due to a bug in Phalcon. See https://github.com/phalcon/cphalcon/pull/10226'); + $config = new \Phalcon\Config\Adapter\Ini(__DIR__ . '/_fixtures/acl.ini'); unset($config->acl->resource->index->actions); unset($config->acl->role->guest->allow->index->actions[0]); diff --git a/tests/Phalcon/Debug/DumpTest.php b/tests/Phalcon/Debug/DumpTest.php deleted file mode 100644 index e931320a8..000000000 --- a/tests/Phalcon/Debug/DumpTest.php +++ /dev/null @@ -1,132 +0,0 @@ - '

test

', - ); - - // set for testing purposes - protected $flushBuffer = false; - - public function testDumpingHtmlStringVarByDefaultShouldEchoNonEscapedDump() - { - ob_start(); - $dump = new \Phalcon\Debug\Dump($this->flushBuffer); - $dump->dump($this->fixtures['htmlString']); - $output = ob_get_clean(); - $this->assertBacktraceExists($output); - // assert string was not converted - $this->assertContains($this->fixtures['htmlString'], $output); - // assert correct sapi set - $this->assertEquals(PHP_SAPI, \Phalcon\Debug\Dump::getSapi()); - } - - public function testDumpingHtmlStringVarWithNonCliSapiShouldEchoEscapedDump() - { - // non cli sapi - \Phalcon\Debug\Dump::setSapi('apache'); - - ob_start(); - $dump = new \Phalcon\Debug\Dump($this->flushBuffer); - $dump->dump($this->fixtures['htmlString']); - $output = ob_get_clean(); - $this->assertBacktraceExists($output); - // assert string was converted - $this->assertContains(htmlentities($this->fixtures['htmlString'], ENT_QUOTES, 'UTF-8'), $output); - // assert correct sapi set - $this->assertEquals('apache', \Phalcon\Debug\Dump::getSapi()); - } - - public function testDumpingHtmlStringVarWithSupressedOutputShouldReturnValue() - { - // supress output - \Phalcon\Debug\Dump::setOutput(false); - - ob_start(); - $dump = new \Phalcon\Debug\Dump($this->flushBuffer); - $return = $dump->dump($this->fixtures['htmlString']); - $output = ob_get_clean(); - $this->assertBacktraceNotExists($output); - // assert no output - $this->assertEmpty($output); - // assert returned value has backtrace - $this->assertBacktraceExists($return); - // assert dump was returned - $this->assertContains($this->fixtures['htmlString'], $return); - // assert right output flag was set - $this->assertFalse(\Phalcon\Debug\Dump::getOutput()); - } - - public function testSecondDumpParamShouldOverrideGlobalOutputSetting() - { - ob_start(); - $dump = new \Phalcon\Debug\Dump($this->flushBuffer); - $return = $dump->dump($this->fixtures['htmlString'], false); - $output = ob_get_clean(); - // no backtrace - $this->assertBacktraceNotExists($output); - // assert no output - $this->assertEmpty($output); - // assert dump was returned - $this->assertContains($this->fixtures['htmlString'], $return); - } - - public function testIfNoXdebugVarDumpObjectShouldFallback() - { - /* @var $mockDumpStub \Phalcon\Debug\Dump */ - $mockDumpStub = $this->getMock('\Phalcon\Debug\Dump', array('xdebugDumpExists'), - array($this->flushBuffer)); - - $mockDumpStub->expects($this->once()) - ->method('xdebugDumpExists') - ->will($this->returnValue(false)); - - ob_start(); - $mockDumpStub->dump($this->fixtures['htmlString']); - $output = ob_get_clean(); - $this->assertBacktraceExists($output); - // assert string was not converted - $this->assertContains($this->fixtures['htmlString'], $output); - - } - - public function testObGetCleanShouldReturnEmptyIfFlushBufferNotSet() - { - ob_start(); - $dump = new \Phalcon\Debug\Dump(); - $return = $dump->dump($this->fixtures['htmlString']); - $output = ob_get_clean(); - - // assert output empty - $this->assertEmpty($output); - $this->assertBacktraceExists($return); - $this->assertContains($this->fixtures['htmlString'], $return); - - } - - protected function assertBacktraceExists($output) - { - // assert backtrace exists - $this->assertContains('Line:', $output); - $this->assertContains('DumpTest.php', $output); - } - - protected function assertBacktraceNotExists($output) - { - // assert backtrace doesnt exist - $this->assertNotContains('Line:', $output); - $this->assertNotContains('DumpTest.php', $output); - } - - protected function tearDown() - { - parent::tearDown(); - // reset output flag to default - \Phalcon\Debug\Dump::setOutput(true); - // reset PHP_SAPI - \Phalcon\Debug\Dump::setSapi(PHP_SAPI); - - } -} diff --git a/tests/Phalcon/Mvc/Model/Validator/BetweenTest.php b/tests/Phalcon/Mvc/Model/Validator/BetweenTest.php index 9a2b5ac2b..f9208f25e 100644 --- a/tests/Phalcon/Mvc/Model/Validator/BetweenTest.php +++ b/tests/Phalcon/Mvc/Model/Validator/BetweenTest.php @@ -62,17 +62,6 @@ public function testValidate($min, $max, $position, $willReturn) } - /** - * @expectedException \Phalcon\Mvc\Model\Exception - * @expectedExceptionMessage Invalid parameter type. - */ - public function testExceptionNotObject() - { - $obj = new Between(array('min' => 1, 'max' => 2, 'field' => 'position')); - - $obj->validate(1); - } - public function testValidateInstanceOf() { require_once(__DIR__ . '/resources/TestBetweenModel.php'); @@ -81,24 +70,6 @@ public function testValidateInstanceOf() $this->assertInstanceOf('Phalcon\Mvc\ModelInterface', $obj); } - /** - * @expectedException \Phalcon\Mvc\Model\Exception - * @expectedExceptionMessage Invalid parameter type. - */ - public function testValidateOtherInstance() - { - require_once(__DIR__ . '/resources/TestBetweenFail.php'); - - $obj = new \TestBetweenFail(); - - $obj->min = 1; - $obj->max = 3; - $obj->position = 4; - - $this->assertNotInstanceOf('Phalcon\Mvc\ModelInterface', $obj); - $obj->validation(); - } - /** * @expectedException \Phalcon\Mvc\Model\Exception * @expectedExceptionMessage Field name must be a string diff --git a/tests/Phalcon/Mvc/Model/Validator/CardNumberTest.php b/tests/Phalcon/Mvc/Model/Validator/CardNumberTest.php index 25c9c9b41..2db721ff6 100644 --- a/tests/Phalcon/Mvc/Model/Validator/CardNumberTest.php +++ b/tests/Phalcon/Mvc/Model/Validator/CardNumberTest.php @@ -141,46 +141,9 @@ public function testValidateInstanceOfModel() $obj = new \TestCardNumberModel(); $this->assertInstanceOf('Phalcon\Mvc\ModelInterface', $obj); - $this->assertNotInstanceOf('Phalcon\Mvc\CollectionInterface', $obj); } - public function testValidateInstanceOfCollection() - { - $di = New DI(); - $di->set('collectionManager', function () { - return new \Phalcon\Mvc\Collection\Manager(); - }); - - require_once(__DIR__ . '/resources/TestCardNumberCollection.php'); - - $obj = new \TestCardNumberCollection($di); - - $obj->type = CardNumber::MASTERCARD; - $obj->cardnumber = 1270338206812535; - - $this->assertInstanceOf('Phalcon\Mvc\CollectionInterface', $obj); - $this->assertNotInstanceOf('Phalcon\Mvc\ModelInterface', $obj); - $obj->validation(); - } - - /** - * @expectedException \Phalcon\Mvc\Model\Exception - * @expectedExceptionMessage Invalid parameter type. - */ - public function testValidateOtherInstance() - { - require_once(__DIR__ . '/resources/TestCardNumberFail.php'); - - $obj = new \TestCardNumberFail(); - - $obj->type = CardNumber::MASTERCARD; - $obj->cardnumber = 1270338206812535; - - $this->assertNotInstanceOf('Phalcon\Mvc\CollectionInterface', $obj); - $this->assertNotInstanceOf('Phalcon\Mvc\ModelInterface', $obj); - $obj->validation(); - } /** * @expectedException \Phalcon\Mvc\Model\Exception diff --git a/tests/Phalcon/Paginator/PagerTest.php b/tests/Phalcon/Paginator/PagerTest.php new file mode 100644 index 000000000..09624bee0 --- /dev/null +++ b/tests/Phalcon/Paginator/PagerTest.php @@ -0,0 +1,262 @@ +getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->getMock(); + + $pager = new \Phalcon\Paginator\Pager($adapter); + $this->assertInstanceOf('Phalcon\Paginator\Pager', $pager); + } + + public function testCallingGetPagesInRangeMethodWithDefaultOptionsShouldReturnExpectedArray() + { + // stub paginate + $paginate = new \stdClass(); + $paginate->total_pages = 20; + $paginate->current = 5; + $paginate->last = 20; + + $adapter = $this->getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->setMethods(array('getPaginate')) + ->getMock(); + $adapter->expects($this->once()) + ->method('getPaginate') + ->will($this->returnValue($paginate)); + + $pager = new \Phalcon\Paginator\Pager($adapter); + $this->assertEquals( + array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + $pager->getPagesInRange() + ); + } + + public function testCallingGetPagesInRangeMethodWithSliderOnEndShouldReturnExpectedArray() + { + // stub paginate + $paginate = new \stdClass(); + $paginate->total_pages = 20; + $paginate->current = 20; + $paginate->last = 20; + + $adapter = $this->getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->setMethods(array('getPaginate')) + ->getMock(); + $adapter->expects($this->once()) + ->method('getPaginate') + ->will($this->returnValue($paginate)); + + $pager = new \Phalcon\Paginator\Pager($adapter, array('rangeLength' => 5)); + $this->assertEquals( + array(16, 17, 18, 19, 20), + $pager->getPagesInRange() + ); + } + + public function testCallingGetPagesInRangeMethodWithSliderOnStartShouldReturnExpectedArray() + { + // stub paginate + $paginate = new \stdClass(); + $paginate->total_pages = 20; + $paginate->current = 1; + $paginate->last = 20; + + $adapter = $this->getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->setMethods(array('getPaginate')) + ->getMock(); + $adapter->expects($this->once()) + ->method('getPaginate') + ->will($this->returnValue($paginate)); + + $pager = new \Phalcon\Paginator\Pager($adapter, array('rangeLength' => 5)); + $this->assertEquals( + array(1, 2, 3, 4, 5), + $pager->getPagesInRange() + ); + } + + public function testGetLayoutMethodShouldReturnObjectOfLayoutType() + { + // stub paginate + $paginate = new \stdClass(); + $paginate->total_pages = 20; + $paginate->current = 1; + $paginate->last = 20; + + $adapter = $this->getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->setMethods(array('getPaginate')) + ->getMock(); + $adapter->expects($this->once()) + ->method('getPaginate') + ->will($this->returnValue($paginate)); + + $pager = new \Phalcon\Paginator\Pager( + $adapter, + array( + 'rangeLength' => 5, + 'urlMask' => 'xxx', + ) + ); + $this->assertInstanceOf('Phalcon\Paginator\Pager\Layout', $pager->getLayout()); + } + + public function testPagerGetterMethodsShouldReturnExpectedValues() + { + // stub paginate + $paginate = new \stdClass(); + $paginate->total_pages = 20; + $paginate->current = 10; + $paginate->last = 20; + $paginate->total_items = 100; + $paginate->first = 1; + $paginate->before = $paginate->current - 1; + $paginate->next = $paginate->current + 1; + $paginate->items = new \ArrayIterator(array(1, 2, 4, 5)); + + $adapter = $this->getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->setMethods(array('getPaginate')) + ->getMock(); + $adapter->expects($this->once()) + ->method('getPaginate') + ->will($this->returnValue($paginate)); + + $pager = new \Phalcon\Paginator\Pager( + $adapter, + array( + 'rangeLength' => 5, + 'urlMask' => 'xxx', + ) + ); + $this->assertEquals($paginate->current, $pager->getCurrentPage()); + $this->assertEquals($paginate->total_items, $pager->count()); + $this->assertEquals(1, $pager->getFirstPage()); + $this->assertTrue($pager->haveToPaginate()); + $this->assertEquals($paginate->before, $pager->getPreviousPage()); + $this->assertEquals($paginate->next, $pager->getNextPage()); + $this->assertInstanceOf('Iterator', $pager->getIterator()); + } + + public function testPagerGetIteratorMethodWillCreateIteratorIfArrayIsPassed() + { + // stub paginate + $paginate = new \stdClass(); + $paginate->total_pages = 20; + $paginate->current = 10; + $paginate->items = array(1, 2, 4, 5); + + $adapter = $this->getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->setMethods(array('getPaginate')) + ->getMock(); + $adapter->expects($this->once()) + ->method('getPaginate') + ->will($this->returnValue($paginate)); + + $pager = new \Phalcon\Paginator\Pager( + $adapter, + array( + 'rangeLength' => 5, + 'urlMask' => 'xxx', + ) + ); + $this->assertInstanceOf('Iterator', $pager->getIterator()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must provide option "urlMask" + */ + public function testGetLayoutMethodWithoutUrlMaskOptionShouldThrowException() + { + // stub paginate + $paginate = new \stdClass(); + $paginate->total_pages = 20; + $paginate->current = 1; + + $adapter = $this->getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->setMethods(array('getPaginate')) + ->getMock(); + $adapter->expects($this->once()) + ->method('getPaginate') + ->will($this->returnValue($paginate)); + + $pager = new \Phalcon\Paginator\Pager( + $adapter, + array( + 'rangeLength' => 5, + ) + ); + $pager->getLayout(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Unable to find range class "UnknownRangeClass" + */ + public function testGetLayoutMethodShouldWithInvalidRangeClassShouldThrowException() + { + // stub paginate + $paginate = new \stdClass(); + $paginate->total_pages = 20; + $paginate->current = 1; + + $adapter = $this->getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->setMethods(array('getPaginate')) + ->getMock(); + $adapter->expects($this->once()) + ->method('getPaginate') + ->will($this->returnValue($paginate)); + + $pager = new \Phalcon\Paginator\Pager( + $adapter, + array( + 'rangeLength' => 5, + 'rangeClass' => 'UnknownRangeClass', + 'urlMask' => 'xxx', + ) + ); + $pager->getLayout(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Unable to find layout "UnknownLayoutClass" + */ + public function testGetLayoutMethodShouldWithInvalidLayoutClassShouldThrowException() + { + // stub paginate + $paginate = new \stdClass(); + $paginate->total_pages = 20; + $paginate->current = 1; + + + $adapter = $this->getMockBuilder('Phalcon\Paginator\Adapter\QueryBuilder') + ->disableOriginalConstructor() + ->setMethods(array('getPaginate')) + ->getMock(); + $adapter->expects($this->once()) + ->method('getPaginate') + ->will($this->returnValue($paginate)); + + $pager = new \Phalcon\Paginator\Pager( + $adapter, + array( + 'rangeLength' => 5, + 'layoutClass' => 'UnknownLayoutClass', + 'urlMask' => 'xxx', + ) + ); + $pager->getLayout(); + } +}