From 42da925424db19a4dc6b52d098d02a1951166244 Mon Sep 17 00:00:00 2001 From: Sid Roberts Date: Sat, 2 May 2015 16:24:09 +0100 Subject: [PATCH 01/11] Travis now checks against Phalcon 2.0.0 and 2.0.x. --- .travis.yml | 8 +- Library/Phalcon/Config/Adapter/Json.php | 32 -- Library/Phalcon/Config/Adapter/README.md | 38 +- Library/Phalcon/Debug/Dump.php | 149 ------- Library/Phalcon/Debug/README.md | 95 ----- .../Phalcon/Mvc/Model/Validator/Between.php | 10 +- .../Mvc/Model/Validator/CardNumber.php | 11 +- .../Mvc/Model/Validator/ConfirmationOf.php | 9 +- .../Phalcon/Mvc/Model/Validator/Decimal.php | 8 +- Library/Phalcon/Mvc/Model/Validator/IP.php | 15 +- Library/Phalcon/Session/Adapter/Memcache.php | 226 ----------- Library/Phalcon/Session/Adapter/README.md | 23 -- Library/Phalcon/Translate/Adapter/Csv.php | 75 ---- Library/Phalcon/Translate/Adapter/Gettext.php | 380 ------------------ Library/Phalcon/Translate/Adapter/README.md | 185 --------- tests/Phalcon/Acl/Factory/MemoryTest.php | 2 + tests/Phalcon/Debug/DumpTest.php | 132 ------ .../Mvc/Model/Validator/BetweenTest.php | 29 -- .../Mvc/Model/Validator/CardNumberTest.php | 37 -- 19 files changed, 25 insertions(+), 1439 deletions(-) delete mode 100644 Library/Phalcon/Config/Adapter/Json.php delete mode 100644 Library/Phalcon/Debug/Dump.php delete mode 100644 Library/Phalcon/Debug/README.md delete mode 100755 Library/Phalcon/Session/Adapter/Memcache.php delete mode 100644 Library/Phalcon/Translate/Adapter/Csv.php delete mode 100644 Library/Phalcon/Translate/Adapter/Gettext.php delete mode 100644 tests/Phalcon/Debug/DumpTest.php 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/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/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/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 3377656b4..e1ebac279 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. @@ -258,19 +89,3 @@ Or, if you wish you can use [Volt](http://docs.phalconphp.com/en/latest/referenc ```html+php

{{ expression._("IndexPage_Hello_World") }}

``` - -CSV --------- -This adapter uses CSV as translation frontend. - -```php -$translate = new Phalcon\Translate\Adapter\Csv([ - 'file' => 'fr_FR.csv', // required - 'delimiter' => ',', // optional, default - ; - 'length' => '4096', // optional, default - 0 - 'enclosure' => '^', // optional, default - " -]); - -echo $translate->_('Hello'); -echo $translate->_('My name is %name%', array('name' => 'John Doe')); //Je m'appelle John Doe -``` 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 From 524c0e734d50befbae6e243fe9fc41cc6cc2e2ec Mon Sep 17 00:00:00 2001 From: Nikita Vershinin Date: Tue, 5 May 2015 17:10:22 +0600 Subject: [PATCH 02/11] Fixed #325 --- Library/Phalcon/Mvc/Model/Behavior/NestedSet.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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; From 9c69267f1acef800886aa73dfffb3433306ceadd Mon Sep 17 00:00:00 2001 From: Nikita Vershinin Date: Tue, 5 May 2015 17:36:35 +0600 Subject: [PATCH 03/11] Fix #300 Method Phalcon\Paginator\Pager::getPagesInRange() returned Tests for pager were returned --- Library/Phalcon/Paginator/Pager.php | 106 ++++--- Library/Phalcon/Paginator/Pager/Layout.php | 3 +- .../Phalcon/Paginator/Pager/Range/Sliding.php | 4 +- tests/Phalcon/Paginator/PagerTest.php | 262 ++++++++++++++++++ 4 files changed, 334 insertions(+), 41 deletions(-) create mode 100644 tests/Phalcon/Paginator/PagerTest.php 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/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(); + } +} From 2eb1a107e29c2f5e581afef1fed69c7e8b2dbb3c Mon Sep 17 00:00:00 2001 From: Green Cat Date: Tue, 5 May 2015 13:48:08 +0100 Subject: [PATCH 04/11] [2.0.x] Removes no longer existing Adapters from README file --- Library/Phalcon/Logger/README.md | 14 -------------- README.md | 29 ++++++++++------------------- 2 files changed, 10 insertions(+), 33 deletions(-) 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/README.md b/README.md index 0424c766c..e2cb1c2f3 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,8 @@ 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. The code in this repository is written in PHP. @@ -22,26 +21,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": "2.0.*@dev" } } ``` -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.4" } } ``` + Run the composer installer: ```bash @@ -98,19 +96,15 @@ $loader->register(); * [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) -### Debug -* [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) @@ -123,7 +117,7 @@ $loader->register(); ### 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) @@ -140,13 +134,10 @@ $loader->register(); * [Phalcon\Test\UnitTestCase](https://github.com/silverbadge/incubator/tree/master/Library/Phalcon/Test) - Generic test case wrapper (thecodeassassin) ### 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) ### Session * [Phalcon\Session\Adapter\Database](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Session/Adapter) - Database adapter for storing sessions (phalcon) -* [Phalcon\Session\Adapter\Memcache](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Session/Adapter) - Memcache adapter for storing sessions (meets-ecommerce) * [Phalcon\Session\Adapter\Mongo](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Session/Adapter) - MongoDb adapter for storing sessions (phalcon) * [Phalcon\Session\Adapter\Redis](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Session/Adapter) - Redis adapter for storing sessions (phalcon) * [Phalcon\Session\Adapter\HandlerSocket](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Session/Adapter) - HandlerSocket adapter for storing sessions (Xrymz) @@ -159,4 +150,4 @@ Current Build Status -------------------- 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/2.0.x.svg?style=flat-square)](https://travis-ci.org/phalcon/incubator) From da5b94fdfc44556745aee303445fe3f8ee1a0196 Mon Sep 17 00:00:00 2001 From: Green Cat Date: Tue, 5 May 2015 13:56:14 +0100 Subject: [PATCH 05/11] Adds back sentence --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e2cb1c2f3..1b65f1800 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Phalcon is a web framework delivered as a C extension providing high performance This is a repository to publish/share/experiment with new adapters, prototypes or functionality that can potentially be incorporated into the framework. +We also welcome submissions of snippets from the community, to further extend the framework. The code in this repository is written in PHP. From 388f0fe3ef17d5adc350eb8ca2ae20a107bc660e Mon Sep 17 00:00:00 2001 From: Green Cat Date: Tue, 5 May 2015 14:03:15 +0100 Subject: [PATCH 06/11] Implements a Redis ACL Adapter With support for php 5.3 --- Library/Phalcon/Acl/Adapter/Redis.php | 416 ++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 Library/Phalcon/Acl/Adapter/Redis.php diff --git a/Library/Phalcon/Acl/Adapter/Redis.php b/Library/Phalcon/Acl/Adapter/Redis.php new file mode 100644 index 000000000..a4f7e4ec5 --- /dev/null +++ b/Library/Phalcon/Acl/Adapter/Redis.php @@ -0,0 +1,416 @@ +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); + } + } + +} \ No newline at end of file From a001fe9c9239a92093ede879293da1be8c6f372c Mon Sep 17 00:00:00 2001 From: Green Cat Date: Tue, 5 May 2015 14:28:10 +0100 Subject: [PATCH 07/11] Fixes PSR-2 --- Library/Phalcon/Acl/Adapter/Redis.php | 36 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/Library/Phalcon/Acl/Adapter/Redis.php b/Library/Phalcon/Acl/Adapter/Redis.php index a4f7e4ec5..cd25a0eb1 100644 --- a/Library/Phalcon/Acl/Adapter/Redis.php +++ b/Library/Phalcon/Acl/Adapter/Redis.php @@ -216,7 +216,9 @@ public function getResourceAccess($resource) */ public function dropResourceAccess($resource, $accessList) { - if (!is_array($accessList)) $accessList = (array)$accessList; + if (!is_array($accessList)) { + $accessList = (array)$accessList; + } array_unshift($accessList, "resourcesAccesses:$resource"); call_user_func_array(array($this->redis, 'sRem'), $accessList); } @@ -242,8 +244,9 @@ public function dropResourceAccess($resource, $accessList) */ public function allow($role, $resource, $access) { - if ($role !== '*' && $resource !== '*') + if ($role !== '*' && $resource !== '*') { $this->allowOrDeny($role, $resource, $access, Acl::ALLOW); + } if ($role === '*' || empty($role)) { $this->rolePermission($resource, $access, Acl::ALLOW); } @@ -261,10 +264,11 @@ public function allow($role, $resource, $access) protected function resourcePermission($role, $access, $allowOrDeny) { foreach ($this->getResources() as $resource) { - if ($role === '*' || empty($role)) + if ($role === '*' || empty($role)) { $this->rolePermission($resource, $access, $allowOrDeny); - else + } else { $this->allowOrDeny($role, $resource, $access, $allowOrDeny); + } } } @@ -277,10 +281,11 @@ protected function resourcePermission($role, $access, $allowOrDeny) protected function rolePermission($resource, $access, $allowOrDeny) { foreach ($this->getRoles() as $role) { - if ($resource === '*' || empty($resource)) + if ($resource === '*' || empty($resource)) { $this->resourcePermission($role, $access, $allowOrDeny); - else + } else { $this->allowOrDeny($role, $resource, $access, $allowOrDeny); + } } } @@ -333,13 +338,16 @@ public function deny($role, $resource, $access) */ public function isAllowed($role, $resource, $access) { - if ($this->redis->sIsMember("accessList:$role:$resource:" . Acl::ALLOW, $access)) return Acl::ALLOW; + 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)) + if ($this->redis->sIsMember("accessList:$role:$resource:" . Acl::ALLOW, $access)) { return Acl::ALLOW; + } } } @@ -364,9 +372,11 @@ 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" - ); + 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); @@ -382,7 +392,6 @@ protected function setAccess($roleName, $resourceName, $accessName, $action) $this->redis->sAdd("$accessList:{$this->getDefaultAction()}", "*"); - return true; } @@ -412,5 +421,4 @@ protected function allowOrDeny($roleName, $resourceName, $access, $action) $this->setAccess($roleName, $resourceName, $access, $action); } } - -} \ No newline at end of file +} From 60fedfcdc766e1a27383280bcc6c94d0bb44b601 Mon Sep 17 00:00:00 2001 From: Green Cat Date: Tue, 5 May 2015 14:41:10 +0100 Subject: [PATCH 08/11] One more PSR-2 fix --- Library/Phalcon/Acl/Adapter/Redis.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Phalcon/Acl/Adapter/Redis.php b/Library/Phalcon/Acl/Adapter/Redis.php index cd25a0eb1..110aa9058 100644 --- a/Library/Phalcon/Acl/Adapter/Redis.php +++ b/Library/Phalcon/Acl/Adapter/Redis.php @@ -372,7 +372,7 @@ protected function setAccess($roleName, $resourceName, $accessName, $action) * Check if the access is valid in the resource */ if ($this->isResourceAccess($resourceName, $accessName)) { - if (!$this->setNXAccess) { + if (!$this->setNXAccess) { throw new Exception( "Access '" . $accessName . "' does not exist in resource '" . $resourceName . "' in ACL" ); From 4077fefe9d52823c3e421f84558b699e52ae8f84 Mon Sep 17 00:00:00 2001 From: Vladimir Metelitsa Date: Tue, 5 May 2015 15:30:46 +0100 Subject: [PATCH 09/11] Updates README - 2.0.x badge - ACL Redis adapter added --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b65f1800..b19e2826a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Latest Version](https://img.shields.io/packagist/v/phalcon/incubator.svg?style=flat-square)](https://github.com/phalcon/incubator/releases) +[![Latest Version](https://img.shields.io/packagist/vpre/phalcon/incubator.svg?style=flat-square)](https://github.com/phalcon/incubator/releases) [![Software License](https://img.shields.io/badge/license-BSD--3-brightgreen.svg?style=flat-square)](LICENSE.md) [![Total Downloads](https://img.shields.io/packagist/dt/phalcon/incubator.svg?style=flat-square)](https://packagist.org/packages/phalcon/incubator) @@ -82,6 +82,7 @@ $loader->register(); ### 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 From f9fe8df7624ff14b7f3d6a3f3ceeec8fbd11d8b9 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 5 May 2015 17:58:55 +0300 Subject: [PATCH 10/11] Added contributing doc [ci skip] --- CONTRIBUTING.md | 28 ++++++++++++++++++++++++++++ README.md | 4 ++++ 2 files changed, 32 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..c17503a33 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# Contributing to Phalcon Incubator + +Phalcon Incubator is an open source project and a volunteer effort. Phalcon Incubator welcomes contribution from everyone. + +Contributions to Phalcon Incubator should be made in the form of GitHub pull requests. +*We only accept bug reports, new feature requests and pull requests in GitHub*. + +## Not All Commits Need CI Builds + +Sometimes all you are changing is the README, some documentation or other things which have no effect on the tests. +In this case, you may not want a build to be created for that commit. To do this, all you need to do is to add `[ci skip]` +somewhere in the commit message. + +Commits that have `[ci skip]` anywhere in the commit messages will be ignored. `[ci skip]` does not have to appear on the +first line, so it is possible to use it without polluting your project's history. + +Alternatively, you can also use `[skip ci]`. + +## Getting Support + +For questions regarding the usage of the framework or support requests please visit the [official forums](http://forum.phalconphp.com/). + +## Requesting Features + +If you have a change or new feature in mind, please fill an [NFR](https://github.com/phalcon/cphalcon/wiki/New-Feature-Request---NFR). + +Thanks!
+Phalcon Team diff --git a/README.md b/README.md index b19e2826a..1bbf9c0e8 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,10 @@ $loader->registerNamespaces(array( $loader->register(); ``` +## Contributing + +See CONTRIBUTING.md + ## Contributions Index ### Acl From 564ff117c7b0e94d3bdf77024e0a61df753b5b38 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 5 May 2015 18:03:11 +0300 Subject: [PATCH 11/11] Merged "Current Build Status" top [ci skip] --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1bbf9c0e8..bc242e874 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,12 @@ $loader->registerNamespaces(array( $loader->register(); ``` +## Current Build Status + +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/2.0.x.svg?style=flat-square)](https://travis-ci.org/phalcon/incubator) + ## Contributing See CONTRIBUTING.md @@ -151,9 +157,3 @@ See CONTRIBUTING.md ### Utils * [Phalcon\Utils\Slug](https://github.com/phalcon/incubator/tree/master/Library/Phalcon/Utils) - Creates a slug for the passed string taking into account international characters. (niden) - -Current Build Status --------------------- -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/2.0.x.svg?style=flat-square)](https://travis-ci.org/phalcon/incubator)