diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9945af7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +tests/_reports +vendor \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ea03129 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - hhvm + +before_script: + - composer self-update + - composer install --dev + +script: phpunit + +matrix: + allow_failures: + - php: hhvm diff --git a/README.md b/README.md index cd7286f..c94800c 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,17 @@ This library provides a service that can be used to filter object values based on annotations -## Installation +## Install -### Composer +Use composer to add DMS\Filter to your app - composer.phar require dms/dms-filter=~1.1 +`composer require dms/dms-filter:~2.0` + +This will get you the latest compatible version with 2.0 without BC breaks. ## Usage -Your Entity: +Your Entity: ```php -``` +``` Filtering: @@ -75,7 +77,7 @@ Filtering: echo $user->name; //"My name" echo $user->email; //"email@mail.com" ?> -``` +``` Full example: https://gist.github.com/1098352 @@ -83,7 +85,7 @@ Full example: https://gist.github.com/1098352 This package relies on these external libraries: -* Doctrine Common: Reader and Cache +* Doctrine Annotations ## Contributing @@ -92,12 +94,11 @@ Feel free to send pull requests, just follow these guides: * Fork * Code * Test - * Tests are in: https://github.com/rdohms/DMS - * Just create Testcase and run `phpunit` inside the `tests` folder + * Just create FilterTestCase and run `phpunit` * Submit PR ## Credits -This library is inspired by the Symfony 2 Validator component and is meant to work alongside it. +This library is inspired by the Symfony 2 Validator component and is meant to work alongside it. Symfony 2 Validator: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Validator diff --git a/Rules/StripNewlines.php b/Rules/StripNewlines.php deleted file mode 100644 index 71efbab..0000000 --- a/Rules/StripNewlines.php +++ /dev/null @@ -1,23 +0,0 @@ -useEncoding()) { - return mb_strtolower((string) $value, $this->encoding); - } - - return strtolower((string) $value); - } - - /** - * Verify is encoding is set and if we have the proper - * function to use it - * - * @return boolean - */ - public function useEncoding() - { - if ($this->encoding === null) { - return false; - } - - if (!function_exists('mb_strtolower')) { - throw new FilterException( - 'mbstring is required to use ToLower with an encoding.'); - } - - $this->encoding = (string) $this->encoding; - $encodings = array_map('strtolower', mb_list_encodings()); - - if (!in_array(strtolower($this->encoding), $encodings)) { - throw new FilterException( - "mbstring does not support the '".$this->encoding."' encoding" - ); - } - - return true; - } - - /** - * {@inheritDoc} - */ - public function getDefaultOption() - { - return 'encoding'; - } -} \ No newline at end of file diff --git a/Rules/ToUpper.php b/Rules/ToUpper.php deleted file mode 100644 index e272f4c..0000000 --- a/Rules/ToUpper.php +++ /dev/null @@ -1,72 +0,0 @@ -useEncoding()) { - return mb_strtoupper((string) $value, $this->encoding); - } - - return strtoupper((string) $value); - } - - /** - * Verify is encoding is set and if we have the proper - * function to use it - * - * @return boolean - */ - public function useEncoding() - { - if ($this->encoding === null) { - return false; - } - - if (!function_exists('mb_strtoupper')) { - throw new FilterException( - 'mbstring is required to use ToLower with an encoding.'); - } - - $this->encoding = (string) $this->encoding; - $encodings = array_map('strtolower', mb_list_encodings()); - - if (!in_array(strtolower($this->encoding), $encodings)) { - throw new FilterException( - "mbstring does not support the '".$this->encoding."' encoding" - ); - } - - return true; - } - - /** - * {@inheritDoc} - */ - public function getDefaultOption() - { - return 'encoding'; - } -} \ No newline at end of file diff --git a/Rules/Trim.php b/Rules/Trim.php deleted file mode 100644 index f5f8e25..0000000 --- a/Rules/Trim.php +++ /dev/null @@ -1,44 +0,0 @@ -charlist === null) { - return trim($value); - } - - return trim($value, $this->charlist); - } - - /** - * {@inheritDoc} - */ - public function getDefaultOption() - { - return 'charlist'; - } -} \ No newline at end of file diff --git a/Version.php b/Version.php deleted file mode 100644 index 6080472..0000000 --- a/Version.php +++ /dev/null @@ -1,29 +0,0 @@ -=5.3.2", - "doctrine/common": ">=2.1" + "doctrine/annotations": "~1.1" }, "autoload": { "psr-0": { - "DMS\\Filter": "" + "DMS": ["src","tests"] } }, - "target-dir": "DMS/Filter" -} \ No newline at end of file + "require-dev": { + "phpunit/phpunit": "~3.7", + "doctrine/cache": "~1.3", + "zendframework/zend-filter": "~2.0" + }, + + "suggest": { + "zendframework/zend-filter": "Use Zend Filters via DMS Filter" + }, + + "repositories": [ + { + "type": "composer", + "url": "https://packages.zendframework.com/" + } + ] +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..f8806ce --- /dev/null +++ b/composer.lock @@ -0,0 +1,737 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "b59fba71a7be12c98baa9f68828f0034", + "packages": [ + { + "name": "doctrine/annotations", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "40db0c96985aab2822edbc4848b3bd2429e02670" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/40db0c96985aab2822edbc4848b3bd2429e02670", + "reference": "40db0c96985aab2822edbc4848b3bd2429e02670", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/cache": "1.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Annotations\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/", + "role": "Creator" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2013-06-16 21:33:03" + }, + { + "name": "doctrine/lexer", + "version": "v1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/2f708a85bb3aab5d99dab8be435abd73e0b18acb", + "reference": "2f708a85bb3aab5d99dab8be435abd73e0b18acb", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2013-01-12 18:59:04" + } + ], + "packages-dev": [ + { + "name": "doctrine/cache", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "e16d7adf45664a50fa86f515b6d5e7f670130449" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/e16d7adf45664a50fa86f515b6d5e7f670130449", + "reference": "e16d7adf45664a50fa86f515b6d5e7f670130449", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": ">=3.7", + "satooshi/php-coveralls": "~0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Cache\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/", + "role": "Creator" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2013-10-25 19:04:14" + }, + { + "name": "phpunit/php-code-coverage", + "version": "1.2.13", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", + "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.0@stable", + "phpunit/php-text-template": ">=1.1.1@stable", + "phpunit/php-token-stream": ">=1.1.3@stable" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*@dev" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.0.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2013-09-10 08:14:32" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2013-10-10 15:34:57" + }, + { + "name": "phpunit/php-text-template", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5180896f51c5b3648ac946b05f9ec02be78a0b23", + "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2012-10-31 18:15:28" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2013-08-02 07:42:54" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", + "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2013-09-13 04:58:23" + }, + { + "name": "phpunit/phpunit", + "version": "3.7.29", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "faeb2d9f15dc83830d2db5e4c67acf1d68c9b5ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/faeb2d9f15dc83830d2db5e4c67acf1d68c9b5ac", + "reference": "faeb2d9f15dc83830d2db5e4c67acf1d68c9b5ac", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": "~1.2.1", + "phpunit/php-file-iterator": ">=1.3.1", + "phpunit/php-text-template": ">=1.1.1", + "phpunit/php-timer": ">=1.0.4", + "phpunit/phpunit-mock-objects": "~1.2.0", + "symfony/yaml": "~2.0" + }, + "require-dev": { + "pear-pear/pear": "1.9.4" + }, + "suggest": { + "ext-json": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "phpunit/php-invoker": ">=1.1.0,<1.2.0" + }, + "bin": [ + "composer/bin/phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2014-01-15 06:46:38" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", + "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": ">=1.1.1@stable" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2013-01-13 10:24:48" + }, + { + "name": "symfony/yaml", + "version": "v2.4.1", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "4e1a237fc48145fae114b96458d799746ad89aa0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/4e1a237fc48145fae114b96458d799746ad89aa0", + "reference": "4e1a237fc48145fae114b96458d799746ad89aa0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2013-12-28 08:12:03" + }, + { + "name": "zendframework/zend-filter", + "version": "2.2.5", + "target-dir": "Zend/Filter", + "source": { + "type": "git", + "url": "https://github.com/zendframework/Component_ZendFilter.git", + "reference": "0dbe1c10822a340a253a99147bb004978915f641" + }, + "dist": { + "type": "zip", + "url": "https://packages.zendframework.com/composer/zendframework-zend-filter-0dbe1c10822a340a253a99147bb004978915f641-zip-8a2bc1.zip", + "reference": "2.2.5", + "shasum": "ba078a81b2b0e194eb3bc27eb2b791ccc00eb6b7" + }, + "require": { + "php": ">=5.3.3", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "zendframework/zend-crypt": "self.version" + }, + "suggest": { + "zendframework/zend-crypt": "Zend\\Crypt component", + "zendframework/zend-i18n": "Zend\\I18n component", + "zendframework/zend-stdlib": "Zend\\Stdlib component", + "zendframework/zend-uri": "Zend\\Uri component for UriNormalize filter", + "zendframework/zend-validator": "Zend\\Validator component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev", + "dev-develop": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Zend\\Filter\\": "" + } + }, + "license": [ + "BSD-3-Clause" + ], + "description": "provides a set of commonly needed data filters", + "keywords": [ + "filter", + "zf2" + ], + "support": { + "source": "https://github.com/zendframework/Component_ZendFilter/tree/release-2.2.5" + }, + "time": "2013-10-20 06:00:14" + }, + { + "name": "zendframework/zend-stdlib", + "version": "2.2.5", + "target-dir": "Zend/Stdlib", + "source": { + "type": "git", + "url": "https://github.com/zendframework/Component_ZendStdlib.git", + "reference": "f440ecfc828d61d620662a03987c8287e1e4801e" + }, + "dist": { + "type": "zip", + "url": "https://packages.zendframework.com/composer/zendframework-zend-stdlib-f440ecfc828d61d620662a03987c8287e1e4801e-zip-7e3c9b.zip", + "reference": "2.2.5", + "shasum": "d142b30e831141bcf5ed77e0d3ebd9356afcf9ce" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "zendframework/zend-eventmanager": "To support aggregate hydrator usage", + "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev", + "dev-develop": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Zend\\Stdlib\\": "" + } + }, + "license": [ + "BSD-3-Clause" + ], + "description": " ", + "keywords": [ + "stdlib", + "zf2" + ], + "support": { + "source": "https://github.com/zendframework/Component_ZendStdlib/tree/release-2.2.5" + }, + "time": "2013-10-20 06:00:20" + } + ], + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": [ + + ], + "platform": { + "php": ">=5.3.2" + }, + "platform-dev": [ + + ] +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..83ec053 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,25 @@ + + + + + + tests/DMS + + + + + + src/DMS + + + tests + vendor + + + + + + + + + diff --git a/Exception/FilterException.php b/src/DMS/Filter/Exception/FilterException.php similarity index 100% rename from Exception/FilterException.php rename to src/DMS/Filter/Exception/FilterException.php diff --git a/src/DMS/Filter/Exception/InvalidCallbackException.php b/src/DMS/Filter/Exception/InvalidCallbackException.php new file mode 100644 index 0000000..b65806c --- /dev/null +++ b/src/DMS/Filter/Exception/InvalidCallbackException.php @@ -0,0 +1,8 @@ +options; - } + } } diff --git a/Filter.php b/src/DMS/Filter/Filter.php similarity index 55% rename from Filter.php rename to src/DMS/Filter/Filter.php index 6d91532..4e9ab18 100644 --- a/Filter.php +++ b/src/DMS/Filter/Filter.php @@ -5,29 +5,45 @@ /** * Filter Object, responsible for retrieving the filtering rules * for the object and applying them - * + * * @package DMS * @subpackage Filter - * + * + */ +use DMS\Filter\Filters\Loader\FilterLoaderInterface; + +/** + * Class Filter + * + * Executor, receives objects that need filtering and executes attached rules. + * + * @package DMS\Filter */ class Filter implements FilterInterface { /** * - * @var Mapping\ClassMetadataFactory + * @var Mapping\ClassMetadataFactory */ protected $metadataFactory; - + + /** + * @var FilterLoaderInterface + */ + protected $filterLoader; + /** * Constructor - * - * @param Mapping\ClassMetadataFactory $metadataFactory + * + * @param Mapping\ClassMetadataFactory $metadataFactory + * @param FilterLoaderInterface $filterLoader */ - public function __construct(Mapping\ClassMetadataFactory $metadataFactory) + public function __construct(Mapping\ClassMetadataFactory $metadataFactory, $filterLoader) { $this->metadataFactory = $metadataFactory; + $this->filterLoader = $filterLoader; } - + /** * {@inheritDoc} */ @@ -43,18 +59,19 @@ public function filterProperty($object, $property) { $this->walkObject($object, $property); } - + /** * {@inheritDoc} */ - public function filterValue($value, $filter) + public function filterValue($value, $rule) { - - if ($filter instanceof Rules\Rule) { - return $filter->applyFilter($value); + + if ($rule instanceof Rules\Rule) { + $filter = $this->filterLoader->getFilterForRule($rule); + return $filter->apply($rule, $value); } - - return $this->walkRuleChain($value, $filter); + + return $this->walkRuleChain($value, $rule); } /** @@ -64,52 +81,52 @@ public function getMetadataFactory() { return $this->metadataFactory; } - + /** * Iterates over annotated properties in an object filtering the selected * values - * + * * @param object $object * @param string $limitProperty */ - protected function walkObject($object, $limitProperty = null) { - - if ( $object === null ) { + protected function walkObject($object, $limitProperty = null) + { + if ($object === null) { return; } - + $metadata = $this->metadataFactory->getClassMetadata(get_class($object)); - + //Get a Object Handler/Walker - $walker = new ObjectWalker($object); - + $walker = new ObjectWalker($object, $this->filterLoader); + //Get all filtered properties or limit with selected - $properties = ($limitProperty !== null)? array($limitProperty) : $metadata->getFilteredProperties(); - + $properties = ($limitProperty !== null) ? array($limitProperty) : $metadata->getFilteredProperties(); + //Iterate over properties with filters - foreach( $properties as $property ) { - + foreach ($properties as $property) { + $walker->applyFilterRules($property, $metadata->getPropertyRules($property)); - + } - + } - + /** * Iterates over an array of filters applying all to the value - * + * * @param mixed $value - * @param array $filters - * @return mixed + * @param array $rules + * @return mixed */ - protected function walkRuleChain($value, $filters) + protected function walkRuleChain($value, $rules) { - - foreach($filters as $filter) { - $value = $filter->applyFilter($value); + foreach ($rules as $rule) { + $filter = $this->filterLoader->getFilterForRule($rule); + $value = $filter->apply($rule, $value); } - + return $value; } - -} \ No newline at end of file + +} diff --git a/FilterInterface.php b/src/DMS/Filter/FilterInterface.php similarity index 88% rename from FilterInterface.php rename to src/DMS/Filter/FilterInterface.php index 9ef5fe9..a614472 100644 --- a/FilterInterface.php +++ b/src/DMS/Filter/FilterInterface.php @@ -4,7 +4,7 @@ /** * Filters the values of a given object - * + * * @package DMS * @subpackage Filter */ @@ -13,35 +13,35 @@ interface FilterInterface /** * Iterates over the properties of the object applying filters and * replacing values - * + * * @param mixed $object */ public function filterEntity($object); - + /** * Filters a specific property in an object, replacing the current value - * + * * @param mixed $object * @param string $property */ public function filterProperty($object, $property); /** - * Runs a given value through one or more filter rules returning the + * Runs a given value through one or more filter rules returning the * filtered value - * + * * @param mixed $value * @param array|Rules\Rule $filter - * + * * @return mixed */ - public function filterValue($value, $filter); - + public function filterValue($value, $filter); + /** * Retrieves the metadata factory for class metdatas - * + * * @return Mapping\ClassMetadataFactoryInterface */ public function getMetadataFactory(); - -} \ No newline at end of file + +} diff --git a/src/DMS/Filter/Filters/Alnum.php b/src/DMS/Filter/Filters/Alnum.php new file mode 100644 index 0000000..67a2a6b --- /dev/null +++ b/src/DMS/Filter/Filters/Alnum.php @@ -0,0 +1,30 @@ +allowWhitespace)? " ":""; + + $rule->unicodePattern = '/[^\p{L}\p{N}' . $whitespaceChar . ']/u'; + $rule->pattern = '/[^a-zA-Z0-9' . $whitespaceChar . ']/'; + + return parent::apply($rule, $value); + } +} diff --git a/src/DMS/Filter/Filters/Alpha.php b/src/DMS/Filter/Filters/Alpha.php new file mode 100644 index 0000000..acb95e9 --- /dev/null +++ b/src/DMS/Filter/Filters/Alpha.php @@ -0,0 +1,30 @@ +allowWhitespace)? " ":""; + + $rule->unicodePattern = '/[^\p{L}' . $whitespaceChar . ']/u'; + $rule->pattern = '/[^a-zA-Z' . $whitespaceChar . ']/'; + + return parent::apply($rule, $value); + } +} diff --git a/src/DMS/Filter/Filters/BaseFilter.php b/src/DMS/Filter/Filters/BaseFilter.php new file mode 100644 index 0000000..0be8701 --- /dev/null +++ b/src/DMS/Filter/Filters/BaseFilter.php @@ -0,0 +1,29 @@ +currentObject = $object; + } + + /** + * Retrieves the current Object to be used + * + * @return object | null + */ + public function getCurrentObject() + { + return $this->currentObject; + } + + /** + * {@inheritDoc} + * + * @param CallbackRule $rule + */ + public function apply(Rule $rule, $value) + { + $type = $rule->getInputType(); + + if ($type == CallbackRule::SELF_METHOD_TYPE) { + return $this->useObjectMethod($rule->callback, $value); + } + + if ($type == CallbackRule::CALLABLE_TYPE) { + return $this->useCallable($rule->callback, $value); + } + + if ($type == CallbackRule::CLOSURE_TYPE) { + return $this->useClosure($rule->callback, $value); + } + + throw new InvalidCallbackException("Unsupported callback provided, failed to filter property"); + } + + /** + * Filters by executing a method in the object + * + * @param string $method + * @param mixed $value + * @throws \DMS\Filter\Exception\FilterException + * @throws \DMS\Filter\Exception\InvalidCallbackException + * @returns mixed + */ + protected function useObjectMethod($method, $value) + { + $currentObject = $this->getCurrentObject(); + + if ($currentObject === null) { + throw new FilterException( + "The target object was not provided to the filter, can't execute method. Please report this." + ); + } + + if (! method_exists($currentObject, $method)) { + throw new InvalidCallbackException( + sprintf( + "CallbackFilter: Method '%s' not found in object of type '%s'", + $method, + get_class($currentObject) + ) + ); + } + + return $currentObject->$method($value); + } + + /** + * Filters using a callable. + * + * @param callable $callable + * @param mixed $value + * @throws \DMS\Filter\Exception\InvalidCallbackException + * @return mixed + */ + protected function useCallable($callable, $value) + { + if (! is_callable($callable, false, $input)) { + throw new InvalidCallbackException("The callable $input could not be resolved."); + } + + return call_user_func($callable, $value); + } + + /** + * @param Closure $closure + * @param mixed $value + * @throws \DMS\Filter\Exception\InvalidCallbackException + * @return mixed + */ + protected function useClosure($closure, $value) + { + if (! $closure instanceof Closure) { + throw new InvalidCallbackException("CallbackFilter: the provided closure is invalid"); + } + + return $closure($value); + } + +} diff --git a/src/DMS/Filter/Filters/Digits.php b/src/DMS/Filter/Filters/Digits.php new file mode 100644 index 0000000..8a1c22c --- /dev/null +++ b/src/DMS/Filter/Filters/Digits.php @@ -0,0 +1,30 @@ +allowWhitespace)? " ":""; + + $rule->unicodePattern = '/[^\p{N}' . $whitespaceChar . ']/'; + $rule->pattern = '/[^0-9' . $whitespaceChar . ']/'; + + return parent::apply($rule, $value); + } +} diff --git a/Rules/Float.php b/src/DMS/Filter/Filters/Float.php similarity index 62% rename from Rules/Float.php rename to src/DMS/Filter/Filters/Float.php index c91646d..a1808a8 100644 --- a/Rules/Float.php +++ b/src/DMS/Filter/Filters/Float.php @@ -1,28 +1,28 @@ flags, $rule->encoding, $rule->doubleEncode); + } + +} diff --git a/src/DMS/Filter/Filters/Int.php b/src/DMS/Filter/Filters/Int.php new file mode 100644 index 0000000..3242ea8 --- /dev/null +++ b/src/DMS/Filter/Filters/Int.php @@ -0,0 +1,24 @@ +getFilter(); + + if (class_exists($filterIdentifier)) { + return new $filterIdentifier; + } + + $error = "Unable to locate filter for: $filterIdentifier defined in " . get_class($rule); + throw new \UnexpectedValueException($error); + } +} diff --git a/src/DMS/Filter/Filters/Loader/FilterLoaderInterface.php b/src/DMS/Filter/Filters/Loader/FilterLoaderInterface.php new file mode 100644 index 0000000..1f8a772 --- /dev/null +++ b/src/DMS/Filter/Filters/Loader/FilterLoaderInterface.php @@ -0,0 +1,25 @@ +regexp, $rule->replacement, $value); + } +} diff --git a/Rules/RegExp.php b/src/DMS/Filter/Filters/RegExp.php similarity index 62% rename from Rules/RegExp.php rename to src/DMS/Filter/Filters/RegExp.php index 1a4018b..c865343 100644 --- a/Rules/RegExp.php +++ b/src/DMS/Filter/Filters/RegExp.php @@ -1,18 +1,18 @@ checkUnicodeSupport() && $this->unicodePattern !== null) - ? $this->unicodePattern - : $this->pattern; + $pattern = ($this->checkUnicodeSupport() && $rule->unicodePattern !== null) + ? $rule->unicodePattern + : $rule->pattern; return preg_replace($pattern, '', $value); } @@ -61,4 +49,4 @@ public function checkUnicodeSupport() return self::$unicodeEnabled; } -} \ No newline at end of file +} diff --git a/src/DMS/Filter/Filters/StripNewlines.php b/src/DMS/Filter/Filters/StripNewlines.php new file mode 100644 index 0000000..0f7c960 --- /dev/null +++ b/src/DMS/Filter/Filters/StripNewlines.php @@ -0,0 +1,23 @@ +allowed); + } +} diff --git a/src/DMS/Filter/Filters/ToLower.php b/src/DMS/Filter/Filters/ToLower.php new file mode 100644 index 0000000..63b17bd --- /dev/null +++ b/src/DMS/Filter/Filters/ToLower.php @@ -0,0 +1,60 @@ +useEncoding($rule)) { + return mb_strtolower((string) $value, $rule->encoding); + } + + return strtolower((string) $value); + } + + /** + * Verify is encoding is set and if we have the proper + * function to use it + * + * @param \DMS\Filter\Rules\ToLower $rule + * + * @throws \DMS\Filter\Exception\FilterException + * @return boolean + */ + public function useEncoding($rule) + { + if ($rule->encoding === null) { + return false; + } + + if (! function_exists('mb_strtolower')) { + throw new FilterException('mbstring is required to use ToLower with an encoding.'); + } + + $encodings = array_map('strtolower', mb_list_encodings()); + + if (!in_array(strtolower($rule->encoding), $encodings)) { + throw new FilterException( + "mbstring does not support the '".$rule->encoding."' encoding" + ); + } + + return true; + } +} diff --git a/src/DMS/Filter/Filters/ToUpper.php b/src/DMS/Filter/Filters/ToUpper.php new file mode 100644 index 0000000..29a8c76 --- /dev/null +++ b/src/DMS/Filter/Filters/ToUpper.php @@ -0,0 +1,61 @@ +useEncoding($rule)) { + return mb_strtoupper((string) $value, $rule->encoding); + } + + return strtoupper((string) $value); + } + + /** + * Verify is encoding is set and if we have the proper + * function to use it + * + * @param \DMS\Filter\Rules\ToUpper $rule + * + * @throws \DMS\Filter\Exception\FilterException + * @return boolean + */ + public function useEncoding($rule) + { + if ($rule->encoding === null) { + return false; + } + + if (!function_exists('mb_strtoupper')) { + throw new FilterException( + 'mbstring is required to use ToLower with an encoding.'); + } + + $this->encoding = (string) $rule->encoding; + $encodings = array_map('strtolower', mb_list_encodings()); + + if (!in_array(strtolower($rule->encoding), $encodings)) { + throw new FilterException( + "mbstring does not support the '".$rule->encoding."' encoding" + ); + } + + return true; + } +} diff --git a/src/DMS/Filter/Filters/Trim.php b/src/DMS/Filter/Filters/Trim.php new file mode 100644 index 0000000..a460785 --- /dev/null +++ b/src/DMS/Filter/Filters/Trim.php @@ -0,0 +1,31 @@ +charlist === null) { + return trim($value); + } + + return trim($value, $rule->charlist); + } +} diff --git a/src/DMS/Filter/Filters/Zend.php b/src/DMS/Filter/Filters/Zend.php new file mode 100644 index 0000000..d815365 --- /dev/null +++ b/src/DMS/Filter/Filters/Zend.php @@ -0,0 +1,59 @@ +getZendInstance($rule->class, $rule->zendOptions)->filter($value); + } + + /** + * Instantiates a configured Zend Filter, if it exists + * + * @param string $class + * @param array $options + * @return \Zend\Filter\FilterInterface + * @throws \DMS\Filter\Exception\InvalidZendFilterException + */ + public function getZendInstance($class, $options) + { + if (strpos($class, 'Zend\Filter') === false) { + $class = "Zend\Filter\\".$class; + } + + if (! class_exists($class)) { + throw new InvalidZendFilterException("Could not find or autoload: $class"); + } + + try { + new \ReflectionMethod($class, 'setOptions'); + + $filter = new $class(); + $filter->setOptions($options); + + return $filter; + } catch (\ReflectionException $e) { + return new $class($options); + } + } + +} diff --git a/Mapping/ClassMetadata.php b/src/DMS/Filter/Mapping/ClassMetadata.php similarity index 78% rename from Mapping/ClassMetadata.php rename to src/DMS/Filter/Mapping/ClassMetadata.php index d1131d6..27c8e3f 100644 --- a/Mapping/ClassMetadata.php +++ b/src/DMS/Filter/Mapping/ClassMetadata.php @@ -2,9 +2,11 @@ namespace DMS\Filter\Mapping; +use DMS\Filter\Rules\Rule; + /** * Represents a class that has Annotations - * + * * @package DMS * @subpackage Filter */ @@ -14,28 +16,28 @@ class ClassMetadata implements ClassMetadataInterface * @var string */ public $className; - + /** * Properties that contain filtering rules * @var array */ public $filteredProperties = array(); - + /** - * @var ReflectionClass + * @var \ReflectionClass */ private $reflClass; - + /** * Constructor - * - * @param string $class + * + * @param string $class */ public function __construct($class) { $this->className = $class; } - + /** * {@inheritDoc} */ @@ -49,41 +51,41 @@ public function getFilteredProperties() */ public function getPropertyRules($property) { - if ( ! isset($this->filteredProperties[$property])) return; - + if (! isset($this->filteredProperties[$property])) { + return null; + } + return $this->filteredProperties[$property]['rules']; } /** * {@inheritDoc} - * - * @todo bend this method into calestenics + * + * @todo bend this method into calesthenics */ - public function mergeRules($metadata) + public function mergeRules(ClassMetadataInterface $metadata) { - foreach ( $metadata->getFilteredProperties() as $property ) { - + foreach ($metadata->getFilteredProperties() as $property) { foreach ($metadata->getPropertyRules($property) as $rule) { $this->addPropertyRule($property, clone $rule); } - } } /** * {@inheritDoc} - * + * * @todo check for duplicate rules */ - public function addPropertyRule($property, $rule) + public function addPropertyRule($property, Rule $rule) { if (!isset ($this->filteredProperties[$property])) { $this->filteredProperties[$property] = array('rules' => array()); } - + $this->filteredProperties[$property]['rules'][] = $rule; } - + /** * {@inheritDoc} */ @@ -91,7 +93,7 @@ public function getClassName() { return $this->className; } - + /** * {@inheritDoc} */ @@ -103,9 +105,9 @@ public function getReflectionClass() return $this->reflClass; } - -} \ No newline at end of file + +} diff --git a/Mapping/ClassMetadataFactory.php b/src/DMS/Filter/Mapping/ClassMetadataFactory.php similarity index 74% rename from Mapping/ClassMetadataFactory.php rename to src/DMS/Filter/Mapping/ClassMetadataFactory.php index 088c21a..b770e54 100644 --- a/Mapping/ClassMetadataFactory.php +++ b/src/DMS/Filter/Mapping/ClassMetadataFactory.php @@ -6,154 +6,156 @@ /** * Responsible for loading metadata for selected classes - * + * * @package DMS * @subpackage Filter */ class ClassMetadataFactory implements ClassMetadataFactoryInterface { /** - * @var Loader\LoaderInterface + * @var Loader\LoaderInterface */ protected $loader; - + /** - * @var Doctrine\Common\Cache\Cache + * @var \Doctrine\Common\Cache\Cache */ protected $cache; - + /** * @var array */ protected $parsedClasses = array(); - + /** * Constructor * Receives a Loader and a Doctrine Compatible cache instance - * + * * @param Loader\LoaderInterface $loader - * @param Cache $cache + * @param Cache $cache */ public function __construct(Loader\LoaderInterface $loader, Cache $cache = null) { $this->loader = $loader; $this->cache = $cache; } - + /** * {@inheritDoc} */ public function getClassMetadata($class) { - + $class = ltrim($class, '\\'); - + //Already parsed - if ( $this->isParsed($class) ) { + if ($this->isParsed($class)) { return $this->getParsedClass($class); } - + //Check Cache for it if ($this->cache !== null && $this->cache->contains($class)) { - + $this->setParsedClass($class, $this->cache->fetch($class)); return $this->getParsedClass($class); - + } - + //Parse unloaded and uncached class return $this->parseClassMetadata($class); } - + /** * Reads class metadata for a new and unparsed class - * + * * @param string $class - * @return ClassMetadata + * @return ClassMetadataInterface */ - private function parseClassMetadata($class) + private function parseClassMetadata($class) { $metadata = new ClassMetadata($class); - + //Load up parent and interfaces $this->loadParentMetadata($metadata); $this->loadInterfaceMetadata($metadata); - + //Load Annotations from Reader $this->loader->loadClassMetadata($metadata); - + //Store internally $this->setParsedClass($class, $metadata); - + if ($this->cache !== null) { $this->cache->save($class, $metadata); } - - return $metadata; + + return $metadata; } - + /** * Checks if a class has already been parsed - * + * * @param string $class - * @return booelan + * @return boolean */ private function isParsed($class) { return isset($this->parsedClasses[$class]); } - + /** * Retrieves data from a class already parsed - * + * * @param string $class - * @return ClassMetadata + * @return ClassMetadataInterface */ private function getParsedClass($class) { - if ( ! $this->isParsed($class)) return; - + if (! $this->isParsed($class)) { + return null; + } + return $this->parsedClasses[$class]; } - + /** * Stores data from a parsed class - * + * * @param string $class - * @param ClassMetadata $metadata + * @param ClassMetadataInterface $metadata */ - private function setParsedClass($class, $metadata) + private function setParsedClass($class, ClassMetadataInterface $metadata) { $this->parsedClasses[$class] = $metadata; } - + /** * Checks if the class being parsed has a parent and cascades parsing * to its parent - * - * @param ClassMetadata $metadata + * + * @param ClassMetadataInterface $metadata */ - protected function loadParentMetadata($metadata) + protected function loadParentMetadata(ClassMetadataInterface $metadata) { $parent = $metadata->getReflectionClass()->getParentClass(); - + if ($parent) { $metadata->mergeRules($this->getClassMetadata($parent->getName())); } } - + /** * Checks if the object has interfaces and cascades parsing of annotatiosn * to all the interfaces - * - * @param ClassMetadata $metadata + * + * @param ClassMetadataInterface $metadata */ - protected function loadInterfaceMetadata($metadata) + protected function loadInterfaceMetadata(ClassMetadataInterface $metadata) { - foreach( $metadata->getReflectionClass()->getInterfaces() as $interface ) { - + foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) { + $metadata->mergeRules($this->getClassMetadata($interface->getName())); - + } } -} \ No newline at end of file +} diff --git a/Mapping/ClassMetadataFactoryInterface.php b/src/DMS/Filter/Mapping/ClassMetadataFactoryInterface.php similarity index 100% rename from Mapping/ClassMetadataFactoryInterface.php rename to src/DMS/Filter/Mapping/ClassMetadataFactoryInterface.php diff --git a/Mapping/ClassMetadataInterface.php b/src/DMS/Filter/Mapping/ClassMetadataInterface.php similarity index 75% rename from Mapping/ClassMetadataInterface.php rename to src/DMS/Filter/Mapping/ClassMetadataInterface.php index 3d51bee..b30c262 100644 --- a/Mapping/ClassMetadataInterface.php +++ b/src/DMS/Filter/Mapping/ClassMetadataInterface.php @@ -2,9 +2,12 @@ namespace DMS\Filter\Mapping; +use DMS\Filter\Rules\Rule; +use ReflectionClass; + /** * Method required by a ClassMetadata class - * + * * @package DMS * @subpackage Filter */ @@ -13,45 +16,45 @@ interface ClassMetadataInterface /** * Retrieve a list of the object's properties that have filters attached * to them - * + * * @return array */ public function getFilteredProperties(); - + /** * Retrieve s list of filtering rules attached to a property - * + * * @param string $property * @return array */ public function getPropertyRules($property); - + /** * Merges rules from another metadata object into this one - * - * @param ClassMetadata $metadata + * + * @param ClassMetadataInterface $metadata */ - public function mergeRules($metadata); - + public function mergeRules(ClassMetadataInterface $metadata); + /** * Get name of class represented in this Metadata object - * - * @return string + * + * @return string */ public function getClassName(); - + /** * Returns a ReflectionClass instance for this class. * * @return ReflectionClass */ public function getReflectionClass(); - + /** * Adds a new rule to a property - * + * * @param string $property - * @param Rules\Rule $rule + * @param Rule $rule */ - public function addPropertyRule($property, $rule); -} \ No newline at end of file + public function addPropertyRule($property, Rule $rule); +} diff --git a/Mapping/Loader/AnnotationLoader.php b/src/DMS/Filter/Mapping/Loader/AnnotationLoader.php similarity index 61% rename from Mapping/Loader/AnnotationLoader.php rename to src/DMS/Filter/Mapping/Loader/AnnotationLoader.php index 9e6618b..a167884 100644 --- a/Mapping/Loader/AnnotationLoader.php +++ b/src/DMS/Filter/Mapping/Loader/AnnotationLoader.php @@ -2,14 +2,16 @@ namespace DMS\Filter\Mapping\Loader; -use Doctrine\Common\Annotations\Reader, - Doctrine\Common\Annotations\AnnotationRegistry, - DMS\Filter\Rules, - DMS\Filter\Mapping; +use DMS\Filter\Mapping\ClassMetadataInterface; +use Doctrine\Common\Annotations\Reader; +use Doctrine\Common\Annotations\AnnotationRegistry; +use DMS\Filter\Rules; +use DMS\Filter\Mapping; +use ReflectionProperty; /** * Loader that reads filtering data from Annotations - * + * * @package DMS * @subpackage Filter */ @@ -17,54 +19,53 @@ class AnnotationLoader implements LoaderInterface { /** * - * @var Doctrine\Common\Annotations\Reader + * @var Reader */ protected $reader; - + /** * Constructor - * - * @param Doctrine\Common\Annotations\Reader $reader + * + * @param Reader $reader */ public function __construct(Reader $reader) { $this->reader = $reader; - + //Register Filter Rules Annotation Namespace AnnotationRegistry::registerAutoloadNamespace('DMS\Filter\Rules', __DIR__ . '/../../../../'); - + } - + /** * Loads annotations data present in the class, using a Doctrine * annotation reader - * - * @param ClassMetadata $metadata + * + * @param ClassMetadataInterface $metadata + * @return bool|void */ - public function loadClassMetadata(Mapping\ClassMetadataInterface $metadata) + public function loadClassMetadata(ClassMetadataInterface $metadata) { - + $reflClass = $metadata->getReflectionClass(); - + //Iterate over properties to get annotations - foreach($reflClass->getProperties() as $property) { - + foreach ($reflClass->getProperties() as $property) { + $this->readProperty($property, $metadata); - + } - + } - + /** * Reads annotations for a selected property in the class - * + * * @param ReflectionProperty $property - * @param ClassMetadata $metadata + * @param ClassMetadataInterface $metadata */ - private function readProperty($property, $metadata) + private function readProperty(ReflectionProperty $property, ClassMetadataInterface $metadata) { - $reflClass = $metadata->getReflectionClass(); - // Skip if this property is not from this class if ($property->getDeclaringClass()->getName() != $metadata->getClassName() @@ -73,11 +74,13 @@ private function readProperty($property, $metadata) } //Iterate over all annotations - foreach($this->reader->getPropertyAnnotations($property) as $rule) { + foreach ($this->reader->getPropertyAnnotations($property) as $rule) { //Skip is its not a rule - if ( ! $rule instanceof Rules\Rule ) continue; - + if (! $rule instanceof Rules\Rule) { + continue; + } + //Add Rule $metadata->addPropertyRule($property->getName(), $rule); @@ -85,4 +88,4 @@ private function readProperty($property, $metadata) } -} \ No newline at end of file +} diff --git a/Mapping/Loader/LoaderInterface.php b/src/DMS/Filter/Mapping/Loader/LoaderInterface.php similarity index 55% rename from Mapping/Loader/LoaderInterface.php rename to src/DMS/Filter/Mapping/Loader/LoaderInterface.php index 6791cbe..51fbc94 100644 --- a/Mapping/Loader/LoaderInterface.php +++ b/src/DMS/Filter/Mapping/Loader/LoaderInterface.php @@ -2,11 +2,11 @@ namespace DMS\Filter\Mapping\Loader; -use DMS\Filter\Mapping; +use DMS\Filter\Mapping\ClassMetadataInterface; /** * Interface for a Loader - * + * * @package DMS * @subpackage Filter */ @@ -15,9 +15,9 @@ interface LoaderInterface /** * Load a Class Metadata. * - * @param ClassMetadata $metadata A metadata + * @param ClassMetadataInterface $metadata A metadata * * @return Boolean */ - function loadClassMetadata(Mapping\ClassMetadataInterface $metadata); -} \ No newline at end of file + public function loadClassMetadata(ClassMetadataInterface $metadata); +} diff --git a/ObjectWalker.php b/src/DMS/Filter/ObjectWalker.php similarity index 59% rename from ObjectWalker.php rename to src/DMS/Filter/ObjectWalker.php index 0cd7a24..4f6bad6 100644 --- a/ObjectWalker.php +++ b/src/DMS/Filter/ObjectWalker.php @@ -2,10 +2,13 @@ namespace DMS\Filter; +use DMS\Filter\Filters\Loader\FilterLoaderInterface; +use DMS\Filter\Filters\ObjectAwareFilter; + /** * Walks over the properties of an object applying the filters * that were defined for them - * + * * @package DMS * @subpackage Filter */ @@ -15,88 +18,107 @@ class ObjectWalker * @var object */ protected $object; - + /** - * @var ReflectionClass + * @var \ReflectionClass */ protected $reflClass; + /** + * @var FilterLoaderInterface + */ + protected $filterLoader; + /** * Constructor - * - * @param object $object + * + * @param object $object + * @param FilterLoaderInterface $filterLoader */ - public function __construct( $object ) + public function __construct($object, $filterLoader) { - $this->object = $object; - $this->reflClass = new \ReflectionClass($object); + $this->object = $object; + $this->reflClass = new \ReflectionClass($object); + $this->filterLoader = $filterLoader; } - + /** * Applies the selected rules to a property in the object - * + * * @param string $property - * @param array $filterRules + * @param array $filterRules */ public function applyFilterRules($property, $filterRules = array()) { - foreach($filterRules as $rule ) { + foreach ($filterRules as $rule) { $this->applyFilterRule($property, $rule); } } - + /** * Applies a Filtering Rule to a property - * + * * @param string $property * @param Rules\Rule $filterRule + * + * @throws \UnexpectedValueException */ - public function applyFilterRule($property, Rules\Rule $filterRule) + public function applyFilterRule($property, Rules\Rule $filterRule) { - + + if ($this->filterLoader === null) { + throw new \UnexpectedValueException("A FilterLoader must be provided"); + } + $value = $this->getPropertyValue($property); - - $filteredValue = $filterRule->applyFilter($value); - + + $filter = $this->filterLoader->getFilterForRule($filterRule); + + if ($filter instanceof ObjectAwareFilter) { + $filter->setCurrentObject($this->object); + } + + $filteredValue = $filter->apply($filterRule, $value); + $this->setPropertyValue($property, $filteredValue); - + } - + /** * Retrieves the value of the property, overcoming visibility problems - * - * @param string $property - * @return mixed + * + * @param string $propertyName + * @return mixed */ private function getPropertyValue($propertyName) { return $this->getAccessibleReflectionProperty($propertyName) ->getValue($this->object); } - + /** * Overrides the value of a property, overcoming visibility problems - * - * @param string $property - * @param mixed $value + * + * @param string$propertyName + * @param mixed $value */ private function setPropertyValue($propertyName, $value) { $this->getAccessibleReflectionProperty($propertyName) ->setValue($this->object, $value); } - + /** * Retrieves a property from the object and makes it visible - * + * * @param string $propertyName - * @return ReflectionProperty + * @return \ReflectionProperty */ private function getAccessibleReflectionProperty($propertyName) { $property = $this->reflClass->getProperty($propertyName); $property->setAccessible(true); - + return $property; } -} \ No newline at end of file +} diff --git a/Rules/Alnum.php b/src/DMS/Filter/Rules/Alnum.php similarity index 50% rename from Rules/Alnum.php rename to src/DMS/Filter/Rules/Alnum.php index 5087ca4..74581e9 100644 --- a/Rules/Alnum.php +++ b/src/DMS/Filter/Rules/Alnum.php @@ -20,20 +20,6 @@ class Alnum extends RegExp */ public $allowWhitespace = true; - /** - * {@inheritDoc} - */ - public function applyFilter($value) - { - //Check for Whitespace support - $whitespaceChar = ($this->allowWhitespace)? " ":""; - - $this->unicodePattern = '/[^\p{L}\p{N}' . $whitespaceChar . ']/u'; - $this->pattern = '/[^a-zA-Z0-9' . $whitespaceChar . ']/'; - - return parent::applyFilter($value); - } - /** * {@inheritDoc} */ @@ -42,4 +28,4 @@ public function getDefaultOption() return 'allowWhitespace'; } -} \ No newline at end of file +} diff --git a/Rules/Alpha.php b/src/DMS/Filter/Rules/Alpha.php similarity index 50% rename from Rules/Alpha.php rename to src/DMS/Filter/Rules/Alpha.php index fd829fd..d9c90c6 100644 --- a/Rules/Alpha.php +++ b/src/DMS/Filter/Rules/Alpha.php @@ -20,20 +20,6 @@ class Alpha extends RegExp */ public $allowWhitespace = true; - /** - * {@inheritDoc} - */ - public function applyFilter($value) - { - //Check for Whitespace support - $whitespaceChar = ($this->allowWhitespace)? " ":""; - - $this->unicodePattern = '/[^\p{L}' . $whitespaceChar . ']/u'; - $this->pattern = '/[^a-zA-Z' . $whitespaceChar . ']/'; - - return parent::applyFilter($value); - } - /** * {@inheritDoc} */ @@ -42,4 +28,4 @@ public function getDefaultOption() return 'allowWhitespace'; } -} \ No newline at end of file +} diff --git a/Rules/Boolean.php b/src/DMS/Filter/Rules/Boolean.php similarity index 52% rename from Rules/Boolean.php rename to src/DMS/Filter/Rules/Boolean.php index c9a8423..526c474 100644 --- a/Rules/Boolean.php +++ b/src/DMS/Filter/Rules/Boolean.php @@ -4,20 +4,13 @@ /** * Boolean Rule - * + * * @package DMS * @subpackage Filter - * + * * @Annotation */ class Boolean extends Rule { - /** - * {@inheritDoc} - */ - public function applyFilter($value) - { - return (boolean) $value; - } -} \ No newline at end of file +} diff --git a/src/DMS/Filter/Rules/Callback.php b/src/DMS/Filter/Rules/Callback.php new file mode 100644 index 0000000..3ecc21b --- /dev/null +++ b/src/DMS/Filter/Rules/Callback.php @@ -0,0 +1,60 @@ +callback instanceof Closure: + return self::CLOSURE_TYPE; + + case is_callable($this->callback, false): + return self::CALLABLE_TYPE; + + case is_string($this->callback): + return self::SELF_METHOD_TYPE; + } + + throw new InvalidCallbackException( + "The input provided for Callback filter is not supported or the callable not valid. + Please refer to the class documentation." + ); + } +} diff --git a/Rules/Digits.php b/src/DMS/Filter/Rules/Digits.php similarity index 50% rename from Rules/Digits.php rename to src/DMS/Filter/Rules/Digits.php index 115d23c..991af85 100644 --- a/Rules/Digits.php +++ b/src/DMS/Filter/Rules/Digits.php @@ -20,20 +20,6 @@ class Digits extends RegExp */ public $allowWhitespace = true; - /** - * {@inheritDoc} - */ - public function applyFilter($value) - { - //Check for Whitespace support - $whitespaceChar = ($this->allowWhitespace)? " ":""; - - $this->unicodePattern = '/[^\p{N}' . $whitespaceChar . ']/'; - $this->pattern = '/[^0-9' . $whitespaceChar . ']/'; - - return parent::applyFilter($value); - } - /** * {@inheritDoc} */ @@ -42,4 +28,4 @@ public function getDefaultOption() return 'allowWhitespace'; } -} \ No newline at end of file +} diff --git a/src/DMS/Filter/Rules/Float.php b/src/DMS/Filter/Rules/Float.php new file mode 100644 index 0000000..947fd7a --- /dev/null +++ b/src/DMS/Filter/Rules/Float.php @@ -0,0 +1,17 @@ +flags, $this->encoding, $this->doubleEncode); - } - -} \ No newline at end of file +} diff --git a/Rules/Int.php b/src/DMS/Filter/Rules/Int.php similarity index 58% rename from Rules/Int.php rename to src/DMS/Filter/Rules/Int.php index e70875d..34f2b1e 100644 --- a/Rules/Int.php +++ b/src/DMS/Filter/Rules/Int.php @@ -13,12 +13,5 @@ */ class Int extends Rule { - /** - * {@inheritDoc} - */ - public function applyFilter($value) - { - return (int) ($value); - } -} \ No newline at end of file +} diff --git a/Rules/PregReplace.php b/src/DMS/Filter/Rules/PregReplace.php similarity index 77% rename from Rules/PregReplace.php rename to src/DMS/Filter/Rules/PregReplace.php index 5af3435..e178565 100644 --- a/Rules/PregReplace.php +++ b/src/DMS/Filter/Rules/PregReplace.php @@ -27,14 +27,6 @@ class PregReplace extends Rule */ public $replacement = ""; - /** - * {@inheritDoc} - */ - public function applyFilter($value) - { - return preg_replace($this->regexp, $this->replacement, $value); - } - /** * {@inheritDoc} */ diff --git a/src/DMS/Filter/Rules/RegExp.php b/src/DMS/Filter/Rules/RegExp.php new file mode 100644 index 0000000..fac340a --- /dev/null +++ b/src/DMS/Filter/Rules/RegExp.php @@ -0,0 +1,31 @@ +parseOptions($options); - + if (count($result->invalidOptions) > 0) { throw new InvalidOptionsException( - sprintf('The options "%s" do not exist in rule %s', implode('", "', $result->invalidOptions), get_class($this)), + sprintf( + 'The options "%s" do not exist in rule %s', + implode('", "', $result->invalidOptions), + get_class($this) + ), $result->invalidOptions ); } if (count($result->missingOptions) > 0) { throw new MissingOptionsException( - sprintf('The options "%s" must be set for rule %s', implode('", "', array_keys($result->missingOptions)), get_class($this)), + sprintf( + 'The options "%s" must be set for rule %s', + implode('", "', array_keys($result->missingOptions)), + get_class($this) + ), array_keys($result->missingOptions) ); } } - + /** * Parses provided options into their properties and returns results * for the parsing process - * + * * @param mixed $options - * @return \stdClass + * @return \stdClass */ private function parseOptions($options) { $parseResult = new \stdClass(); $parseResult->invalidOptions = array(); - $parseResult->missingOptions = array_flip((array) $this->getRequiredOptions()); - + $parseResult->missingOptions = array_flip((array)$this->getRequiredOptions()); + //Doctrine parses constructor parameter into 'value' array param, restore it if (is_array($options) && count($options) == 1 && isset($options['value'])) { $options = $options['value']; } - + //Parse Option Array if (is_array($options) && count($options) > 0 && is_string(key($options))) { $this->parseOptionsArray($options, $parseResult); return $parseResult; } - + //Parse Single Value if (null !== $options && ! (is_array($options) && count($options) === 0)) { $this->parseSingleOption($options, $parseResult); return $parseResult; } - + return $parseResult; } - + /** * Parses Options in the array format - * + * * @param array $options - * @param \stdClass $result + * @param \stdClass $result */ - private function parseOptionsArray($options, $result) + private function parseOptionsArray($options, \stdClass $result) { foreach ($options as $option => $value) { - - if (!property_exists($this, $option)) { + + if (! property_exists($this, $option)) { $result->invalidOptions[] = $option; continue; } - + //Define Option $this->$option = $value; unset($result->missingOptions[$option]); } } - + /** * Parses single option received - * + * * @param string $options - * @param \stdClass $result + * @param \stdClass $result + * @throws \DMS\Filter\Exception\RuleDefinitionException */ - private function parseSingleOption($options, $result) + private function parseSingleOption($options, \stdClass $result) { $option = $this->getDefaultOption(); - //No Default set, usnsure what to do + //No Default set, unsure what to do if (null === $option) { throw new RuleDefinitionException( sprintf('No default option is configured for rule %s', get_class($this)) ); } - + //Default option points to invalid one - if (!property_exists($this, $option)) { + if (! property_exists($this, $option)) { $result->invalidOptions[] = $option; return; } - + //Define Option $this->$option = $options; unset($result->missingOptions[$option]); } - + /** * Returns the name of the required options * @@ -156,7 +154,7 @@ public function getRequiredOptions() { return array(); } - + /** * Returns the name of the default option * @@ -169,4 +167,16 @@ public function getDefaultOption() { return null; } + + /** + * Retrieves the Filter class that is responsible for executing this filter + * It may also be a service name. By default it loads a class with the + * same name from the Filters namespace. + * + * @return string + */ + public function getFilter() + { + return str_replace('Rules', 'Filters', get_class($this)); + } } diff --git a/src/DMS/Filter/Rules/StripNewlines.php b/src/DMS/Filter/Rules/StripNewlines.php new file mode 100644 index 0000000..2e6b228 --- /dev/null +++ b/src/DMS/Filter/Rules/StripNewlines.php @@ -0,0 +1,15 @@ + + * * @var string */ public $allowed = null; - - /** - * {@inheritDoc} - */ - public function applyFilter($value) - { - return strip_tags($value, $this->allowed); - } /** * {@inheritDoc} @@ -34,4 +26,4 @@ public function getDefaultOption() { return 'allowed'; } -} \ No newline at end of file +} diff --git a/src/DMS/Filter/Rules/ToLower.php b/src/DMS/Filter/Rules/ToLower.php new file mode 100644 index 0000000..fc5f047 --- /dev/null +++ b/src/DMS/Filter/Rules/ToLower.php @@ -0,0 +1,29 @@ +filter = new Filter($this->buildMetadataFactory(), new FilterLoader()); + } + + public function tearDown() + { + parent::tearDown(); + } + + public function testFilter() + { + $class = new Dummy\Classes\AnnotatedClass(); + $class->name = "Sir Isaac Newton"; + $class->nickname = "justaname"; + $class->description = "This is an apple.

Isn't it?

"; + + $classClone = clone $class; + + $this->filter->filterEntity($class); + + $this->assertNotEquals($classClone->name, $class->name); + $this->assertEquals($classClone->nickname, $class->nickname); + $this->assertNotEquals($classClone->description, $class->description); + + $this->assertNotContains(" Newton"; + $class->nickname = "justaname"; + $class->description = "This is an apple.

Isn't it?

"; + $class->surname = " Surname"; + + $classClone = clone $class; + + $this->filter->filterEntity($class); + + $this->assertNotEquals($classClone->name, $class->name); + $this->assertEquals($classClone->nickname, $class->nickname); + $this->assertNotEquals($classClone->description, $class->description); + $this->assertNotEquals($classClone->surname, $class->surname); + + $this->assertNotContains(" Newton"; + $class->description = "This is an apple.

Isn't it?

"; + + $classClone = clone $class; + + $this->filter->filterProperty($class, 'description'); + + $this->assertEquals($classClone->name, $class->name); + $this->assertNotEquals($classClone->description, $class->description); + + $this->assertContains("