commit d9ab2a082eae09e604c837e67f96da97bd870919 Author: Meritoo Date: Tue Aug 29 22:37:19 2017 +0200 First, initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8413c4c --- /dev/null +++ b/.gitignore @@ -0,0 +1,189 @@ +# ---------------------------------------------------------------------------------------------------------------------- +### Vendors +# ---------------------------------------------------------------------------------------------------------------------- +/vendor/ + + +# ---------------------------------------------------------------------------------------------------------------------- +### Composer +# ---------------------------------------------------------------------------------------------------------------------- +/composer.phar + + +# ---------------------------------------------------------------------------------------------------------------------- +### Phing +# ---------------------------------------------------------------------------------------------------------------------- +/phing/properties + + +# ---------------------------------------------------------------------------------------------------------------------- +### PHPUnit +# ---------------------------------------------------------------------------------------------------------------------- +/phpunit.xml + + +# ---------------------------------------------------------------------------------------------------------------------- +### PHP Coding Standards Fixer generated files +# ---------------------------------------------------------------------------------------------------------------------- +/.php_cs.cache + + +# ---------------------------------------------------------------------------------------------------------------------- +### Generated databases +# ---------------------------------------------------------------------------------------------------------------------- +/data/tmp +*.sql +*.sqlite + + +# ---------------------------------------------------------------------------------------------------------------------- +### Compiled source +# ---------------------------------------------------------------------------------------------------------------------- +*.com +*.class +*.dll +*.exe +*.o +*.so + + +# ---------------------------------------------------------------------------------------------------------------------- +### Shell scripts +# ---------------------------------------------------------------------------------------------------------------------- +/*.sh + + +# ---------------------------------------------------------------------------------------------------------------------- +### JetBrains template +# ---------------------------------------------------------------------------------------------------------------------- +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + + + +# ---------------------------------------------------------------------------------------------------------------------- +### NetBeans template +# ---------------------------------------------------------------------------------------------------------------------- +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +.nb-gradle/ + + +# ---------------------------------------------------------------------------------------------------------------------- +### OSX template +# ---------------------------------------------------------------------------------------------------------------------- +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +# ---------------------------------------------------------------------------------------------------------------------- +### Linux template +# ---------------------------------------------------------------------------------------------------------------------- +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + + +# ---------------------------------------------------------------------------------------------------------------------- +### Windows template +# ---------------------------------------------------------------------------------------------------------------------- +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..b9a07c6 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Common Library (GitHub) \ No newline at end of file diff --git a/.idea/Common Library (GitHub).iml b/.idea/Common Library (GitHub).iml new file mode 100644 index 0000000..9449480 --- /dev/null +++ b/.idea/Common Library (GitHub).iml @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/composerJson.xml b/.idea/composerJson.xml new file mode 100644 index 0000000..4199499 --- /dev/null +++ b/.idea/composerJson.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..51737e7 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..1b1341f --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/webResources.xml b/.idea/webResources.xml new file mode 100644 index 0000000..aa647dc --- /dev/null +++ b/.idea/webResources.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 0000000..4e1c7bd --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,30 @@ +notPath('/DependencyInjection\/Configuration\.php/') + ->notPath('/autoload\.php/') + ->in([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]); + +return PhpCsFixer\Config::create() + ->setRules([ + '@Symfony' => true, + 'phpdoc_summary' => false, + 'phpdoc_separation' => false, + 'phpdoc_align' => false, + 'cast_spaces' => false, + 'binary_operator_spaces' => [ + 'align_double_arrow' => true, + ], + 'concat_space' => [ + 'spacing' => 'one', + ], + ]) + ->setFinder($finder); diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2b59c1f --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +MIT License +Copyright (c) Meritoo.pl, http://www.meritoo.pl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9251ca6 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# Meritoo Common Library +Useful classes, methods, extensions etc. + +## Installation + +#### Composer + +1. Open ```composer.json``` and add address of repository in ```repositories``` section: + + ```json + "repositories": [ + { + "type": "vcs", + "url": "git@github.com:meritoo/common-library.git" + } + ] + ``` + +2. Run [Composer](https://getcomposer.org) to install new package: + + ```bash + $ composer require meritoo/common-library + ``` + +> How to install Composer: https://getcomposer.org/download + +## Usage + +This package contains a lot of static methods, so usage is not so complicated. Just run the static method who would you like to use. Example: + +```php +use Meritoo\Common\Utilities\Arrays; + +$firstElement = Arrays::getFirstElement(['lorem' 'ipsum']); +// result: "lorem" +``` + +Enjoy! diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..61e560c --- /dev/null +++ b/build.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..293d3c4 --- /dev/null +++ b/composer.json @@ -0,0 +1,34 @@ +{ + "name": "meritoo/common-library", + "description": "Useful classes, methods, extensions etc.", + "license": "MIT", + "version": "0.0.1", + "authors": [ + { + "name": "Meritoo.pl", + "email": "github@meritoo.pl", + "homepage": "http://www.meritoo.pl" + } + ], + "require": { + "php": ">=5.6.0", + "doctrine/orm": "^2.5", + "gedmo/doctrine-extensions": "^2.4", + "symfony/http-foundation": "^3.3" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.0", + "squizlabs/php_codesniffer": "^2.8", + "phpmd/phpmd": "^2.6", + "sebastian/phpcpd": "^3.0", + "pdepend/pdepend": "^2.5", + "phploc/phploc": "^3.0", + "friendsofphp/php-cs-fixer": "^2.1" + }, + "autoload": { + "psr-4": { + "Meritoo\\Common\\": "src/Meritoo/Common/", + "Meritoo\\Common\\Tests\\": "tests/Meritoo/Common/Tests/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..f2b4743 --- /dev/null +++ b/composer.lock @@ -0,0 +1,3518 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "a308119442767e36f71e362b6165ed40", + "packages": [ + { + "name": "behat/transliterator", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Transliterator.git", + "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", + "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "chuyskywalker/rolling-curl": "^3.1", + "php-yaoi/php-yaoi": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Transliterator": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Artistic-1.0" + ], + "description": "String transliterator", + "keywords": [ + "i18n", + "slug", + "transliterator" + ], + "time": "2017-04-04T11:38:05+00:00" + }, + { + "name": "doctrine/annotations", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2017-02-24T16:22:25+00:00" + }, + { + "name": "doctrine/cache", + "version": "v1.6.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "shasum": "" + }, + "require": { + "php": "~5.5|~7.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2016-10-29T11:16:17+00:00" + }, + { + "name": "doctrine/collections", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba", + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/coding-standard": "~0.1@dev", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2017-01-03T10:49:41+00:00" + }, + { + "name": "doctrine/common", + "version": "v2.7.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "930297026c8009a567ac051fd545bf6124150347" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/930297026c8009a567ac051fd545bf6124150347", + "reference": "930297026c8009a567ac051fd545bf6124150347", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/inflector": "1.*", + "doctrine/lexer": "1.*", + "php": "~5.6|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2017-01-13T14:02:13+00:00" + }, + { + "name": "doctrine/dbal", + "version": "v2.5.12", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/7b9e911f9d8b30d43b96853dab26898c710d8f44", + "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.4,<2.8-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*||^3.0" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\DBAL\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ], + "time": "2017-02-08T12:53:47+00:00" + }, + { + "name": "doctrine/inflector", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Inflector\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ], + "time": "2015-11-06T14:35:42+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14T21:17:01+00:00" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "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": "2014-09-09T13:34:57+00:00" + }, + { + "name": "doctrine/orm", + "version": "v2.5.6", + "source": { + "type": "git", + "url": "https://github.com/doctrine/doctrine2.git", + "reference": "e6c434196c8ef058239aaa0724b4aadb0107940b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/e6c434196c8ef058239aaa0724b4aadb0107940b", + "reference": "e6c434196c8ef058239aaa0724b4aadb0107940b", + "shasum": "" + }, + "require": { + "doctrine/cache": "~1.4", + "doctrine/collections": "~1.2", + "doctrine/common": ">=2.5-dev,<2.8-dev", + "doctrine/dbal": ">=2.5-dev,<2.6-dev", + "doctrine/instantiator": "~1.0.1", + "ext-pdo": "*", + "php": ">=5.4", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "symfony/yaml": "~2.3|~3.0" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "bin": [ + "bin/doctrine", + "bin/doctrine.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\ORM\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm" + ], + "time": "2016-12-18T15:42:34+00:00" + }, + { + "name": "gedmo/doctrine-extensions", + "version": "v2.4.29", + "source": { + "type": "git", + "url": "https://github.com/Atlantic18/DoctrineExtensions.git", + "reference": "f65c02eae3bff1aa57709ea4c8ca26947df95b9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Atlantic18/DoctrineExtensions/zipball/f65c02eae3bff1aa57709ea4c8ca26947df95b9c", + "reference": "f65c02eae3bff1aa57709ea4c8ca26947df95b9c", + "shasum": "" + }, + "require": { + "behat/transliterator": "~1.2", + "doctrine/common": "~2.4", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/common": ">=2.5.0", + "doctrine/mongodb-odm": ">=1.0.2", + "doctrine/orm": ">=2.5.0", + "phpunit/phpunit": "*", + "symfony/yaml": "~2.6|~3.0" + }, + "suggest": { + "doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM", + "doctrine/orm": "to use the extensions with the ORM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Gedmo\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Buchmann", + "email": "david@liip.ch" + }, + { + "name": "Gediminas Morkevicius", + "email": "gediminas.morkevicius@gmail.com" + }, + { + "name": "Gustavo Falco", + "email": "comfortablynumb84@gmail.com" + } + ], + "description": "Doctrine2 behavioral extensions", + "homepage": "http://gediminasm.org/", + "keywords": [ + "Blameable", + "behaviors", + "doctrine2", + "extensions", + "gedmo", + "loggable", + "nestedset", + "sluggable", + "sortable", + "timestampable", + "translatable", + "tree", + "uploadable" + ], + "time": "2017-05-29T17:33:48+00:00" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, + { + "name": "symfony/console", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "70d2a29b2911cbdc91a7e268046c395278238b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/70d2a29b2911cbdc91a7e268046c395278238b2e", + "reference": "70d2a29b2911cbdc91a7e268046c395278238b2e", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/debug": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3", + "symfony/dependency-injection": "~3.3", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/filesystem": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/filesystem": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2017-06-02T19:24:58+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "e9c50482841ef696e8fa1470d950a79c8921f45d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/e9c50482841ef696e8fa1470d950a79c8921f45d", + "reference": "e9c50482841ef696e8fa1470d950a79c8921f45d", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2017-06-01T21:01:25+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "80eb5a1f968448b77da9e8b2c0827f6e8d767846" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/80eb5a1f968448b77da9e8b2c0827f6e8d767846", + "reference": "80eb5a1f968448b77da9e8b2c0827f6e8d767846", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "symfony/expression-language": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "https://symfony.com", + "time": "2017-06-05T13:06:51+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "f29dca382a6485c3cbe6379f0c61230167681937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937", + "reference": "f29dca382a6485c3cbe6379f0c61230167681937", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2017-06-09T14:24:12+00:00" + } + ], + "packages-dev": [ + { + "name": "friendsofphp/php-cs-fixer", + "version": "v2.3.2", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "597745f744bcce1aed59dfd1bb4603de2a06cda9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/597745f744bcce1aed59dfd1bb4603de2a06cda9", + "reference": "597745f744bcce1aed59dfd1bb4603de2a06cda9", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.2", + "ext-json": "*", + "ext-tokenizer": "*", + "gecko-packages/gecko-php-unit": "^2.0", + "php": "^5.6 || >=7.0 <7.2", + "sebastian/diff": "^1.4", + "symfony/console": "^3.0", + "symfony/event-dispatcher": "^3.0", + "symfony/filesystem": "^3.0", + "symfony/finder": "^3.0", + "symfony/options-resolver": "^3.0", + "symfony/polyfill-php70": "^1.0", + "symfony/polyfill-xml": "^1.3", + "symfony/process": "^3.0", + "symfony/stopwatch": "^3.0" + }, + "conflict": { + "hhvm": "<3.18" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.1", + "justinrainbow/json-schema": "^5.0", + "mi-schi/phpmd-extension": "^4.2", + "phpmd/phpmd": "^2.4.3", + "phpunit/phpunit": "^4.8.35 || ^5.4.3", + "satooshi/php-coveralls": "^1.0", + "symfony/phpunit-bridge": "^3.2.2" + }, + "suggest": { + "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "ext-xml": "For better performance.", + "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "time": "2017-05-24T21:59:38+00:00" + }, + { + "name": "gecko-packages/gecko-php-unit", + "version": "v2.0", + "source": { + "type": "git", + "url": "https://github.com/GeckoPackages/GeckoPHPUnit.git", + "reference": "40a697ec261f3526e8196363b481b24383740c13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GeckoPackages/GeckoPHPUnit/zipball/40a697ec261f3526e8196363b481b24383740c13", + "reference": "40a697ec261f3526e8196363b481b24383740c13", + "shasum": "" + }, + "require": { + "php": "^5.3.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GeckoPackages\\PHPUnit\\": "src\\PHPUnit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Additional PHPUnit tests.", + "homepage": "https://github.com/GeckoPackages", + "keywords": [ + "extension", + "filesystem", + "phpunit" + ], + "time": "2016-11-22T11:01:27+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", + "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "doctrine/collections": "1.*", + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "homepage": "https://github.com/myclabs/DeepCopy", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2017-04-12T18:52:22+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.10", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2017-03-13T16:27:32+00:00" + }, + { + "name": "pdepend/pdepend", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/pdepend/pdepend.git", + "reference": "0c50874333149c0dad5a2877801aed148f2767ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/0c50874333149c0dad5a2877801aed148f2767ff", + "reference": "0c50874333149c0dad5a2877801aed148f2767ff", + "shasum": "" + }, + "require": { + "php": ">=5.3.7", + "symfony/config": "^2.3.0|^3", + "symfony/dependency-injection": "^2.3.0|^3", + "symfony/filesystem": "^2.3.0|^3" + }, + "require-dev": { + "phpunit/phpunit": "^4.4.0,<4.8", + "squizlabs/php_codesniffer": "^2.0.0" + }, + "bin": [ + "src/bin/pdepend" + ], + "type": "library", + "autoload": { + "psr-4": { + "PDepend\\": "src/main/php/PDepend" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Official version of pdepend to be handled with Composer", + "time": "2017-01-19T14:23:36+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2015-12-27T11:43:31+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.2.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2016-09-30T07:12:33+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2016-11-25T06:54:22+00:00" + }, + { + "name": "phploc/phploc", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phploc.git", + "reference": "74f917e6f80f291856989960d31afa44a4196859" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/74f917e6f80f291856989960d31afa44a4196859", + "reference": "74f917e6f80f291856989960d31afa44a4196859", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "sebastian/finder-facade": "~1.1", + "sebastian/git": "~2.1", + "sebastian/version": "~1.0.3|~2.0", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~5" + }, + "bin": [ + "phploc" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "A tool for quickly measuring the size of a PHP project.", + "homepage": "https://github.com/sebastianbergmann/phploc", + "time": "2016-04-25T08:11:21+00:00" + }, + { + "name": "phpmd/phpmd", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/phpmd/phpmd.git", + "reference": "4e9924b2c157a3eb64395460fcf56b31badc8374" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/4e9924b2c157a3eb64395460fcf56b31badc8374", + "reference": "4e9924b2c157a3eb64395460fcf56b31badc8374", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "pdepend/pdepend": "^2.5", + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "^4.0", + "squizlabs/php_codesniffer": "^2.0" + }, + "bin": [ + "src/bin/phpmd" + ], + "type": "project", + "autoload": { + "psr-0": { + "PHPMD\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Manuel Pichler", + "email": "github@manuel-pichler.de", + "homepage": "https://github.com/manuelpichler", + "role": "Project Founder" + }, + { + "name": "Other contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" + }, + { + "name": "Marc Würth", + "email": "ravage@bluewin.ch", + "homepage": "https://github.com/ravage84", + "role": "Project Maintainer" + } + ], + "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", + "homepage": "http://phpmd.org/", + "keywords": [ + "mess detection", + "mess detector", + "pdepend", + "phpmd", + "pmd" + ], + "time": "2017-01-20T14:41:10+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8 || ^5.6.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2017-03-02T20:05:34+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^5.6 || ^7.0", + "phpunit/php-file-iterator": "^1.3", + "phpunit/php-text-template": "^1.2", + "phpunit/php-token-stream": "^1.4.2 || ^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0", + "sebastian/environment": "^1.3.2 || ^2.0", + "sebastian/version": "^1.0 || ^2.0" + }, + "require-dev": { + "ext-xdebug": "^2.1.4", + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-xdebug": "^2.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "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": "2017-04-02T07:44:40+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "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": "2016-10-03T07:40:28+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "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": "2017-02-26T11:10:40+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.11", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2017-02-27T10:12:30+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "5.7.21", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "3b91adfb64264ddec5a2dee9851f354aa66327db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3b91adfb64264ddec5a2dee9851f354aa66327db", + "reference": "3b91adfb64264ddec5a2dee9851f354aa66327db", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "~1.3", + "php": "^5.6 || ^7.0", + "phpspec/prophecy": "^1.6.2", + "phpunit/php-code-coverage": "^4.0.4", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "^3.2", + "sebastian/comparator": "^1.2.4", + "sebastian/diff": "^1.4.3", + "sebastian/environment": "^1.3.4 || ^2.0", + "sebastian/exporter": "~2.0", + "sebastian/global-state": "^1.1", + "sebastian/object-enumerator": "~2.0", + "sebastian/resource-operations": "~1.0", + "sebastian/version": "~1.0.3|~2.0", + "symfony/yaml": "~2.1|~3.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-xdebug": "*", + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2017-06-21T08:11:54+00:00" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", + "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.6 || ^7.0", + "phpunit/php-text-template": "^1.2", + "sebastian/exporter": "^1.2 || ^2.0" + }, + "conflict": { + "phpunit/phpunit": "<5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "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": "2016-12-08T20:27:08+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "1.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2 || ~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2017-01-29T09:50:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2017-05-22T07:24:03+00:00" + }, + { + "name": "sebastian/environment", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2016-11-26T07:53:53+00:00" + }, + { + "name": "sebastian/exporter", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~2.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2016-11-19T08:54:04+00:00" + }, + { + "name": "sebastian/finder-facade", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/finder-facade.git", + "reference": "2a6f7f57efc0aa2d23297d9fd9e2a03111a8c0b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/2a6f7f57efc0aa2d23297d9fd9e2a03111a8c0b9", + "reference": "2a6f7f57efc0aa2d23297d9fd9e2a03111a8c0b9", + "shasum": "" + }, + "require": { + "symfony/finder": "~2.3|~3.0", + "theseer/fdomdocument": "~1.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", + "homepage": "https://github.com/sebastianbergmann/finder-facade", + "time": "2016-02-17T07:02:23+00:00" + }, + { + "name": "sebastian/git", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/git.git", + "reference": "815bbbc963cf35e5413df195aa29df58243ecd24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/git/zipball/815bbbc963cf35e5413df195aa29df58243ecd24", + "reference": "815bbbc963cf35e5413df195aa29df58243ecd24", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Simple wrapper for Git", + "homepage": "http://www.github.com/sebastianbergmann/git", + "keywords": [ + "git" + ], + "abandoned": true, + "time": "2017-01-23T20:57:12+00:00" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12T03:26:01+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "sebastian/recursion-context": "~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-02-18T15:18:39+00:00" + }, + { + "name": "sebastian/phpcpd", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpcpd.git", + "reference": "d7006078b75a34c9250831c3453a2e256a687615" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/d7006078b75a34c9250831c3453a2e256a687615", + "reference": "d7006078b75a34c9250831c3453a2e256a687615", + "shasum": "" + }, + "require": { + "php": "^5.6|^7.0", + "phpunit/php-timer": "^1.0.6", + "sebastian/finder-facade": "^1.1", + "sebastian/version": "^2.0", + "symfony/console": "^3.0" + }, + "bin": [ + "phpcpd" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Copy/Paste Detector (CPD) for PHP code.", + "homepage": "https://github.com/sebastianbergmann/phpcpd", + "time": "2017-02-05T07:48:01+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2016-11-19T07:33:16+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2015-07-28T20:34:47+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "2.9.1", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dcbed1074f8244661eecddfc2a675430d8d33f62", + "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "bin": [ + "scripts/phpcs", + "scripts/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "classmap": [ + "CodeSniffer.php", + "CodeSniffer/CLI.php", + "CodeSniffer/Exception.php", + "CodeSniffer/File.php", + "CodeSniffer/Fixer.php", + "CodeSniffer/Report.php", + "CodeSniffer/Reporting.php", + "CodeSniffer/Sniff.php", + "CodeSniffer/Tokens.php", + "CodeSniffer/Reports/", + "CodeSniffer/Tokenizers/", + "CodeSniffer/DocGenerators/", + "CodeSniffer/Standards/AbstractPatternSniff.php", + "CodeSniffer/Standards/AbstractScopeSniff.php", + "CodeSniffer/Standards/AbstractVariableSniff.php", + "CodeSniffer/Standards/IncorrectPatternException.php", + "CodeSniffer/Standards/Generic/Sniffs/", + "CodeSniffer/Standards/MySource/Sniffs/", + "CodeSniffer/Standards/PEAR/Sniffs/", + "CodeSniffer/Standards/PSR1/Sniffs/", + "CodeSniffer/Standards/PSR2/Sniffs/", + "CodeSniffer/Standards/Squiz/Sniffs/", + "CodeSniffer/Standards/Zend/Sniffs/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "http://www.squizlabs.com/php-codesniffer", + "keywords": [ + "phpcs", + "standards" + ], + "time": "2017-05-22T02:43:20+00:00" + }, + { + "name": "symfony/config", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "35716d4904e0506a7a5a9bcf23f854aeb5719bca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/35716d4904e0506a7a5a9bcf23f854aeb5719bca", + "reference": "35716d4904e0506a7a5a9bcf23f854aeb5719bca", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/filesystem": "~2.8|~3.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3", + "symfony/yaml": "~3.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2017-06-02T18:07:20+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "4cec19ec1d25f22e1ec8ab14635d3879a1287053" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/4cec19ec1d25f22e1ec8ab14635d3879a1287053", + "reference": "4cec19ec1d25f22e1ec8ab14635d3879a1287053", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/container": "^1.0" + }, + "conflict": { + "symfony/config": "<3.3.1", + "symfony/finder": "<3.3", + "symfony/yaml": "<3.3" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "~3.3", + "symfony/expression-language": "~2.8|~3.0", + "symfony/yaml": "~3.3" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DependencyInjection Component", + "homepage": "https://symfony.com", + "time": "2017-06-06T03:13:52+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "4054a102470665451108f9b59305c79176ef98f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4054a102470665451108f9b59305c79176ef98f0", + "reference": "4054a102470665451108f9b59305c79176ef98f0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0", + "symfony/dependency-injection": "~3.3", + "symfony/expression-language": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2017-06-04T18:15:29+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "c709670bf64721202ddbe4162846f250735842c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c709670bf64721202ddbe4162846f250735842c0", + "reference": "c709670bf64721202ddbe4162846f250735842c0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2017-05-28T14:08:56+00:00" + }, + { + "name": "symfony/finder", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/baea7f66d30854ad32988c11a09d7ffd485810c4", + "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2017-06-01T21:01:25+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "ff48982d295bcac1fd861f934f041ebc73ae40f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/ff48982d295bcac1fd861f934f041ebc73ae40f0", + "reference": "ff48982d295bcac1fd861f934f041ebc73ae40f0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2017-04-12T14:14:56+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "032fd647d5c11a9ceab8ee8747e13b5448e93874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/032fd647d5c11a9ceab8ee8747e13b5448e93874", + "reference": "032fd647d5c11a9ceab8ee8747e13b5448e93874", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2017-06-09T14:24:12+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "d3a71580c1e2cab33b6d705f0ec40e9015e14d5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/d3a71580c1e2cab33b6d705f0ec40e9015e14d5c", + "reference": "d3a71580c1e2cab33b6d705f0ec40e9015e14d5c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2017-06-09T08:25:21+00:00" + }, + { + "name": "symfony/polyfill-xml", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-xml.git", + "reference": "89326af9d173053826ae8fe26a6f49597ba4e9f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-xml/zipball/89326af9d173053826ae8fe26a6f49597ba4e9f3", + "reference": "89326af9d173053826ae8fe26a6f49597ba4e9f3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-php72": "~1.4" + }, + "type": "metapackage", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for xml's utf8_encode and utf8_decode functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2017-06-09T08:25:21+00:00" + }, + { + "name": "symfony/process", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "8e30690c67aafb6c7992d6d8eb0d707807dd3eaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/8e30690c67aafb6c7992d6d8eb0d707807dd3eaf", + "reference": "8e30690c67aafb6c7992d6d8eb0d707807dd3eaf", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2017-05-22T12:32:03+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "602a15299dc01556013b07167d4f5d3a60e90d15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/602a15299dc01556013b07167d4f5d3a60e90d15", + "reference": "602a15299dc01556013b07167d4f5d3a60e90d15", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "time": "2017-04-12T14:14:56+00:00" + }, + { + "name": "symfony/yaml", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/9752a30000a8ca9f4b34b5227d15d0101b96b063", + "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/console": "~2.8|~3.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2017-06-02T22:05:06+00:00" + }, + { + "name": "theseer/fdomdocument", + "version": "1.6.5", + "source": { + "type": "git", + "url": "https://github.com/theseer/fDOMDocument.git", + "reference": "8dcfd392135a5bd938c3c83ea71419501ad9855d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/8dcfd392135a5bd938c3c83ea71419501ad9855d", + "reference": "8dcfd392135a5bd938c3c83ea71419501ad9855d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "lib-libxml": "*", + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "lead" + } + ], + "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", + "homepage": "https://github.com/theseer/fDOMDocument", + "time": "2017-04-21T14:50:31+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2016-11-23T20:04:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.3.0" + }, + "platform-dev": [] +} diff --git a/data/tests/composer.json b/data/tests/composer.json new file mode 100644 index 0000000..5d99728 --- /dev/null +++ b/data/tests/composer.json @@ -0,0 +1,7 @@ +{ + "name": "test/test", + "license": "proprietary", + "version": "1.0.2", + "type": "library", + "description": "For testing purposes only" +} diff --git a/data/tests/lorem-ipsum.txt b/data/tests/lorem-ipsum.txt new file mode 100644 index 0000000..32cd736 --- /dev/null +++ b/data/tests/lorem-ipsum.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras vitae. diff --git a/data/tests/minion.jpg b/data/tests/minion.jpg new file mode 100644 index 0000000..826fe2c Binary files /dev/null and b/data/tests/minion.jpg differ diff --git a/phing/app.xml b/phing/app.xml new file mode 100644 index 0000000..00c7f8e --- /dev/null +++ b/phing/app.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phing/properties.dist b/phing/properties.dist new file mode 100644 index 0000000..ae06943 --- /dev/null +++ b/phing/properties.dist @@ -0,0 +1,125 @@ +# -------------------------------------------------------------------------------- +# Information +# -------------------------------------------------------------------------------- + +# Property files contain key/value pairs +# key = value +# +# Property keys may contain alphanumeric chars and colons, but +# not special chars. This way you can create pseudo-namespaces +# +# You can refer to values of other properties by enclosing their keys in "${}". +# Example: dir.js = ${dir.web}/js +# +# Everything behind the equal sign is the value, you do +# not have to enclose strings: text=This is some text, Your OS is ${php.os} + +# -------------------------------------------------------------------------------- +# Common, e.g. default environment +# -------------------------------------------------------------------------------- + +# Default environment +# +env = dev + +# Install assets using symlinks +# +assets.installWithSymlink = true + +# Clear cache with the "warmup" option +# +cache.clearWithWarmup = true + +# -------------------------------------------------------------------------------- +# Composer +# -------------------------------------------------------------------------------- + +composer.download_command = php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));" + +# Path to composer executable or composer.phar file +# +composer.path = composer.phar +#composer.path = /usr/local/bin/composer + +# Path to php executable used by composer +# +composer.php = php + +# Self update of the composer +# +composer.self-update = false + +# Validate the composer.json file +# +composer.validate = false + +# -------------------------------------------------------------------------------- +# Directories +# -------------------------------------------------------------------------------- + +# System directories +# +dir.data = ${project.basedir}/data +dir.src = ${project.basedir}/src +dir.tests = ${project.basedir}/tests + +# -------------------------------------------------------------------------------- +# Build directories +# -------------------------------------------------------------------------------- + +dir.build = ${project.basedir}/build +dir.reports = ${dir.build}/logs +dir.reports.pdepend = ${dir.reports}/pdepend +dir.reports.coverage = ${dir.reports}/phpunit_coverage +# +# Disabled, because unnecessary right now +# phpdocumentor/phpdocumentor cannot be installed via Composer +# +# Krzysztof Niziol +# 2017-02-22 +# +#dir.docs = ${dir.build}/docs +#dir.docs.phpdoc2 = ${dir.docs}/phpdoc2 + +# -------------------------------------------------------------------------------- +# Data directories +# -------------------------------------------------------------------------------- + +dir.data.tests = ${dir.data}/tests +dir.data.temporary = ${dir.data}/tmp + +# -------------------------------------------------------------------------------- +# Testing +# -------------------------------------------------------------------------------- + +# Test database path +# +tests.database = ${dir.data.temporary}/database.sqlite + +# +# Disabled, because unnecessary right now +# PHPUnit is installed and loaded by Composer +# +# Krzysztof Niziol +# 2017-02-22 +# +# Run PHPUnit using exec task instead of phpunitTask +#phpunit.useExec = false + +# +# Disabled, because unnecessary right now +# We want generate code coverage always +# +# Krzysztof Niziol +# 2017-02-22 +# +# Collect coverage data during tests +#phpunit.withCoverage = true + +# Path of the PHPUnit (https://phpunit.de) +# +phpUnit.path = ./vendor/bin/phpunit + +# Path of the PHP Coding Standards Fixer (http://cs.sensiolabs.org) +# +phpCsFixer.path = ./vendor/bin/php-cs-fixer diff --git a/phing/tests.xml b/phing/tests.xml new file mode 100644 index 0000000..3f88654 --- /dev/null +++ b/phing/tests.xml @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..e369b8f --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,35 @@ + + + + + + ./tests/ + + + + + + ./src/ + + + + + + performance + + + + + + + diff --git a/src/Meritoo/Common/Exception/Base/UnknownTypeException.php b/src/Meritoo/Common/Exception/Base/UnknownTypeException.php new file mode 100644 index 0000000..0a4c5e9 --- /dev/null +++ b/src/Meritoo/Common/Exception/Base/UnknownTypeException.php @@ -0,0 +1,41 @@ + + * @copyright Meritoo.pl + */ +abstract class UnknownTypeException extends Exception +{ + /** + * Class constructor + * + * @param string|int $unknownType The unknown type of something (value of constant) + * @param BaseType $typeInstance An instance of class that contains type of the something + * @param string $typeName Name of the something + */ + public function __construct($unknownType, BaseType $typeInstance, $typeName) + { + $allTypes = $typeInstance->getAll(); + $types = Arrays::values2string($allTypes, '', ', '); + + $template = 'The \'%s\' type of %s is unknown. Probably doesn\'t exist or there is a typo. You should use one' + . ' of these types: %s.'; + + $message = sprintf(sprintf($template, $unknownType, $typeName, $types)); + parent::__construct($message); + } +} diff --git a/src/Meritoo/Common/Exception/Date/IncorrectDatePartException.php b/src/Meritoo/Common/Exception/Date/IncorrectDatePartException.php new file mode 100644 index 0000000..7a68268 --- /dev/null +++ b/src/Meritoo/Common/Exception/Date/IncorrectDatePartException.php @@ -0,0 +1,32 @@ + + * @copyright Meritoo.pl + */ +class IncorrectDatePartException extends Exception +{ + /** + * Class constructor + * + * @param string $value Incorrect value + * @param string $datePart Type of date part, e.g. "year". One of \Meritoo\Common\Type\DatePartType class constants. + */ + public function __construct($value, $datePart) + { + $message = sprintf('Value of %s \'%s\' is incorrect. Is there everything ok?', $datePart, $value); + parent::__construct($message); + } +} diff --git a/src/Meritoo/Common/Exception/IncorrectColorHexLengthException.php b/src/Meritoo/Common/Exception/IncorrectColorHexLengthException.php new file mode 100644 index 0000000..bcd5bc3 --- /dev/null +++ b/src/Meritoo/Common/Exception/IncorrectColorHexLengthException.php @@ -0,0 +1,32 @@ + + * @copyright Meritoo.pl + */ +class IncorrectColorHexLengthException extends \Exception +{ + /** + * Class constructor + * + * @param string $color Incorrect hexadecimal value of color + */ + public function __construct($color) + { + $template = 'Length of hexadecimal value of color \'%s\' is incorrect. It\'s %d, but it should be 3 or 6.' + . ' Is there everything ok?'; + + $message = sprintf($template, $color, strlen($color)); + parent::__construct($message); + } +} diff --git a/src/Meritoo/Common/Exception/InvalidColorHexValueException.php b/src/Meritoo/Common/Exception/InvalidColorHexValueException.php new file mode 100644 index 0000000..2249447 --- /dev/null +++ b/src/Meritoo/Common/Exception/InvalidColorHexValueException.php @@ -0,0 +1,29 @@ + + * @copyright Meritoo.pl + */ +class InvalidColorHexValueException extends \Exception +{ + /** + * Class constructor + * + * @param string $color Incorrect hexadecimal value of color + */ + public function __construct($color) + { + $message = sprintf('Hexadecimal value of color \'%s\' is incorrect. Is there everything ok?', $color); + parent::__construct($message); + } +} diff --git a/src/Meritoo/Common/Exception/Reflection/CannotResolveClassNameException.php b/src/Meritoo/Common/Exception/Reflection/CannotResolveClassNameException.php new file mode 100644 index 0000000..8c1135c --- /dev/null +++ b/src/Meritoo/Common/Exception/Reflection/CannotResolveClassNameException.php @@ -0,0 +1,47 @@ + + * @copyright Meritoo.pl + */ +class CannotResolveClassNameException extends Exception +{ + /** + * Class constructor + * + * @param array|object|string $source Source of the class's / trait's name. It cane be an array of objects, + * namespaces, object or namespace. + * @param bool $forClass (optional) If is set to true, message of this exception for class is + * prepared. Otherwise - for trait. + */ + public function __construct($source, $forClass = true) + { + $forWho = 'trait'; + $value = ''; + + if ($forClass) { + $forWho = 'class'; + } + + if (is_scalar($source)) { + $value = sprintf(' %s', (string)$source); + } + + $template = 'Name of %s from given \'%s\'%s cannot be resolved. Is there everything ok?'; + $message = sprintf($template, $forWho, gettype($source), $value); + + parent::__construct($message); + } +} diff --git a/src/Meritoo/Common/Exception/Reflection/MissingChildClassesException.php b/src/Meritoo/Common/Exception/Reflection/MissingChildClassesException.php new file mode 100644 index 0000000..73c13b6 --- /dev/null +++ b/src/Meritoo/Common/Exception/Reflection/MissingChildClassesException.php @@ -0,0 +1,38 @@ + + * @copyright Meritoo.pl + */ +class MissingChildClassesException extends Exception +{ + /** + * Class constructor + * + * @param array|object|string $parentClass Class that hasn't child classes, but it should. An array of objects, + * strings, object or string. + */ + public function __construct($parentClass) + { + $template = 'The \'%s\' class requires one child class at least who will extend her (maybe is an abstract' + . ' class), but the child classes are missing. Did you forget to extend this class?'; + + $parentClassName = Reflection::getClassName($parentClass); + $message = sprintf($template, $parentClassName); + + parent::__construct($message); + } +} diff --git a/src/Meritoo/Common/Exception/Reflection/TooManyChildClassesException.php b/src/Meritoo/Common/Exception/Reflection/TooManyChildClassesException.php new file mode 100644 index 0000000..b5a4f13 --- /dev/null +++ b/src/Meritoo/Common/Exception/Reflection/TooManyChildClassesException.php @@ -0,0 +1,39 @@ + + * @copyright Meritoo.pl + */ +class TooManyChildClassesException extends Exception +{ + /** + * Class constructor + * + * @param array|object|string $parentClass Class that has more than one child class, but it shouldn't. An array + * of objects, strings, object or string. + * @param array $childClasses Child classes + */ + public function __construct($parentClass, array $childClasses) + { + $template = "The '%s' class requires one child class at most who will extend her, but more than one child" + . " class was found:\n- %s\n\nWhy did you create more than one classes that extend '%s' class?"; + + $parentClassName = Reflection::getClassName($parentClass); + $message = sprintf($template, $parentClassName, implode("\n- ", $childClasses), $parentClassName); + + parent::__construct($message); + } +} diff --git a/src/Meritoo/Common/Type/Base/BaseType.php b/src/Meritoo/Common/Type/Base/BaseType.php new file mode 100644 index 0000000..369f527 --- /dev/null +++ b/src/Meritoo/Common/Type/Base/BaseType.php @@ -0,0 +1,53 @@ + + * @copyright Meritoo.pl + */ +abstract class BaseType +{ + /** + * All types + * + * @var array + */ + private $all; + + /** + * Returns all types + * + * @return array + */ + public function getAll() + { + if ($this->all === null) { + $this->all = Reflection::getConstants($this); + } + + return $this->all; + } + + /** + * Returns information if given type is correct + * + * @param string $type The type to check + * @return bool + */ + public function isCorrectType($type) + { + return in_array($type, $this->getAll(), true); + } +} diff --git a/src/Meritoo/Common/Type/DatePartType.php b/src/Meritoo/Common/Type/DatePartType.php new file mode 100644 index 0000000..82b8b6b --- /dev/null +++ b/src/Meritoo/Common/Type/DatePartType.php @@ -0,0 +1,56 @@ + + * @copyright Meritoo.pl + */ +class DatePartType extends BaseType +{ + /** + * The "day" date part + * + * @var string + */ + const DAY = 'day'; + + /** + * The "hour" date part + * + * @var string + */ + const HOUR = 'hour'; + + /** + * The "minute" date part + * + * @var string + */ + const MINUTE = 'minute'; + + /** + * The "month" date part + * + * @var string + */ + const MONTH = 'month'; + + /** + * The "second" date part + * + * @var string + */ + const SECOND = 'second'; + + /** + * The "year" date part + * + * @var string + */ + const YEAR = 'year'; +} diff --git a/src/Meritoo/Common/Utilities/Arrays.php b/src/Meritoo/Common/Utilities/Arrays.php new file mode 100644 index 0000000..5773805 --- /dev/null +++ b/src/Meritoo/Common/Utilities/Arrays.php @@ -0,0 +1,1641 @@ + + * @copyright Meritoo.pl + */ +class Arrays +{ + /** + * Name of the array's key used to store position of element of the array + * + * @var string + */ + const POSITION_KEY_NAME = 'position'; + + /** + * Converts given array's column to string. + * Recursive call is made for multi-dimensional arrays. + * + * @param array $array Array data to be converted + * @param string|int $arrayColumnKey (optional) Column name + * @param string $separator (optional) Separator used in resultant string + * @return string + */ + public static function values2string(array $array, $arrayColumnKey = '', $separator = ',') + { + $effect = ''; + + if (!empty($array)) { + foreach ($array as $key => $value) { + if (!empty($effect) && + ( + empty($arrayColumnKey) || (!is_array($value) && $key === $arrayColumnKey) + ) + ) { + $effect .= $separator; + } + + if (is_array($value)) { + $effect .= self::values2string($value, $arrayColumnKey, $separator); + /* + * Previous version. Doesn't work with array containing arrays, e.g.: + * array( + * 1 => array( + * 'item1', + * 'item2' + * ), + * 2 => array( + * 'item3', + * 'item4' + * ) + * ) + * + if(isset($value[$arrayColumnKey])){ + $effect .= $value[$arrayColumnKey]; + } + */ + } else { + if (empty($arrayColumnKey)) { + $effect .= $value; + } elseif ($key === $arrayColumnKey) { + $effect .= $array[$arrayColumnKey]; + } + } + } + } + + return $effect; + } + + /** + * Converts given array to string with keys, e.g. abc=1&def=2 or abc="1" def="2" + * + * @param array $array Array data to be converted + * @param string $separator (optional) Separator used between name-value pairs in resultant string + * @param string $valuesKeysSeparator (optional) Separator used between name and value in resultant string + * @param string $valuesWrapper (optional) Wrapper used to wrap values, e.g. double-quote: key="value" + * @return string + */ + public static function valuesKeys2string($array, $separator = ',', $valuesKeysSeparator = '=', $valuesWrapper = '') + { + $effect = ''; + + if (is_array($array) && !empty($array)) { + foreach ($array as $key => $value) { + if (!empty($effect)) { + $effect .= $separator; + } + + if (!empty($valuesWrapper)) { + $value = sprintf('%s%s%s', $valuesWrapper, $value, $valuesWrapper); + } + + $effect .= $key . $valuesKeysSeparator . $value; + } + } + + return $effect; + } + + /** + * Converts given array's rows to csv string + * + * @param array $array Array data to be converted. It have to be an array that represents database table. + * @param string $separator (optional) Separator used in resultant string + * @return string + */ + public static function values2csv($array, $separator = ',') + { + if (is_array($array) && !empty($array)) { + $rows = []; + $lineSeparator = "\n"; + + foreach ($array as $row) { + /* + * I have to use html_entity_decode() function here, because some string values can contain + * entities with semicolon and this can destroy the CSV column order. + */ + + if (is_array($row) && !empty($row)) { + foreach ($row as &$value) { + $value = html_entity_decode($value); + } + + $rows[] = implode($separator, $row); + } + } + + if (!empty($rows)) { + return implode($lineSeparator, $rows); + } + } + + return ''; + } + + /** + * Returns information if given element is the first one + * + * @param array $array The array to get the first element of + * @param mixed $element The element to check / verify + * @param bool $firstLevelOnly (optional) If is set to true, first element is returned. Otherwise - totally + * first element is returned (first of the First array). + * @return bool + */ + public static function isFirstElement(array $array, $element, $firstLevelOnly = true) + { + $firstElement = self::getFirstElement($array, $firstLevelOnly); + + return $element === $firstElement; + } + + /** + * Returns the first element of given array + * + * It may be first element of given array or the totally first element from the all elements (first element of the + * first array). + * + * @param array $array The array to get the first element of + * @param bool $firstLevelOnly (optional) If is set to true, first element is returned. Otherwise - totally + * first element is returned (first of the first array). + * @return mixed + */ + public static function getFirstElement(array $array, $firstLevelOnly = true) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return null; + } + + $firstKey = self::getFirstKey($array); + $first = $array[$firstKey]; + + if (!$firstLevelOnly && is_array($first)) { + $first = self::getFirstElement($first, $firstLevelOnly); + } + + return $first; + } + + /** + * Returns first key of array + * + * @param array $array The array to get the first key of + * @return mixed + */ + public static function getFirstKey(array $array) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return null; + } + + $keys = array_keys($array); + + return $keys[0]; + } + + /** + * Returns information if given element is the last one + * + * @param array $array The array to get the last element of + * @param mixed $element The element to check / verify + * @param bool $firstLevelOnly (optional) If is set to true, last element is returned. Otherwise - totally + * last element is returned (last of the latest array). + * @return bool + */ + public static function isLastElement(array $array, $element, $firstLevelOnly = true) + { + $lastElement = self::getLastElement($array, $firstLevelOnly); + + return $element === $lastElement; + } + + /** + * Returns the last element of given array + * + * It may be last element of given array or the totally last element from the all elements (last element of the + * latest array). + * + * @param array $array The array to get the last element of + * @param bool $firstLevelOnly (optional) If is set to true, last element is returned. Otherwise - totally + * last element is returned (last of the latest array). + * @return mixed + */ + public static function getLastElement(array $array, $firstLevelOnly = true) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return null; + } + + $last = end($array); + + if (!$firstLevelOnly && is_array($last)) { + $last = self::getLastElement($last, $firstLevelOnly); + } + + return $last; + } + + /** + * Returns breadcrumb (a path) to the last element of array + * + * @param array $array The array to get the breadcrumb + * @param string $separator (optional) Separator used to stick the elements + * @return string + */ + public static function getLastElementBreadCrumb($array, $separator = '/') + { + $keys = array_keys($array); + $keysCount = count($keys); + + $lastKey = $keys[$keysCount - 1]; + $last = end($array); + + $breadCrumb = $lastKey; + + if (is_array($last)) { + $crumb = self::getLastElementBreadCrumb($last, $separator); + } else { + $crumb = $last; + } + + return $breadCrumb . $separator . $crumb; + } + + /** + * Returns the last row of array + * + * @param array $array The array to get the last row of + * @return mixed + */ + public static function getLastRow(array $array) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return null; + } + + $effect = []; + $last = end($array); + + if (is_array($last)) { + /* + * We've got an array, so looking for the last row of array will be done recursively + */ + $effect = self::getLastRow($last); + + /* + * The last row is not an array or it's an empty array? + * Let's use the previous candidate + */ + if (!is_array($effect) || (is_array($effect) && empty($effect))) { + $effect = $last; + } + } + + return $effect; + } + + /** + * Replaces array keys that match given pattern with new key name + * + * @param array $dataArray The array + * @param string $oldKeyPattern Old key pattern + * @param string $newKey New key name + * @return array + */ + public static function replaceArrayKeys($dataArray, $oldKeyPattern, $newKey) + { + $effect = []; + + if (is_array($dataArray) && !empty($dataArray)) { + foreach ($dataArray as $key => $value) { + if (preg_match($oldKeyPattern, $key)) { + $key = $newKey; + } + + if (is_array($value)) { + $value = self::replaceArrayKeys($value, $oldKeyPattern, $newKey); + } + + $effect[$key] = $value; + } + } + + return $effect; + } + + /** + * Generates JavaScript code for given PHP array + * + * @param array $array The array that should be generated to JavaScript + * @param string $jsVariableName (optional) Name of the variable that will be in generated JavaScript code + * @param bool $preserveIndexes (optional) If is set to true and $jsVariableName isn't empty, indexes also + * will be added to the JavaScript code. Otherwise not. + * @return string|null + */ + public static function array2JavaScript(array $array, $jsVariableName = '', $preserveIndexes = false) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return null; + } + + $effect = ''; + $counter = 0; + + $arrayCount = count($array); + $array = self::quoteStrings($array); + $isMultiDimensional = self::isMultiDimensional($array); + + /* + * Name of the variable was not provided and it's a multi dimensional array? + * Let's create the name, because variable is required for later usage (related to multi dimensional array) + */ + if (self::isMultiDimensional($array) && empty($jsVariableName)) { + $jsVariableName = 'autoGeneratedVariable'; + } + + if (!empty($jsVariableName) && is_string($jsVariableName)) { + $effect .= sprintf('var %s = ', $jsVariableName); + } + + $effect .= 'new Array('; + + if ($preserveIndexes || $isMultiDimensional) { + $effect .= $arrayCount; + $effect .= ');'; + } + + foreach ($array as $index => $value) { + ++$counter; + + if (is_array($value)) { + $variable = $index; + + if (is_integer($index)) { + $variable = 'value_' . $variable; + } + + $value = self::array2JavaScript($value, $variable, $preserveIndexes); + + if (!empty($value)) { + /* + * Add an empty line for the 1st iteration only. Required to avoid missing empty line after + * declaration of variable: + * + * var autoGeneratedVariable = new Array(...);autoGeneratedVariable[0] = new Array(...); + * autoGeneratedVariable[1] = new Array(...); + */ + if ($counter === 1) { + $effect .= "\n"; + } + + $effect .= $value . "\n"; + $effect .= sprintf('%s[%s] = %s;', $jsVariableName, Miscellaneous::quoteValue($index), $variable); + + if ($counter !== $arrayCount) { + $effect .= "\n"; + } + } + } else { + if ($preserveIndexes) { + if (!empty($jsVariableName)) { + $index = Miscellaneous::quoteValue($index); + $effect .= sprintf("\n%s[%s] = %s;", $jsVariableName, $index, $value); + } + } else { + $format = '%s'; + + if ($counter < $arrayCount) { + $format .= ', '; + } + + $effect .= sprintf($format, $value); + } + } + } + + if (!$preserveIndexes && !$isMultiDimensional) { + $effect .= ');'; + } + + return $effect; + } + + /** + * Quotes (adds quotes) to elements of an array that are strings + * + * @param array $array The array to check for string values + * @return array + */ + public static function quoteStrings($array) + { + $effect = $array; + + if (is_array($array) && !empty($array)) { + $effect = []; + + foreach ($array as $index => $value) { + if (is_array($value)) { + $value = self::quoteStrings($value); + } elseif (is_string($value)) { + if (!Regex::isQuoted($value)) { + $value = '\'' . $value . '\''; + } + } + + $effect[$index] = $value; + } + } + + return $effect; + } + + /** + * Removes marginal element (first or last) + * + * @param string|array $item The item which should be shortened + * @param bool $last (optional) If is set to true, last element is removed. Otherwise - first. + * @return string|array + */ + public static function removeMarginalElement($item, $last = true) + { + if (is_string($item)) { + if ($last) { + $item = substr($item, 0, strlen($item) - 1); + } else { + $item = substr($item, 1); + } + } elseif (is_array($item)) { + $key = self::getFirstKey($item); + + if ($last) { + $key = self::getLastKey($item); + } + + unset($item[$key]); + } + + return $item; + } + + /** + * Returns last key of array + * + * @param array $array The array to get the last key of + * @return mixed + */ + public static function getLastKey(array $array) + { + $keys = array_keys($array); + + return end($keys); + } + + /** + * Removes element / item of given array + * + * @param array $array The array that contains element / item which should be removed + * @param mixed $item The element / item which should be removed + * @return bool|array + */ + public static function removeElement(array $array, $item) + { + if (empty($array) || !in_array($item, $array)) { + return false; + } + + /* + * Flip the array to make it looks like: value => key + */ + $arrayFlipped = array_flip($array); + + /* + * Take the key of element / item that should be removed + */ + $key = $arrayFlipped[$item]; + + /* + * ...and remove the element / item + */ + unset($array[$key]); + + return $array; + } + + /** + * Removes items from given array starting at given element (before or after the element) + * + * @param array $array The array which contains items to remove + * @param mixed $needle The element which is start point of deletion + * @param bool $before (optional) If is set to true, all elements before given needle are removed. Otherwise - all + * after needle. + */ + public static function removeElements(&$array, $needle, $before = true) + { + if (is_array($array) && !empty($array)) { + if (!$before) { + $array = array_reverse($array, true); + } + + foreach ($array as $key => &$value) { + $remove = false; + + if (is_array($value)) { + self::removeElements($value, $needle, $before); + + if (is_array($value) && empty($value)) { + $remove = true; + } + } else { + if ($value === $needle) { + break; + } else { + $remove = true; + } + } + + if ($remove) { + unset($array[$key]); + } + } + + if (!$before) { + $array = array_reverse($array, true); + } + } + } + + /** + * Sets keys as values and values as keys in given array. + * Replaces keys with values. + * + * @param array $array The array to change values with keys + * @param bool $ignoreDuplicatedValues (optional) If is set to true, duplicated values are ignored. This means that + * when there is more than 1 value and that values become key, only the last + * value will be used with it's key, because other will be overridden. + * Otherwise - values are preserved and keys assigned to that values are + * returned as an array. + * @return array + * + * Example of $ignoreDuplicatedValues = false: + * - provided array + * $array = [ + * 'lorem' => 100, // <-- Duplicated value + * 'ipsum' => 200, + * 'dolor' => 100, // <-- Duplicated value + * ]; + * + * - result + * $replaced = [ + * 100 => [ + * 'lorem', // <-- Key of duplicated value + * 'dolor', // <-- Key of duplicated value + * ], + * 200 => 'ipsum', + * ]; + */ + public static function setKeysAsValues(array $array, $ignoreDuplicatedValues = true) + { + if (empty($array)) { + return []; + } + + $replaced = []; + + foreach ($array as $key => $value) { + /* + * The value it's an array? + * Let's replace keys with values in this array first + */ + if (is_array($value)) { + $replaced[$key] = self::setKeysAsValues($value, $ignoreDuplicatedValues); + continue; + } + + /* + * Duplicated values shouldn't be ignored and processed value is used as key already? + * Let's use an array and that will contain all values (to avoid ignoring / overriding duplicated values) + */ + if (!$ignoreDuplicatedValues && isset($replaced[$value])) { + $existing = self::makeArray($replaced[$value]); + + $replaced[$value] = array_merge($existing, [ + $key, + ]); + + continue; + } + + /* + * Standard behaviour + */ + $replaced[$value] = $key; + } + + return $replaced; + } + + /** + * Applies ksort() function recursively in the given array + * + * @param array $array The array to sort + * @param int $sortFlags (optional) Options of ksort() function + * @return array|null + */ + public static function ksortRecursive(array &$array, $sortFlags = SORT_REGULAR) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return null; + } + + $effect = $array; + ksort($effect, $sortFlags); + + foreach ($effect as &$value) { + if (is_array($value)) { + ksort($value, $sortFlags); + } + } + + return $effect; + } + + /** + * Returns count / amount of elements that are not array + * + * @param array $array The array to count + * @return int|null + */ + public static function getNonArrayElementsCount(array $array) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return null; + } + + $count = 0; + + foreach ($array as &$value) { + if (is_array($value)) { + $count += self::getNonArrayElementsCount($value); + continue; + } + + ++$count; + } + + return $count; + } + + /** + * Converts given string with special separators to array + * + * Example: + * ~ string: + * "light:jasny|dark:ciemny" + * + * ~ array as a result: + * [ + * 'light' => 'jasny', + * 'dark' => 'ciemny', + * ] + * + * @param string $string The string to be converted + * @param string $separator (optional) Separator used between name-value pairs in the string + * @param string $valuesKeysSeparator (optional) Separator used between name and value in the string + * @return array + */ + public static function string2array($string, $separator = '|', $valuesKeysSeparator = ':') + { + /* + * Empty string? + * Nothing to do + */ + if (empty($string)) { + return null; + } + + $array = []; + $exploded = explode($separator, $string); + + foreach ($exploded as $item) { + $exploded2 = explode($valuesKeysSeparator, $item); + + if (count($exploded2) == 2) { + $key = trim($exploded2[0]); + $value = trim($exploded2[1]); + + $array[$key] = $value; + } + } + + return $array; + } + + /** + * Returns information if given keys exist in given array + * + * @param array $keys The keys to find + * @param array $array The array which maybe contains keys + * @param bool $explicit (optional) If is set to true, all keys should exist in given array. Otherwise - not all. + * @return bool + */ + public static function areKeysInArray($keys, $array, $explicit = true) + { + $effect = false; + + if (is_array($array) && !empty($array)) { + $firstKey = true; + + foreach ($keys as $key) { + $exists = array_key_exists($key, $array); + + if ($firstKey) { + $effect = $exists; + $firstKey = false; + } else { + if ($explicit) { + $effect = $effect && $exists; + + if (!$effect) { + break; + } + } else { + $effect = $effect || $exists; + + if ($effect) { + break; + } + } + } + } + } + + return $effect; + } + + /** + * Returns paths of the last elements + * + * @param array $array The array with elements + * @param string $separator (optional) Separator used in resultant strings. Default: ".". + * @param string $parentPath (optional) Path of the parent element. Default: "". + * @param string|array $stopIfMatchedBy (optional) Patterns of keys or paths that matched will stop the process + * of path building and including children of those keys or paths (recursive + * will not be used for keys in lower level of given array) + * @return array + * + * Examples - $stopIfMatchedBy argument: + * a) "\d+" + * b) [ + * "lorem\-", + * "\d+", + * ]; + */ + public static function getLastElementsPaths(array $array, $separator = '.', $parentPath = '', $stopIfMatchedBy = '') + { + $paths = []; + + if (!empty($array)) { + if (!empty($stopIfMatchedBy)) { + $stopIfMatchedBy = self::makeArray($stopIfMatchedBy); + } + + foreach ($array as $key => $value) { + $path = $key; + $stopRecursion = false; + + /* + * If the path of parent element is delivered, + * I have to use it and build longer path + */ + if (!empty($parentPath)) { + $pathTemplate = '%s%s%s'; + $path = sprintf($pathTemplate, $parentPath, $separator, $key); + } + + /* + * Check if the key or current path matches one of patterns at which the process should be stopped, + * the recursive not used. It means that I have to pass current value and stop processing of the + * array (don't go to the next step). + */ + if (!empty($stopIfMatchedBy)) { + foreach ($stopIfMatchedBy as $rawPattern) { + $pattern = sprintf('|%s|', $rawPattern); + + if (preg_match($pattern, $key) || preg_match($pattern, $path)) { + $stopRecursion = true; + break; + } + } + } + + /* + * The value is passed to the returned array if: + * - it's not an array + * or + * - the process is stopped, recursive is not used + */ + if (!is_array($value) || (is_array($value) && empty($value)) || $stopRecursion) { + $paths[$path] = $value; + continue; + } + + /* + * Let's iterate through the next level, using recursive + */ + if (is_array($value)) { + $recursivePaths = self::getLastElementsPaths($value, $separator, $path, $stopIfMatchedBy); + $paths += $recursivePaths; + } + } + } + + return $paths; + } + + /** + * Makes and returns an array for given variable + * + * @param mixed $variable Variable that should be an array + * @return array + */ + public static function makeArray($variable) + { + if (is_array($variable)) { + return $variable; + } + + return [$variable]; + } + + /** + * Returns information if keys / indexes of given array are matched by given pattern + * + * @param array $array The array to check + * @param string $pattern The pattern which keys / indexes should match, e.g. "\d+" + * @param bool $firstLevelOnly (optional) If is set to true, all keys / indexes are checked. Otherwise - from the + * first level only. + * @return bool + */ + public static function areAllKeysMatchedByPattern($array, $pattern, $firstLevelOnly = false) + { + /* + * It's not an array or it's empty array? + */ + if (!is_array($array) || (is_array($array) && empty($array))) { + return false; + } + + /* + * I suppose that all are keys are matched + * and then I have to look for keys that don't matches + */ + $areMatched = true; + + /* + * Building the pattern + */ + $rawPattern = $pattern; + $pattern = sprintf('|%s|', $rawPattern); + + foreach ($array as $key => $value) { + /* + * Not matched? So I have to stop the iteration, because one not matched key + * means that not all keys are matched by given pattern + */ + if (!preg_match($pattern, $key)) { + $areMatched = false; + break; + } + + /* + * The not matching key was not found and the value is an array? + * Let's begin recursive looking for result + */ + if ($areMatched && is_array($value) && !$firstLevelOnly) { + $areMatched = self::areAllKeysMatchedByPattern($value, $rawPattern, $firstLevelOnly); + } + } + + return $areMatched; + } + + /** + * Returns information if keys / indexes of given array are integers, in other words if the array contains + * zero-based keys / indexes + * + * @param array $array The array to check + * @param bool $firstLevelOnly (optional) If is set to true, all keys / indexes are checked. Otherwise - from the + * first level only. + * @return bool + */ + public static function areAllKeysIntegers($array, $firstLevelOnly = false) + { + $pattern = '\d+'; + + return self::areAllKeysMatchedByPattern($array, $pattern, $firstLevelOnly); + } + + /** + * Returns value of given array set under given path of keys, of course if the value exists. + * The keys should be delivered in the same order as used by source array. + * + * @param array $array The array which should contains a value + * @param array $keys Keys, path of keys, to find in given array + * @return mixed + * + * Examples: + * a) $array + * [ + * 'some key' => [ + * 'another some key' => [ + * 'yet another key' => 123, + * ], + * 'some different key' => 456, + * ] + * ] + * + * b) $keys + * [ + * 'some key', + * 'another some key', + * 'yet another key', + * ] + * + * Based on the above examples will return: + * 123 + */ + public static function getValueByKeysPath(array $array, array $keys) + { + $value = null; + + if (!empty($array) && self::issetRecursive($array, $keys)) { + foreach ($keys as $key) { + $value = $array[$key]; + array_shift($keys); + + if (is_array($value) && !empty($keys)) { + $value = self::getValueByKeysPath($value, $keys); + } + + break; + } + } + + return $value; + } + + /** + * Returns information if given path of keys are set is given array. + * The keys should be delivered in the same order as used by source array. + * + * @param array $array The array to check + * @param array $keys Keys, path of keys, to find in given array + * @return bool + * + * Examples: + * a) $array + * [ + * 'some key' => [ + * 'another some key' => [ + * 'yet another key' => 123, + * ], + * 'some different key' => 456, + * ] + * ] + * + * b) $keys + * [ + * 'some key', + * 'another some key', + * 'yet another key', + * ] + */ + public static function issetRecursive(array $array, array $keys) + { + $isset = false; + + if (!empty($array)) { + foreach ($keys as $key) { + $isset = isset($array[$key]); + + if ($isset) { + $newArray = $array[$key]; + array_shift($keys); + + if (is_array($newArray) && !empty($keys)) { + $isset = self::issetRecursive($newArray, $keys); + } + } + + break; + } + } + + return $isset; + } + + /** + * Returns all values of given key. + * It may be useful when you want to retrieve all values of one column. + * + * @param array $array The array which should contain values of the key + * @param string $key The key + * @return array|null + */ + public static function getAllValuesOfKey(array $array, $key) + { + if (empty($array)) { + return null; + } + + $values = []; + + foreach ($array as $index => $value) { + if ($index === $key) { + $values[] = $value; + continue; + } + + if (is_array($value)) { + $recursiveValues = self::getAllValuesOfKey($value, $key); + + if (!empty($recursiveValues)) { + $values = array_merge($values, $recursiveValues); + } + } + } + + return $values; + } + + /** + * Sets positions for each element / child of given array and returns the array + * + * Position for the 1st element / child of a parent is set to 1 and incremented for the next element and + * so on. Each parent is treated as separate array, so its elements are treated as positioned at 1st level. + * + * @param array $array The array which should has values of position for each element + * @param string $keyName (optional) Name of key which will contain the position value + * @param int $startPosition (optional) Default, start value of the position for main / given array, not the + * children + * @return array + */ + public static function setPositions(array $array, $keyName = self::POSITION_KEY_NAME, $startPosition = null) + { + if (!empty($array)) { + $childPosition = 1; + + if ($startPosition !== null) { + $array[$keyName] = $startPosition; + } + + foreach ($array as &$value) { + if (is_array($value)) { + $value = self::setPositions($value, $keyName, $childPosition); + ++$childPosition; + } + } + } + + return $array; + } + + /** + * Trims string values of given array and returns the new array + * + * @param array $array The array which values should be trimmed + * @return array + */ + public static function trimRecursive(array $array) + { + $effect = $array; + + if (!empty($array)) { + $effect = []; + + foreach ($array as $key => $value) { + if (is_array($value)) { + $effect[$key] = self::trimRecursive($value); + continue; + } + + if (is_string($value)) { + $value = trim($value); + } + + $effect[$key] = $value; + } + } + + return $effect; + } + + /** + * Sorts an array by keys given in second array as values. + * Keys which are not in array with order are pushed after sorted elements. + * + * Example: + * - array to sort: + * + * array( + * 'lorem' => array( + * 'ipsum' + * ), + * 'dolor' => array( + * 'sit', + * 'amet' + * ), + * 'neque' => 'neque' + * ) + * + * - keys order: + * + * array( + * 'dolor', + * 'lorem' + * ) + * + * - the result: + * + * array( + * 'dolor' => array( + * 'sit', + * 'amet' + * ), + * 'lorem' => array( + * 'ipsum' + * ), + * 'neque' => 'neque' // <-- the rest, values of other keys + * ) + * + * + * @param array $array An array to sort + * @param array $keysOrder An array with keys of the 1st argument in proper / required order + * @return array|null + */ + public static function sortByCustomKeysOrder(array $array, array $keysOrder) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return null; + } + + $ordered = []; + + /* + * 1st iteration: + * Get elements in proper / required order + */ + if (!empty($keysOrder)) { + foreach ($keysOrder as $key) { + if (isset($array[$key])) { + $ordered[$key] = $array[$key]; + unset($array[$key]); + } + } + } + + /* + * 2nd iteration: + * Get the rest of elements + */ + if (!empty($array)) { + foreach ($array as $key => $element) { + $ordered[$key] = $element; + } + } + + return $ordered; + } + + /** + * Returns smartly imploded string + * + * Separators located at the beginning or end of elements are removed. + * It's required to avoid problems with duplicated separator, e.g. "first//second/third", where separator is a + * "/" string. + * + * @param array $array The array with elements to implode + * @param string $separator Separator used to stick together elements of given array + * @return string + */ + public static function implodeSmart(array $array, $separator) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return ''; + } + + foreach ($array as &$element) { + if (is_array($element)) { + $element = self::implodeSmart($element, $separator); + } + + if (Regex::startsWith($element, $separator)) { + $element = substr($element, 1); + } + + if (Regex::endsWith($element, $separator)) { + $element = substr($element, 0, strlen($element) - 1); + } + } + + return implode($separator, $array); + } + + /** + * Returns information if given array is empty, iow. information if all elements of given array are empty + * + * @param array $array The array to verify + * @param bool $strictNull (optional) If is set to true elements are verified if they are null. Otherwise - only + * if they are empty (e.g. null, '', 0, array()). + * @return bool + */ + public static function areAllValuesEmpty(array $array, $strictNull = false) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return false; + } + + foreach ($array as $element) { + /* + * If elements are verified if they are exactly null and the element is: + * - not an array + * - not null + * or elements are NOT verified if they are exactly null and the element is: + * - not empty (e.g. null, '', 0, array()) + * + * If one of the above is true, not all elements of given array are empty + */ + if ((!is_array($element) && $strictNull && $element !== null) || !empty($element)) { + return false; + } + } + + return true; + } + + /** + * Returns an array containing all the entries from 1st array that are not present in 2nd array. + * An item from 1st array is the same as in 2nd array if both, keys and values, are the same. + * + * Example of difference: + * $array1 = [ + * 1 => 'Lorem', + * 2 => 'ipsum, + * ]; + * + * $array2 = [ + * 1 => 'Lorem', + * 5 => 'ipsum, // <-- The same values, but different key. Here we got 5, in 1st array - 2. + * ]; + * + * @param array $array1 The 1st array to verify + * @param array $array2 The 2nd array to verify + * @param bool $valuesOnly (optional) If is set to true, compares values only. Otherwise - keys and values + * (default behaviour). + * @return array + */ + public static function arrayDiffRecursive(array $array1, array $array2, $valuesOnly = false) + { + $effect = []; + + /* + * Values should be compared only and both arrays are one-dimensional? + * Let's find difference by using simple function + */ + if ($valuesOnly && self::getDimensionsCount($array1) == 1 && self::getDimensionsCount($array2) == 1) { + return array_diff($array1, $array2); + } + + foreach ($array1 as $key => $value) { + $array2HasKey = array_key_exists($key, $array2); + + /* + * Values should be compared only? + */ + if ($valuesOnly) { + $difference = null; + + if (is_array($value)) { + if ($array2HasKey && is_array($array2[$key])) { + $difference = self::arrayDiffRecursive($value, $array2[$key], $valuesOnly); + } + } else { + /* + * 2nd array hasn't key from 1st array? + * OR + * Key exists in both, 1st and 2nd array, but values are different? + */ + if (!$array2HasKey || ($array2HasKey && $value != $array2[$key])) { + $difference = $value; + } + } + + if ($difference !== null) { + $effect[] = $difference; + } + } else { + /* + * The key exists in 2nd array? + */ + if ($array2HasKey) { + /* + * The value it's an array (it's a nested array)? + */ + if (is_array($value)) { + $diff = []; + + if (is_array($array2[$key])) { + /* + * Let's verify the nested array + */ + $diff = self::arrayDiffRecursive($value, $array2[$key], $valuesOnly); + } + + if (empty($diff)) { + continue; + } + + $effect[$key] = $diff; + } else { + /* + * Value is different than in 2nd array? + * OKay, I've got difference + */ + if ($value != $array2[$key]) { + $effect[$key] = $value; + } + } + } else { + /* + * OKay, I've got difference + */ + $effect[$key] = $value; + } + } + } + + return $effect; + } + + /** + * Returns an index / key of given element in given array + * + * @param array $array The array to verify + * @param mixed $element The element who index / key is needed + * @return bool|null|mixed + */ + public static function getIndexOf(array $array, $element) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return false; + } + + foreach ($array as $index => $value) { + if ($value === $element) { + return $index; + } + } + + return null; + } + + /** + * Returns an array with incremented indexes / keys + * + * @param array $array The array which indexes / keys should be incremented + * @param int|null $startIndex (optional) Index from which incrementation should be started. If not provided, + * the first index / key will be used. + * @param int $incrementStep (optional) Value used for incrementation. The step of incrementation. + * @return array + */ + public static function incrementIndexes(array $array, $startIndex = null, $incrementStep = 1) + { + if (!empty($array)) { + $valuesToIncrement = []; + + /* + * Start index not provided? + * Let's look for the first index / key of given array + */ + if ($startIndex === null) { + $startIndex = self::getFirstKey($array); + } + + /* + * Is the start index a numeric value? + * Other indexes / keys cannot be incremented + */ + if (is_numeric($startIndex)) { + /* + * 1st step: + * Get values which indexes should be incremented and remove those values from given array + */ + foreach ($array as $index => $value) { + if ($index < $startIndex) { + continue; + } + + $valuesToIncrement[$index] = $value; + unset($array[$index]); + } + + /* + * 2nd step: + * Increment indexes of gathered values + */ + if (!empty($valuesToIncrement)) { + foreach ($valuesToIncrement as $oldIndex => $value) { + $newIndex = $oldIndex + $incrementStep; + $array[$newIndex] = $value; + } + } + } + } + + return $array; + } + + /** + * Returns next element of given array related to given element + * + * @param array $array The array with elements + * @param mixed $element Element for who next element should be returned + * @return null|mixed + */ + public static function getNextElement(array $array, $element) + { + return self::getNeighbour($array, $element); + } + + /** + * Returns previous element of given array related to given element + * + * @param array $array The array with elements + * @param mixed $element Element for who previous element should be returned + * @return null|mixed + */ + public static function getPreviousElement(array $array, $element) + { + return self::getNeighbour($array, $element, false); + } + + /** + * Returns information if given array is a multi dimensional array + * + * @param array $array The array to verify + * @return bool|null + */ + public static function isMultiDimensional(array $array) + { + /* + * No elements? + * Nothing to do + */ + if (empty($array)) { + return null; + } + + return count($array) !== count($array, COUNT_RECURSIVE); + } + + /** + * Returns count of dimensions, maximum nesting level actually, in given array + * + * @param array $array The array to verify + * @return int + */ + public static function getDimensionsCount(array $array) + { + $dimensionsCount = 1; + + foreach ($array as $value) { + if (is_array($value)) { + /* + * I have to increment returned value, because that means we've got 1 level more (if the value is an + * array) + */ + $count = self::getDimensionsCount($value) + 1; + + if ($count > $dimensionsCount) { + $dimensionsCount = $count; + } + } + } + + return $dimensionsCount; + } + + /** + * Returns neighbour (next or previous element) for given element + * + * @param array $array The array with elements + * @param mixed $element Element for who next element should be returned + * @param bool $next (optional) If is set to true, returns next neighbour. Otherwise - previous. + * @return mixed|null + */ + private static function getNeighbour(array $array, $element, $next = true) + { + $noNext = $next && self::isLastElement($array, $element); + $noPrevious = !$next && self::isFirstElement($array, $element); + + /* + * No elements? + * OR + * Given element does not exist in given array? + * OR + * Next neighbour should be returned and given element is last? + * OR + * Previous neighbour should be returned and given element is first? + * + * Nothing to do + */ + if (empty($array) || !in_array($element, $array) || $noNext || $noPrevious) { + return null; + } + + $neighbourKey = null; + $keys = array_keys($array); + $elementKey = self::getIndexOf($array, $element); + $indexOfKey = self::getIndexOf($keys, $elementKey); + + /* + * Index of element or of element's key is unknown? + * Probably the element does not exist in given array, so... nothing to do + */ + if ($elementKey === null || $indexOfKey === null) { + return null; + } + + /* + * Looking for key of the neighbour (next or previous element) + */ + if ($next) { + ++$indexOfKey; + } else { + --$indexOfKey; + } + + /* + * Let's prepare key of the neighbour and... + * ...we've got the neighbour :) + */ + $neighbourKey = $keys[$indexOfKey]; + + return $array[$neighbourKey]; + } +} diff --git a/src/Meritoo/Common/Utilities/Bundle.php b/src/Meritoo/Common/Utilities/Bundle.php new file mode 100644 index 0000000..ad2a1cf --- /dev/null +++ b/src/Meritoo/Common/Utilities/Bundle.php @@ -0,0 +1,46 @@ + + * @copyright Meritoo.pl + */ +class Bundle +{ + /** + * Returns path to view / template of given bundle + * + * @param string $viewPath Path of the view / template, e.g. "MyDirectory/my-template" + * @param string $bundleName Name of the bundle, e.g. "MyExtraBundle" + * @param string $extension (optional) Extension of the view / template + * @return string|null + */ + public static function getBundleViewPath($viewPath, $bundleName, $extension = 'html.twig') + { + /* + * Unknown path, extension of the view / template or name of the bundle? + * Nothing to do + */ + if (empty($viewPath) || empty($bundleName) || empty($extension)) { + return null; + } + + /* + * Path of the view / template doesn't end with given extension? + */ + if (!Regex::endsWith($viewPath, $extension)) { + $viewPath = sprintf('%s.%s', $viewPath, $extension); + } + + return sprintf('%s:%s', $bundleName, $viewPath); + } +} diff --git a/src/Meritoo/Common/Utilities/Composer.php b/src/Meritoo/Common/Utilities/Composer.php new file mode 100644 index 0000000..3e33e3a --- /dev/null +++ b/src/Meritoo/Common/Utilities/Composer.php @@ -0,0 +1,69 @@ + + * @copyright Meritoo.pl + */ +class Composer +{ + /** + * Name of the Composer's main file with configuration in Json format + * + * @var string + */ + const FILE_NAME_MAIN = 'composer.json'; + + /** + * Returns value from composer.json file + * + * @param string $composerJsonPath Path of composer.json file + * @param string $nodeName Name of node who value should be returned + * @return string|null + */ + public static function getValue($composerJsonPath, $nodeName) + { + $composerJsonString = is_string($composerJsonPath); + $composerJsonReadable = false; + + if ($composerJsonString) { + $composerJsonReadable = is_readable($composerJsonPath); + } + + /* + * Provided path or name of node are invalid? + * The composer.json file doesn't exist or isn't readable? + * Name of node is unknown? + * + * Nothing to do + */ + if (!$composerJsonString || !is_string($nodeName) || !$composerJsonReadable || empty($nodeName)) { + return null; + } + + $content = file_get_contents($composerJsonPath); + $data = json_decode($content); + + /* + * Unknown data from the composer.json file or there is no node with given name? + * Nothing to do + */ + if ($data === null || !isset($data->{$nodeName})) { + return null; + } + + /* @var stdClass $data */ + return $data->{$nodeName}; + } +} diff --git a/src/Meritoo/Common/Utilities/Date.php b/src/Meritoo/Common/Utilities/Date.php new file mode 100644 index 0000000..1b59b39 --- /dev/null +++ b/src/Meritoo/Common/Utilities/Date.php @@ -0,0 +1,714 @@ + + * @copyright Meritoo.pl + */ +class Date +{ + /** + * The 'days' unit of date difference. + * Difference between dates in days. + * + * @var string + */ + const DATE_DIFFERENCE_UNIT_DAYS = 'days'; + + /** + * The 'hours' unit of date difference. + * Difference between dates in hours. + * + * @var string + */ + const DATE_DIFFERENCE_UNIT_HOURS = 'hours'; + + /** + * The 'minutes' unit of date difference. + * Difference between dates in minutes. + * + * @var string + */ + const DATE_DIFFERENCE_UNIT_MINUTES = 'minutes'; + + /** + * The 'months' unit of date difference. + * Difference between dates in months. + * + * @var string + */ + const DATE_DIFFERENCE_UNIT_MONTHS = 'months'; + + /** + * The 'years' unit of date difference. + * Difference between dates in years. + * + * @var string + */ + const DATE_DIFFERENCE_UNIT_YEARS = 'years'; + + /** + * Returns start and end date for given period. + * The dates are returned in an array with indexes 'start' and 'end'. + * + * @param int $period The period, type of period. One of DatePeriod class constants, e.g. DatePeriod::LAST_WEEK. + * @return DatePeriod + */ + public static function getDatesForPeriod($period) + { + $datePeriod = null; + + if (DatePeriod::isCorrectPeriod($period)) { + $dateStart = null; + $dateEnd = null; + + switch ($period) { + case DatePeriod::LAST_WEEK: + $thisWeekStart = new DateTime('this week'); + + $dateStart = clone $thisWeekStart; + $dateEnd = clone $thisWeekStart; + + $dateStart->sub(new DateInterval('P7D')); + $dateEnd->sub(new DateInterval('P1D')); + + break; + case DatePeriod::THIS_WEEK: + $dateStart = new DateTime('this week'); + + $dateEnd = clone $dateStart; + $dateEnd->add(new DateInterval('P6D')); + + break; + case DatePeriod::NEXT_WEEK: + $dateStart = new DateTime('this week'); + $dateStart->add(new DateInterval('P7D')); + + $dateEnd = clone $dateStart; + $dateEnd->add(new DateInterval('P6D')); + + break; + case DatePeriod::LAST_MONTH: + $dateStart = new DateTime('first day of last month'); + $dateEnd = new DateTime('last day of last month'); + + break; + case DatePeriod::THIS_MONTH: + $lastMonth = self::getDatesForPeriod(DatePeriod::LAST_MONTH); + $nextMonth = self::getDatesForPeriod(DatePeriod::NEXT_MONTH); + + $dateStart = $lastMonth->getEndDate(); + $dateStart->add(new DateInterval('P1D')); + + $dateEnd = $nextMonth->getStartDate(); + $dateEnd->sub(new DateInterval('P1D')); + + break; + case DatePeriod::NEXT_MONTH: + $dateStart = new DateTime('first day of next month'); + $dateEnd = new DateTime('last day of next month'); + + break; + case DatePeriod::LAST_YEAR: + case DatePeriod::THIS_YEAR: + case DatePeriod::NEXT_YEAR: + $dateStart = new DateTime(); + $dateEnd = new DateTime(); + + if ($period == DatePeriod::LAST_YEAR || $period == DatePeriod::NEXT_YEAR) { + $yearDifference = 1; + + if ($period == DatePeriod::LAST_YEAR) { + $yearDifference *= -1; + } + + $modifyString = sprintf('%s year', $yearDifference); + $dateStart->modify($modifyString); + $dateEnd->modify($modifyString); + } + + $year = $dateStart->format('Y'); + $dateStart->setDate($year, 1, 1); + $dateEnd->setDate($year, 12, 31); + + break; + } + + if ($dateStart !== null && $dateEnd !== null) { + $dateStart->setTime(0, 0, 0); + $dateEnd->setTime(23, 59, 59); + + $datePeriod = new DatePeriod($dateStart, $dateEnd); + } + } + + return $datePeriod; + } + + /** + * Generates and returns random time (the hour, minute and second values) + * + * @param string $format (optional) Format of returned value. A string acceptable by the DateTime::format() + * method. + * @return string|null + */ + public static function generateRandomTime($format = 'H:i:s') + { + $dateTime = new DateTime(); + + /* + * Format si empty or is incorrect? + * Nothing to do + */ + if (empty($format) || $dateTime->format($format) === $format) { + return null; + } + + $hours = []; + $minutes = []; + $seconds = []; + + for ($i = 1; $i <= 23; ++$i) { + $hours[] = $i; + } + + for ($i = 1; $i <= 59; ++$i) { + $minutes[] = $i; + } + + for ($i = 1; $i <= 59; ++$i) { + $seconds[] = $i; + } + + /* + * Prepare random time (hour, minute and second) + */ + $hour = $hours[array_rand($hours)]; + $minute = $minutes[array_rand($minutes)]; + $second = $seconds[array_rand($seconds)]; + + return $dateTime + ->setTime($hour, $minute, $second) + ->format($format); + } + + /** + * Returns current day of week + * + * @return int + */ + public static function getCurrentDayOfWeek() + { + $now = new DateTime(); + + $year = $now->format('Y'); + $month = $now->format('m'); + $day = $now->format('d'); + + return self::getDayOfWeek($year, $month, $day); + } + + /** + * Returns day of week (number 0 to 6, 0 - sunday, 6 - saturday). + * Based on the Zeller's algorithm (http://pl.wikipedia.org/wiki/Kalendarz_wieczny). + * + * @param int $year The year value + * @param int $month The month value + * @param int $day The day value + * + * @return int + * @throws IncorrectDatePartException + */ + public static function getDayOfWeek($year, $month, $day) + { + $year = (int)$year; + $month = (int)$month; + $day = (int)$day; + + /* + * Oops, incorrect year + */ + if ($year <= 0) { + throw new IncorrectDatePartException($year, DatePartType::YEAR); + } + + /* + * Oops, incorrect month + */ + if ($month < 1 || $month > 12) { + throw new IncorrectDatePartException($month, DatePartType::MONTH); + } + + /* + * Oops, incorrect day + */ + if ($day < 1 || $day > 31) { + throw new IncorrectDatePartException($day, DatePartType::DAY); + } + + if ($month < 3) { + $count = 0; + $yearValue = $year - 1; + } else { + $count = 2; + $yearValue = $year; + } + + $firstPart = floor(23 * $month / 9); + $secondPart = floor($yearValue / 4); + $thirdPart = floor($yearValue / 100); + $fourthPart = floor($yearValue / 400); + + return ($firstPart + $day + 4 + $year + $secondPart - $thirdPart + $fourthPart - $count) % 7; + } + + /** + * Returns based on locale name of current weekday + * + * @return string + */ + public static function getCurrentDayOfWeekName() + { + $now = new DateTime(); + + $year = $now->format('Y'); + $month = $now->format('m'); + $day = $now->format('d'); + + return self::getDayOfWeekName($year, $month, $day); + } + + /** + * Returns name of weekday based on locale + * + * @param int $year The year value + * @param int $month The month value + * @param int $day The day value + * @return string + */ + public static function getDayOfWeekName($year, $month, $day) + { + $hour = 0; + $minute = 0; + $second = 0; + + $time = mktime($hour, $minute, $second, $month, $day, $year); + $name = strftime('%A', $time); + + $encoding = mb_detect_encoding($name); + + if ($encoding === false) { + $name = mb_convert_encoding($name, 'UTF-8', 'ISO-8859-2'); + } + + return $name; + } + + /** + * Returns difference between given dates. + * + * The difference is calculated in units based on the 3rd argument or all available unit of date difference + * (defined as DATE_DIFFERENCE_UNIT_* constants of this class). + * + * The difference is also whole / complete value for given unit instead of relative value as may be received by + * DateTime::diff() method, e.g.: + * - 2 days, 50 hours + * instead of + * - 2 days, 2 hours + * + * If the unit of date difference is null, all units are returned in array (units are keys of the array). + * Otherwise - one, integer value is returned. + * + * @param string|DateTime $dateStart The start date + * @param string|DateTime $dateEnd The end date + * @param int $differenceUnit (optional) Unit of date difference. One of this class + * DATE_DIFFERENCE_UNIT_* constants. If is set to null all units are + * returned in the array. + * @return array|int + */ + public static function getDateDifference($dateStart, $dateEnd, $differenceUnit = null) + { + $validDateStart = self::isValidDate($dateStart, true); + $validDateEnd = self::isValidDate($dateEnd, true); + + /* + * The start or end date is unknown? + * or + * The start or end date is not valid date? + * + * Nothing to do + */ + if (empty($dateStart) || empty($dateEnd) || !$validDateStart || !$validDateEnd) { + return null; + } + + $dateStart = self::getDateTime($dateStart, true); + $dateEnd = self::getDateTime($dateEnd, true); + + $difference = []; + $dateDiff = $dateEnd->getTimestamp() - $dateStart->getTimestamp(); + + $daysInSeconds = 0; + $hoursInSeconds = 0; + + $hourSeconds = 60 * 60; + $daySeconds = $hourSeconds * 24; + + /* + * These units are related, because while calculating difference in the lowest unit, difference in the + * highest unit is required, e.g. while calculating hours I have to know difference in days + */ + $relatedUnits = [ + self::DATE_DIFFERENCE_UNIT_DAYS, + self::DATE_DIFFERENCE_UNIT_HOURS, + self::DATE_DIFFERENCE_UNIT_MINUTES, + ]; + + if ($differenceUnit === null || $differenceUnit == self::DATE_DIFFERENCE_UNIT_YEARS) { + $diff = $dateEnd->diff($dateStart); + + /* + * Difference between dates in years should be returned only? + */ + if ($differenceUnit == self::DATE_DIFFERENCE_UNIT_YEARS) { + return $diff->y; + } + + $difference[self::DATE_DIFFERENCE_UNIT_YEARS] = $diff->y; + } + + if ($differenceUnit === null || $differenceUnit == self::DATE_DIFFERENCE_UNIT_MONTHS) { + $diff = $dateEnd->diff($dateStart); + + /* + * Difference between dates in months should be returned only? + */ + if ($differenceUnit == self::DATE_DIFFERENCE_UNIT_MONTHS) { + return $diff->m; + } + + $difference[self::DATE_DIFFERENCE_UNIT_MONTHS] = $diff->m; + } + + if ($differenceUnit === null || in_array($differenceUnit, $relatedUnits)) { + $days = (int)floor($dateDiff / $daySeconds); + + /* + * Difference between dates in days should be returned only? + */ + if ($differenceUnit == self::DATE_DIFFERENCE_UNIT_DAYS) { + return $days; + } + + /* + * All units should be returned? + */ + if ($differenceUnit === null) { + $difference[self::DATE_DIFFERENCE_UNIT_DAYS] = $days; + } + + /* + * Calculation for later usage + */ + $daysInSeconds = $days * $daySeconds; + } + + if ($differenceUnit === null || in_array($differenceUnit, $relatedUnits)) { + $hours = (int)floor(($dateDiff - $daysInSeconds) / $hourSeconds); + + /* + * Difference between dates in hours should be returned only? + */ + if ($differenceUnit == self::DATE_DIFFERENCE_UNIT_HOURS) { + return $hours; + } + + /* + * All units should be returned? + */ + if ($differenceUnit === null) { + $difference[self::DATE_DIFFERENCE_UNIT_HOURS] = $hours; + } + + /* + * Calculation for later usage + */ + $hoursInSeconds = $hours * $hourSeconds; + } + + if ($differenceUnit === null || $differenceUnit == self::DATE_DIFFERENCE_UNIT_MINUTES) { + $minutes = (int)floor(($dateDiff - $daysInSeconds - $hoursInSeconds) / 60); + + /* + * Difference between dates in minutes should be returned only? + */ + if ($differenceUnit == self::DATE_DIFFERENCE_UNIT_MINUTES) { + return $minutes; + } + + $difference[self::DATE_DIFFERENCE_UNIT_MINUTES] = $minutes; + } + + return $difference; + } + + /** + * Returns collection / set of dates for given start date and count of dates. + * Start from given date, add next, iterated value to given date interval and returns requested count of dates. + * + * @param DateTime $startDate The start date. Start of the collection / set. + * @param int $datesCount Count of dates in resulting collection / set + * @param string $intervalTemplate (optional) Template used to build date interval. It should contain "%d" as the + * placeholder which is replaced with a number that represents each iteration. + * Default: interval for days. + * @return array + */ + public static function getDatesCollection(DateTime $startDate, $datesCount, $intervalTemplate = 'P%dD') + { + $dates = []; + + /* + * The template used to build date interval have to be string. + * Otherwise cannot run preg_match() function and an error occurs. + */ + if (is_string($intervalTemplate)) { + /* + * Let's verify the interval template. It should contains the "%d" placeholder and something before and + * after it. + * + * Examples: + * - P%dD + * - P%dM + * - P1Y%dMT1H + */ + $intervalPattern = '/^(\w*)\%d(\w*)$/'; + $matches = []; + $matchCount = preg_match($intervalPattern, $intervalTemplate, $matches); + + if ($matchCount > 0 && (!empty($matches[1]) || !empty($matches[2]))) { + $datesCount = (int)$datesCount; + + for ($index = 1; $index <= $datesCount; ++$index) { + $date = clone $startDate; + $dates[$index] = $date->add(new DateInterval(sprintf($intervalTemplate, $index))); + } + } + } + + return $dates; + } + + /** + * Returns random date based on given start date + * + * @param DateTime $startDate The start date. Start of the random date. + * @param int $start (optional) Start of random partition + * @param int $end (optional) End of random partition + * @param string $intervalTemplate (optional) Template used to build date interval. The placeholder is replaced + * with next, iterated value. + * @return DateTime + */ + public static function getRandomDate(DateTime $startDate = null, $start = 1, $end = 100, $intervalTemplate = 'P%sD') + { + if ($startDate === null) { + $startDate = new DateTime(); + } + + $start = (int)$start; + $end = (int)$end; + + /* + * Incorrect end of random partition? + * Use start as the end of random partition + */ + if ($end < $start) { + $end = $start; + } + + $randomDate = clone $startDate; + $randomInterval = new DateInterval(sprintf($intervalTemplate, rand($start, $end))); + + return $randomDate->add($randomInterval); + } + + /** + * Returns the DateTime object for given value. + * If the DateTime object cannot be created, false is returned. + * + * @param mixed $value The value which maybe is a date + * @param bool $allowCompoundFormats (optional) If is set to true, the compound formats used to create an + * instance of DateTime class are allowed (e.g. "now", "last day of next + * month", "yyyy"). Otherwise - not and every incorrect value is refused. + * @param string $dateFormat (optional) Format of date used to verify if given value is actually a date. + * It should be format matched to the given value, e.g. "Y-m-d H:i" for + * "2015-01-01 10:00" value. + * @return DateTime|bool + */ + public static function getDateTime($value, $allowCompoundFormats = false, $dateFormat = 'Y-m-d') + { + /* + * Empty value? + * Nothing to do :) + */ + if (empty($value)) { + return false; + } + + /* + * Instance of DateTime class? + * Nothing to do :) + */ + if ($value instanceof DateTime) { + return $value; + } + + try { + try { + /* + * Pass the value to the constructor. Maybe it's one of the allowed relative formats. + * Examples: "now", "last day of next month" + */ + $date = new DateTime($value); + + /* + * Instance of the DateTime class was created. + * Let's verify if given value is really proper date. + */ + $dateFromFormat = DateTime::createFromFormat($dateFormat, $value); + + if ($dateFromFormat === false) { + /* + * Nothing to do more, because: + * a) instance of the DateTime was created + * and + * b) if createFromFormat() method failed, given value is one of the allowed relative formats + * ("now", "last day of next month") + * and... + */ + if ($allowCompoundFormats) { + /* + * ...and + * c) it's not an integer, e.g. not 10 or 100 or 1000 + */ + if (!is_numeric($value)) { + return $date; + } + } else { + $specialFormats = [ + 'now', + ]; + + /* + * ...and + * c) it's special compound format that contains characters that each may be used by + * DateTime::format() method and it raises problem while trying to verify the value at the end + * of this method: + * + * (new DateTime())->format($value); + * + * So, I have to refuse those special compound formats if they are not explicitly declared as + * compound (2nd argument of this method, set to false by default) + */ + if (in_array($value, $specialFormats)) { + return false; + } + } + } /* + * Verify instance of the DateTime created by constructor and by createFromFormat() method. + * After formatting, these dates should be the same. + */ + else { + if ($dateFromFormat->format($dateFormat) === $value) { + return $date; + } + } + } catch (\Exception $exception) { + if (!$allowCompoundFormats) { + return false; + } + } + + /* + * Does the value is a string that may be used to format date? + * Example: "Y-m-d" + */ + $dateString = (new DateTime())->format($value); + + if ($dateString != $value) { + return new DateTime($dateString); + } + } catch (\Exception $exception) { + } + + return false; + } + + /** + * Returns information if given value is valid date + * + * @param mixed $value The value which maybe is a date + * @param bool $allowCompoundFormats (optional) If is set to true, the compound formats used to create an + * instance of DateTime class are allowed (e.g. "now", "last day of next + * month", "yyyy"). Otherwise - not and every incorrect value is refused. + * @return bool + */ + public static function isValidDate($value, $allowCompoundFormats = false) + { + return self::getDateTime($value, $allowCompoundFormats) instanceof DateTime; + } + + /** + * Returns information if given format of date is valid + * + * @param string $format The validated format of date + * @return bool + */ + public static function isValidDateFormat($format) + { + if (empty($format) || !is_string($format)) { + return false; + } + + /* + * Datetime to string + */ + $formatted = (new DateTime())->format($format); + + /* + * Formatted date it's the format who is validated? + * The format is invalid + */ + if ($formatted == $format) { + return false; + } + + /* + * Validate the format used to create the datetime + */ + $fromFormat = DateTime::createFromFormat($format, $formatted); + + /* + * It's instance of DateTime? + * The format is valid + */ + if ($fromFormat instanceof DateTime) { + return true; + } + + return $fromFormat instanceof DateTime; + } +} diff --git a/src/Meritoo/Common/Utilities/DatePeriod.php b/src/Meritoo/Common/Utilities/DatePeriod.php new file mode 100644 index 0000000..ac704f3 --- /dev/null +++ b/src/Meritoo/Common/Utilities/DatePeriod.php @@ -0,0 +1,195 @@ + + * @copyright Meritoo.pl + */ +class DatePeriod +{ + /** + * The period constant: last month + * + * @var int + */ + const LAST_MONTH = 4; + + /** + * The period constant: last week + * + * @var int + */ + const LAST_WEEK = 1; + + /** + * The period constant: last year + * + * @var int + */ + const LAST_YEAR = 7; + + /** + * The period constant: next month + * + * @var int + */ + const NEXT_MONTH = 6; + + /** + * The period constant: next week + * + * @var int + */ + const NEXT_WEEK = 3; + + /** + * The period constant: next year + * + * @var int + */ + const NEXT_YEAR = 9; + + /** + * The period constant: this month + * + * @var int + */ + const THIS_MONTH = 5; + + /** + * The period constant: this week + * + * @var int + */ + const THIS_WEEK = 2; + + /** + * The period constant: this year + * + * @var int + */ + const THIS_YEAR = 8; + + /** + * The start date of period + * + * @var DateTime + */ + private $startDate; + + /** + * The end date of period + * + * @var DateTime + */ + private $endDate; + + /** + * Class constructor + * + * @param DateTime $startDate (optional) The start date of period + * @param DateTime $endDate (optional) The end date of period + */ + public function __construct(DateTime $startDate = null, DateTime $endDate = null) + { + $this->startDate = $startDate; + $this->endDate = $endDate; + } + + /** + * Returns information if given period is correct + * + * @param int $period The period to verify + * @return bool + */ + public static function isCorrectPeriod($period) + { + return in_array($period, Reflection::getConstants(__CLASS__)); + } + + /** + * Returns formatted one of the period's date: start date or end date + * + * @param string $format Format used to format the date + * @param bool $startDate (optional) If is set to true, start date is formatted. Otherwise - end date. + * @return string + */ + public function getFormattedDate($format, $startDate = true) + { + $date = $this->getEndDate(); + + /* + * Start date should be formatted? + */ + if ($startDate) { + $date = $this->getStartDate(); + } + + /* + * Unknown date or format is invalid? + */ + if ($date === null || !Date::isValidDateFormat($format)) { + return ''; + } + + return $date->format($format); + } + + /** + * Returns the end date of period + * + * @return DateTime + */ + public function getEndDate() + { + return $this->endDate; + } + + /** + * Sets the end date of period + * + * @param DateTime $endDate (optional) The end date of period + * @return $this + */ + public function setEndDate(DateTime $endDate = null) + { + $this->endDate = $endDate; + + return $this; + } + + /** + * Returns the start date of period + * + * @return DateTime + */ + public function getStartDate() + { + return $this->startDate; + } + + /** + * Sets the start date of period + * + * @param DateTime $startDate (optional) The start date of period + * @return $this + */ + public function setStartDate(DateTime $startDate = null) + { + $this->startDate = $startDate; + + return $this; + } +} diff --git a/src/Meritoo/Common/Utilities/GeneratorUtility.php b/src/Meritoo/Common/Utilities/GeneratorUtility.php new file mode 100644 index 0000000..8783fdc --- /dev/null +++ b/src/Meritoo/Common/Utilities/GeneratorUtility.php @@ -0,0 +1,37 @@ + + * @copyright Meritoo.pl + */ +class GeneratorUtility +{ + /** + * Returns elements of generator + * + * @param Generator $generator The generator who elements should be returned + * @return array + */ + public static function getGeneratorElements(Generator $generator) + { + $elements = []; + + for (; $generator->valid(); $generator->next()) { + $elements[] = $generator->current(); + } + + return $elements; + } +} diff --git a/src/Meritoo/Common/Utilities/Locale.php b/src/Meritoo/Common/Utilities/Locale.php new file mode 100644 index 0000000..8322e81 --- /dev/null +++ b/src/Meritoo/Common/Utilities/Locale.php @@ -0,0 +1,106 @@ + + * @copyright Meritoo.pl + */ +class Locale +{ + /** + * Sets locale for given category using given language and country code + * + * @param int $category Named constant specifying the category of the functions affected by the locale + * setting. It's the same constant as required by setlocale() function. + * @param string $languageCode Language code, in ISO 639-1 format. Short form of the locale, e.g. "fr". + * @param string $countryCode (optional) Country code, in ISO 3166-1 alpha-2 format, e.g. "FR" + * @return bool + * + * Available categories (values of $category argument): + * - LC_ALL for all of the below + * - LC_COLLATE for string comparison, see strcoll() + * - LC_CTYPE for character classification and conversion, for example strtoupper() + * - LC_MONETARY for localeconv() + * - LC_NUMERIC for decimal separator (See also localeconv()) + * - LC_TIME for date and time formatting with strftime() + * - LC_MESSAGES for system responses (available if PHP was compiled with libintl) + */ + public static function setLocale($category, $languageCode, $countryCode = '') + { + $category = (int)$category; + + if (is_string($languageCode)) { + $languageCode = trim($languageCode); + } + + $availableCategories = [ + LC_ALL, + LC_COLLATE, + LC_CTYPE, + LC_MONETARY, + LC_NUMERIC, + LC_TIME, + LC_MESSAGES, + ]; + + if (empty($languageCode) || !in_array($category, $availableCategories)) { + return false; + } + + $localeLongForm = self::getLongForm($languageCode, $countryCode); + setlocale($category, $localeLongForm); + + return true; + } + + /** + * Returns long form of the locale + * + * @param string $languageCode Language code, in ISO 639-1 format. Short form of the locale, e.g. "fr". + * @param string $countryCode (optional) Country code, in ISO 3166-1 alpha-2 format, e.g. "FR" + * @param string $encoding (optional) Encoding of the final locale + * @return string + * + * Example: + * - language code: fr + * - country code: '' + * - result: fr_FR + */ + public static function getLongForm($languageCode, $countryCode = '', $encoding = 'UTF-8') + { + if (is_string($languageCode)) { + $languageCode = trim($languageCode); + } + + /* + * Language code not provided? + * Nothing to do + */ + if (empty($languageCode)) { + return ''; + } + + /* + * Country code not provided? + * Let's use language code + */ + if (empty($countryCode)) { + $countryCode = $languageCode; + } + + if (!empty($encoding)) { + $encoding = sprintf('.%s', $encoding); + } + + return sprintf('%s_%s%s', $languageCode, strtoupper($countryCode), $encoding); + } +} diff --git a/src/Meritoo/Common/Utilities/MimeTypes.php b/src/Meritoo/Common/Utilities/MimeTypes.php new file mode 100644 index 0000000..3c4881c --- /dev/null +++ b/src/Meritoo/Common/Utilities/MimeTypes.php @@ -0,0 +1,816 @@ + + * @copyright Meritoo.pl + */ +class MimeTypes +{ + /** + * Mime types data + * + * @var array + */ + private static $mimeTypes = [ + '7z' => 'application/x-7z-compressed', + 'ez' => 'application/andrew-inset', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'ccxml' => 'application/ccxml+xml', + 'davmount' => 'application/davmount+xml', + 'ecma' => 'application/ecmascript', + 'pfr' => 'application/font-tdpfr', + 'stk' => 'application/hyperstudio', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'mrc' => 'application/marc', + 'ma' => 'application/mathematica', + 'nb' => 'application/mathematica', + 'mb' => 'application/mathematica', + 'mathml' => 'application/mathml+xml', + 'mbox' => 'application/mbox', + 'mscml' => 'application/mediaservercontrol+xml', + 'mp4s' => 'application/mp4', + 'dot' => 'application/msword', + 'doc' => 'application/msword', + /* + * MS Office system file format MIME types + * http://technet.microsoft.com/en-us/library/ee309278%28office.12%29.aspx + */ + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'mxf' => 'application/mxf', + 'bin' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'class' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'iso' => 'application/octet-stream', + 'dmg' => 'application/octet-stream', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'pkg' => 'application/octet-stream', + 'bpk' => 'application/octet-stream', + 'dump' => 'application/octet-stream', + 'elc' => 'application/octet-stream', + 'scpt' => 'application/octet-stream', + 'oda' => 'application/oda', + 'ogg' => 'application/ogg', + 'pdf' => 'application/pdf', + 'pgp' => 'application/pgp-encrypted', + 'asc' => 'application/pgp-signature', + 'sig' => 'application/pgp-signature', + 'prf' => 'application/pics-rules', + 'p10' => 'application/pkcs10', + 'p7m' => 'application/pkcs7-mime', + 'p7c' => 'application/pkcs7-mime', + 'p7s' => 'application/pkcs7-signature', + 'cer' => 'application/pkix-cert', + 'crl' => 'application/pkix-crl', + 'pkipath' => 'application/pkix-pkipath', + 'pki' => 'application/pkixcmp', + 'pls' => 'application/pls+xml', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'cww' => 'application/prs.cww', + 'rdf' => 'application/rdf+xml', + 'rif' => 'application/reginfo+xml', + 'rnc' => 'application/relax-ng-compact-syntax', + 'rl' => 'application/resource-lists+xml', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'sbml' => 'application/sbml+xml', + 'sdp' => 'application/sdp', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'shf' => 'application/shf+xml', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'gram' => 'application/srgs', + 'grxml' => 'application/srgs+xml', + 'ssml' => 'application/ssml+xml', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'aso' => 'application/vnd.accpac.simply.aso', + 'imp' => 'application/vnd.accpac.simply.imp', + 'acu' => 'application/vnd.acucobol', + 'atc' => 'application/vnd.acucorp', + 'acutc' => 'application/vnd.acucorp', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'ami' => 'application/vnd.amiga.ami', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'atx' => 'application/vnd.antix.game-component', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'aep' => 'application/vnd.audiograph', + 'mpm' => 'application/vnd.blueice.multipass', + 'bmi' => 'application/vnd.bmi', + 'rep' => 'application/vnd.businessobjects', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'cdy' => 'application/vnd.cinderella', + 'cla' => 'application/vnd.claymore', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'csp' => 'application/vnd.commonspace', + 'cst' => 'application/vnd.commonspace', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cmc' => 'application/vnd.cosmocaller', + 'clkx' => 'application/vnd.crick.clicker', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'pml' => 'application/vnd.ctc-posml', + 'ppd' => 'application/vnd.cups-ppd', + 'curl' => 'application/vnd.curl', + 'rdz' => 'application/vnd.data-vision.rdz', + 'dna' => 'application/vnd.dna', + 'mlp' => 'application/vnd.dolby.mlp', + 'dpg' => 'application/vnd.dpgraph', + 'dfac' => 'application/vnd.dreamfactory', + 'mag' => 'application/vnd.ecowin.chart', + 'nml' => 'application/vnd.enliven', + 'esf' => 'application/vnd.epson.esf', + 'msf' => 'application/vnd.epson.msf', + 'qam' => 'application/vnd.epson.quickanime', + 'slt' => 'application/vnd.epson.salt', + 'ssf' => 'application/vnd.epson.ssf', + 'es3' => 'application/vnd.eszigno3+xml', + 'et3' => 'application/vnd.eszigno3+xml', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'fdf' => 'application/vnd.fdf', + 'gph' => 'application/vnd.flographit', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fm' => 'application/vnd.framemaker', + 'frame' => 'application/vnd.framemaker', + 'maker' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'ltf' => 'application/vnd.frogans.ltf', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'oas' => 'application/vnd.fujitsu.oasys', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'fzs' => 'application/vnd.fuzzysheet', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gac' => 'application/vnd.groove-account', + 'ghf' => 'application/vnd.groove-help', + 'gim' => 'application/vnd.groove-identity-message', + 'grv' => 'application/vnd.groove-injector', + 'gtm' => 'application/vnd.groove-tool-message', + 'tpl' => 'application/vnd.groove-tool-template', + 'vcg' => 'application/vnd.groove-vcard', + 'zmm' => 'application/vnd.handheld-entertainment+xml', + 'hbci' => 'application/vnd.hbci', + 'les' => 'application/vnd.hhe.lesson-player', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'jlt' => 'application/vnd.hp-jlyt', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'x3d' => 'application/vnd.hzn-3d-crossword', + 'mpy' => 'application/vnd.ibm.minipay', + 'afp' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'list3820' => 'application/vnd.ibm.modcap', + 'irm' => 'application/vnd.ibm.rights-management', + 'sc' => 'application/vnd.ibm.secure-container', + 'igl' => 'application/vnd.igloader', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'irp' => 'application/vnd.irepository.package+xml', + 'xpr' => 'application/vnd.is-xpr', + 'jam' => 'application/vnd.jam', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'jisp' => 'application/vnd.jisp', + 'ktz' => 'application/vnd.kahootz', + 'ktr' => 'application/vnd.kahootz', + 'karbon' => 'application/vnd.kde.karbon', + 'chrt' => 'application/vnd.kde.kchart', + 'kfo' => 'application/vnd.kde.kformula', + 'flw' => 'application/vnd.kde.kivio', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'ksp' => 'application/vnd.kde.kspread', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'htke' => 'application/vnd.kenameaapp', + 'kia' => 'application/vnd.kidspiration', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'skp' => 'application/vnd.koan', + 'skd' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + '123' => 'application/vnd.lotus-1-2-3', + 'apr' => 'application/vnd.lotus-approach', + 'pre' => 'application/vnd.lotus-freelance', + 'nsf' => 'application/vnd.lotus-notes', + 'org' => 'application/vnd.lotus-organizer', + 'scm' => 'application/vnd.lotus-screencam', + 'lwp' => 'application/vnd.lotus-wordpro', + 'portpkg' => 'application/vnd.macports.portpkg', + 'mcd' => 'application/vnd.mcd', + 'mc1' => 'application/vnd.medcalcdata', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'mwf' => 'application/vnd.mfer', + 'mfm' => 'application/vnd.mfmp', + 'flo' => 'application/vnd.micrografx.flo', + 'igx' => 'application/vnd.micrografx.igx', + 'mif' => 'application/vnd.mif', + 'daf' => 'application/vnd.mobius.daf', + 'dis' => 'application/vnd.mobius.dis', + 'mbk' => 'application/vnd.mobius.mbk', + 'mqy' => 'application/vnd.mobius.mqy', + 'msl' => 'application/vnd.mobius.msl', + 'plc' => 'application/vnd.mobius.plc', + 'txf' => 'application/vnd.mobius.txf', + 'mpn' => 'application/vnd.mophun.application', + 'mpc' => 'application/vnd.mophun.certificate', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'cil' => 'application/vnd.ms-artgalry', + 'asf' => 'application/vnd.ms-asf', + 'cab' => 'application/vnd.ms-cab-compressed', + 'xls' => 'application/vnd.ms-excel', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlm' => 'application/vnd.ms-excel', + 'xla' => 'application/vnd.ms-excel', + 'xlc' => 'application/vnd.ms-excel', + 'xlt' => 'application/vnd.ms-excel', + 'xlw' => 'application/vnd.ms-excel', + 'eot' => 'application/vnd.ms-fontobject', + 'chm' => 'application/vnd.ms-htmlhelp', + 'ims' => 'application/vnd.ms-ims', + 'lrm' => 'application/vnd.ms-lrm', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pps' => 'application/vnd.ms-powerpoint', + 'pot' => 'application/vnd.ms-powerpoint', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'wps' => 'application/vnd.ms-works', + 'wks' => 'application/vnd.ms-works', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'wpl' => 'application/vnd.ms-wpl', + 'xps' => 'application/vnd.ms-xpsdocument', + 'mseq' => 'application/vnd.mseq', + 'mus' => 'application/vnd.musician', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'ext' => 'application/vnd.novadigm.ext', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'otf' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'otm' => 'application/vnd.oasis.opendocument.text-master', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'xo' => 'application/vnd.olpc-sugar', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'dp' => 'application/vnd.osgi.dp', + 'prc' => 'application/vnd.palm', + 'pdb' => 'application/vnd.palm', + 'pqa' => 'application/vnd.palm', + 'oprc' => 'application/vnd.palm', + 'str' => 'application/vnd.pg.format', + 'ei6' => 'application/vnd.pg.osasli', + 'efif' => 'application/vnd.picsel', + 'plf' => 'application/vnd.pocketlearn', + 'pbd' => 'application/vnd.powerbuilder6', + 'box' => 'application/vnd.previewsystems.box', + 'mgz' => 'application/vnd.proteus.magazine', + 'qps' => 'application/vnd.publishare-delta-tree', + 'ptid' => 'application/vnd.pvi.ptid1', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'mxl' => 'application/vnd.recordare.musicxml', + 'rm' => 'application/vnd.rn-realmedia', + 'see' => 'application/vnd.seemail', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'iif' => 'application/vnd.shana.informed.interchange', + 'ipk' => 'application/vnd.shana.informed.package', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'mmf' => 'application/vnd.smaf', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'dxp' => 'application/vnd.spotfire.dxp', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'svd' => 'application/vnd.svd', + 'xsm' => 'application/vnd.syncml+xml', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tmo' => 'application/vnd.tmobile-livetv', + 'tpt' => 'application/vnd.trid.tpt', + 'mxs' => 'application/vnd.triscape.mxs', + 'tra' => 'application/vnd.trueapp', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'utz' => 'application/vnd.uiq.theme', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'vcx' => 'application/vnd.vcx', + 'vsd' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vss' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vis' => 'application/vnd.visionary', + 'vsf' => 'application/vnd.vsf', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wtb' => 'application/vnd.webturbo', + 'wpd' => 'application/vnd.wordperfect', + 'wqd' => 'application/vnd.wqd', + 'stf' => 'application/vnd.wt.stf', + 'xar' => 'application/vnd.xara', + 'xfdl' => 'application/vnd.xfdl', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'vxml' => 'application/voicexml+xml', + 'hlp' => 'application/winhlp', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'ace' => 'application/x-ace-compressed', + 'bcpio' => 'application/x-bcpio', + 'torrent' => 'application/x-bittorrent', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'boz' => 'application/x-bzip2', + 'vcd' => 'application/x-cdlink', + 'chat' => 'application/x-chat', + 'pgn' => 'application/x-chess-pgn', + 'cpio' => 'application/x-cpio', + 'csh' => 'application/x-csh', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'fgd' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'spl' => 'application/x-futuresplash', + 'gtar' => 'application/x-gtar', + 'hdf' => 'application/x-hdf', + 'jnlp' => 'application/x-java-jnlp-file', + 'latex' => 'application/x-latex', + 'wmd' => 'application/x-ms-wmd', + 'wmz' => 'application/x-ms-wmz', + 'mdb' => 'application/x-msaccess', + 'obd' => 'application/x-msbinder', + 'crd' => 'application/x-mscardfile', + 'clp' => 'application/x-msclip', + 'exe' => 'application/x-msdownload', + 'dll' => 'application/x-msdownload', + 'com' => 'application/x-msdownload', + 'bat' => 'application/x-msdownload', + 'msi' => 'application/x-msdownload', + 'mvb' => 'application/x-msmediaview', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'wmf' => 'application/x-msmetafile', + 'mny' => 'application/x-msmoney', + 'pub' => 'application/x-mspublisher', + 'scd' => 'application/x-msschedule', + 'trm' => 'application/x-msterminal', + 'wri' => 'application/x-mswrite', + 'nc' => 'application/x-netcdf', + 'cdf' => 'application/x-netcdf', + 'p12' => 'application/x-pkcs12', + 'pfx' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'spc' => 'application/x-pkcs7-certificates', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'rar' => 'application/x-rar-compressed', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'tar' => 'application/x-tar', + 'tcl' => 'application/x-tcl', + 'tex' => 'application/x-tex', + 'texinfo' => 'application/x-texinfo', + 'texi' => 'application/x-texinfo', + 'ustar' => 'application/x-ustar', + 'src' => 'application/x-wais-source', + 'der' => 'application/x-x509-ca-cert', + 'crt' => 'application/x-x509-ca-cert', + 'xenc' => 'application/xenc+xml', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'xml' => 'application/xml', + 'xsl' => 'application/xml', + 'dtd' => 'application/xml-dtd', + 'xop' => 'application/xop+xml', + 'xslt' => 'application/xslt+xml', + 'xspf' => 'application/xspf+xml', + 'mxml' => 'application/xv+xml', + 'xhvml' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xvm' => 'application/xv+xml', + 'zip' => 'application/zip', + 'au' => 'audio/basic', + 'snd' => 'audio/basic', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'kar' => 'audio/midi', + 'rmi' => 'audio/midi', + 'mp4a' => 'audio/mp4', + 'm4a' => 'audio/mp4a-latm', + 'm4p' => 'audio/mp4a-latm', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'm2a' => 'audio/mpeg', + 'm3a' => 'audio/mpeg', + 'eol' => 'audio/vnd.digital-winds', + 'lvp' => 'audio/vnd.lucent.voice', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'wav' => 'audio/wav', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'm3u' => 'audio/x-mpegurl', + 'wax' => 'audio/x-ms-wax', + 'wma' => 'audio/x-ms-wma', + 'ram' => 'audio/x-pn-realaudio', + 'ra' => 'audio/x-pn-realaudio', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'cdx' => 'chemical/x-cdx', + 'cif' => 'chemical/x-cif', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'csml' => 'chemical/x-csml', + 'xyz' => 'chemical/x-xyz', + 'bmp' => 'image/bmp', + 'cgm' => 'image/cgm', + 'g3' => 'image/g3fax', + 'gif' => 'image/gif', + 'ief' => 'image/ief', + 'jp2' => 'image/jp2', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'pict' => 'image/pict', + 'pic' => 'image/pict', + 'pct' => 'image/pict', + 'png' => 'image/png', + 'btif' => 'image/prs.btif', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'psd' => 'image/vnd.adobe.photoshop', + 'djvu' => 'image/vnd.djvu', + 'djv' => 'image/vnd.djvu', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'fbs' => 'image/vnd.fastbidsheet', + 'fpx' => 'image/vnd.fpx', + 'fst' => 'image/vnd.fst', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'ico' => 'image/vnd.microsoft.icon', + 'mdi' => 'image/vnd.ms-modi', + 'npx' => 'image/vnd.net-fpx', + 'wbmp' => 'image/vnd.wap.wbmp', + 'xif' => 'image/vnd.xiff', + 'ras' => 'image/x-cmu-raster', + 'cmx' => 'image/x-cmx', + 'pntg' => 'image/x-macpaint', + 'pnt' => 'image/x-macpaint', + 'mac' => 'image/x-macpaint', + 'pcx' => 'image/x-pcx', + 'pnm' => 'image/x-portable-anymap', + 'pbm' => 'image/x-portable-bitmap', + 'pgm' => 'image/x-portable-graymap', + 'ppm' => 'image/x-portable-pixmap', + 'qtif' => 'image/x-quicktime', + 'qti' => 'image/x-quicktime', + 'rgb' => 'image/x-rgb', + 'xbm' => 'image/x-xbitmap', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'eml' => 'message/rfc822', + 'mime' => 'message/rfc822', + 'igs' => 'model/iges', + 'iges' => 'model/iges', + 'msh' => 'model/mesh', + 'mesh' => 'model/mesh', + 'silo' => 'model/mesh', + 'dwf' => 'model/vnd.dwf', + 'gdl' => 'model/vnd.gdl', + 'gtw' => 'model/vnd.gtw', + 'mts' => 'model/vnd.mts', + 'vtu' => 'model/vnd.vtu', + 'wrl' => 'model/vrml', + 'vrml' => 'model/vrml', + 'ics' => 'text/calendar', + 'ifb' => 'text/calendar', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'html' => 'text/html', + 'htm' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'conf' => 'text/plain', + 'def' => 'text/plain', + 'list' => 'text/plain', + 'log' => 'text/plain', + 'in' => 'text/plain', + 'dsc' => 'text/prs.lines.tag', + 'rtx' => 'text/richtext', + 'sgml' => 'text/sgml', + 'sgm' => 'text/sgml', + 'tsv' => 'text/tab-separated-values', + 't' => 'text/troff', + 'tr' => 'text/troff', + 'roff' => 'text/troff', + 'man' => 'text/troff', + 'me' => 'text/troff', + 'ms' => 'text/troff', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'fly' => 'text/vnd.fly', + 'flx' => 'text/vnd.fmi.flexstor', + '3dml' => 'text/vnd.in3d.3dml', + 'spot' => 'text/vnd.in3d.spot', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'wml' => 'text/vnd.wap.wml', + 'wmls' => 'text/vnd.wap.wmlscript', + 's' => 'text/x-asm', + 'asm' => 'text/x-asm', + 'c' => 'text/x-c', + 'cc' => 'text/x-c', + 'cxx' => 'text/x-c', + 'cpp' => 'text/x-c', + 'h' => 'text/x-c', + 'hh' => 'text/x-c', + 'dic' => 'text/x-c', + 'f' => 'text/x-fortran', + 'for' => 'text/x-fortran', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'p' => 'text/x-pascal', + 'pas' => 'text/x-pascal', + 'java' => 'text/x-java-source', + 'etx' => 'text/x-setext', + 'uu' => 'text/x-uuencode', + 'vcs' => 'text/x-vcalendar', + 'vcf' => 'text/x-vcard', + '3gp' => 'video/3gpp', + '3g2' => 'video/3gpp2', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'jpgm' => 'video/jpm', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mp4' => 'video/mp4', + 'mp4v' => 'video/mp4', + 'mpg4' => 'video/mp4', + 'm4v' => 'video/mp4', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'm1v' => 'video/mpeg', + 'm2v' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'fvt' => 'video/vnd.fvt', + 'mxu' => 'video/vnd.mpegurl', + 'm4u' => 'video/vnd.mpegurl', + 'viv' => 'video/vnd.vivo', + 'dv' => 'video/x-dv', + 'dif' => 'video/x-dv', + 'fli' => 'video/x-fli', + 'asx' => 'video/x-ms-asf', + 'wm' => 'video/x-ms-wm', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wvx' => 'video/x-ms-wvx', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'ice' => 'x-conference/x-cooltalk', + ]; + + /** + * Returns extensions for given mimes types + * + * @param array $mimesTypes The mimes types, e.g. ['video/mpeg', 'image/jpeg'] + * @param bool $asUpperCase (optional) If is set to true, extensions are returned as upper case. Otherwise - lower + * case. + * @return array + */ + public static function getExtensions(array $mimesTypes, $asUpperCase = false) + { + if (empty($mimesTypes)) { + return []; + } + + $extensions = []; + + foreach ($mimesTypes as $mimeType) { + $extension = self::getExtension($mimeType); + + /* + * No extension for given mime type? + * Nothing to do + */ + if (empty($extension)) { + continue; + } + + /* + * Extensions should be returned as upper case? + */ + if ($asUpperCase) { + if (is_array($extension)) { + array_walk($extension, function (&$value) { + $value = strtoupper($value); + }); + } else { + $extension = strtoupper($extension); + } + } + + $extensions[$mimeType] = $extension; + } + + return $extensions; + } + + /** + * Returns extension for given mime type + * + * @param string $mimeType The mime type, e.g. "video/mpeg" + * @return string|array + */ + public static function getExtension($mimeType) + { + if (is_string($mimeType) && in_array($mimeType, self::$mimeTypes)) { + $data = Arrays::setKeysAsValues(self::$mimeTypes, false); + + return $data[$mimeType]; + } + + return ''; + } + + /** + * Returns information whether file with the given path is an image + * + * @param string $path Path of the file to check + * @return bool + */ + public static function isImagePath($path) + { + $mimeType = self::getMimeType($path); + + return self::isImage($mimeType); + } + + /** + * Returns mime type of given file + * + * @param string $filePath Path of the file to check + * @return string + * + * @throws \RuntimeException + */ + public static function getMimeType($filePath) + { + /* + * The file does not exist? + * Nothing to do + */ + if (!is_string($filePath) || !is_readable($filePath)) { + return ''; + } + + /* + * 1st possibility: the finfo class + */ + if (class_exists('finfo')) { + $finfo = new \finfo(); + + return $finfo->file($filePath, FILEINFO_MIME_TYPE); + } + + /* + * 2nd possibility: the mime_content_type function + */ + if (function_exists('mime_content_type')) { + return mime_content_type($filePath); + } + + /* + * Oops, there is no possibility to read the mime type + */ + $template = 'Neither \'finfo\' class nor \'mime_content_type\' function exists. There is no way to read the' + . ' mime type of file \'%s\'.'; + + $message = sprintf($template, $filePath); + throw new \RuntimeException($message); + } + + /** + * Returns information whether the given file type is an image + * + * @param string $mimeType The mime type of file + * @return bool + */ + public static function isImage($mimeType) + { + if (in_array($mimeType, self::$mimeTypes)) { + return (bool)preg_match('|^image/.+$|', $mimeType); + } + + return false; + } +} diff --git a/src/Meritoo/Common/Utilities/Miscellaneous.php b/src/Meritoo/Common/Utilities/Miscellaneous.php new file mode 100644 index 0000000..015684d --- /dev/null +++ b/src/Meritoo/Common/Utilities/Miscellaneous.php @@ -0,0 +1,1474 @@ + + * @copyright Meritoo.pl + */ +class Miscellaneous +{ + /** + * Returns directory's content (names of directories and files) + * + * @param string $directoryPath Path of directory who content should be returned + * @param bool $recursive (optional) If is set to true, sub-directories are also searched for content. + * Otherwise - only content of given directory is returned. + * @param int $maxFilesCount (optional) Maximum files that will be returned. If it's null, all files are + * returned. + * @return array|null + */ + public static function getDirectoryContent($directoryPath, $recursive = false, $maxFilesCount = null) + { + /* + * Path of directory is unknown or does not exist and is not readable? + * Nothing to do + */ + if (empty($directoryPath) || !is_readable($directoryPath)) { + return null; + } + + $files = []; + $startFileName = ''; + + if (self::isFilePath($directoryPath)) { + $startDirectoryPath = dirname($directoryPath); + $startFileName = str_replace($startDirectoryPath, '', $directoryPath); + + $directoryPath = $startDirectoryPath; + } + + $count = 0; + $startFileFound = false; + + if (!Regex::endsWith($directoryPath, '/')) { + $directoryPath .= '/'; + } + + if (Regex::startsWith($startFileName, '/')) { + $startFileName = mb_substr($startFileName, 1); + } + + $directoryContent = scandir($directoryPath); + + if (!empty($directoryContent)) { + foreach ($directoryContent as $fileName) { + if ($fileName != '.' && $fileName != '..') { + $content = null; + + if (!empty($startFileName) && !$startFileFound) { + if ($fileName == $startFileName) { + $startFileFound = true; + } + + continue; + } + + if ($recursive && is_dir($directoryPath . $fileName)) { + $content = self::getDirectoryContent($directoryPath . $fileName, true, $maxFilesCount - $count); + } + + if ($content !== null) { + $files[$fileName] = $content; + + if (!empty($maxFilesCount)) { + $count += Arrays::getNonArrayElementsCount($content); + } + } else { + $files[] = $fileName; + + if (!empty($maxFilesCount)) { + ++$count; + } + } + + if (!empty($maxFilesCount) && $count >= $maxFilesCount) { + break; + } + } + } + } + + return $files; + } + + /** + * Returns information if given path it's a file's path, if the path contains file name + * + * @param string $path The path to check + * @return bool + */ + public static function isFilePath($path) + { + $info = pathinfo($path); + + return isset($info['extension']) && !empty($info['extension']); + } + + /** + * Converts checkbox value to boolean + * + * @param string $checkboxValue Checkbox value + * @return bool + */ + public static function checkboxValue2Boolean($checkboxValue) + { + $mapping = [ + 'on' => true, + 'off' => false, + ]; + + $clearValue = strtolower(trim($checkboxValue)); + + if (isset($mapping[$clearValue])) { + return $mapping[$clearValue]; + } + + return false; + } + + /** + * Converts checkbox value to integer + * + * @param string $checkboxValue Checkbox value + * @return int + */ + public static function checkboxValue2Integer($checkboxValue) + { + return (int)self::checkboxValue2Boolean($checkboxValue); + } + + /** + * Returns name of file with given extension after verification if it contains the extension + * + * @param string $fileName The file name to verify + * @param string $extension The extension to verify and include + * @return string + */ + public static function includeFileExtension($fileName, $extension) + { + if (self::getFileExtension($fileName, true) != strtolower($extension)) { + return sprintf('%s.%s', $fileName, $extension); + } + + return $fileName; + } + + /** + * Returns file extension + * + * @param string $fileName File name + * @param bool $asLowerCase (optional) if true extension is returned as lowercase string + * @return string + */ + public static function getFileExtension($fileName, $asLowerCase = false) + { + $extension = ''; + $matches = []; + + if (preg_match('|(.+)\.(.+)|', $fileName, $matches)) { + $extension = end($matches); + } + + if ($asLowerCase) { + return strtolower($extension); + } + + return $extension; + } + + /** + * Returns file name from given path + * + * @param string $path A path that contains file name + * @return string + */ + public static function getFileNameFromPath($path) + { + $matches = []; + $pattern = sprintf('|([^\%s.]+\.[A-Za-z0-9.]+)$|', DIRECTORY_SEPARATOR); + + if ((bool)preg_match($pattern, $path, $matches)) { + return $matches[1]; + } + + return ''; + } + + /** + * Returns unique name for file based on given original name + * + * @param string $originalFileName Original name of the file + * @param int $objectId (optional) Object ID, the ID of database's row. May be included into the + * generated / unique name. + * @return string + */ + public static function getUniqueFileName($originalFileName, $objectId = 0) + { + /* + * Get parts of the file name: + * - without extension + * - and... the extension + */ + $withoutExtension = self::getFileNameWithoutExtension($originalFileName); + $extension = self::getFileExtension($originalFileName, true); + + /* + * Let's clear name of file + * + * Attention. The name without extension may be cleared / urlized only + * to avoid incorrect name by replacing "." with "-". + */ + $withoutExtension = Urlizer::urlize($withoutExtension); + + /* + * Now I have to complete the template used to build / generate unique name + */ + $template = '%s-%s'; // file's name and unique key + + if ($objectId > 0) { + $template .= '-%s'; // object ID + } + + $template .= '.%s'; // file's extension + + /* + * Add some uniqueness + */ + $unique = uniqid(mt_rand(), true); + + /* + * Finally build and return the unique name + */ + if ($objectId > 0) { + return sprintf($template, $withoutExtension, $unique, $objectId, $extension); + } + + return sprintf($template, $withoutExtension, $unique, $extension); + } + + /** + * Returns file name without extension + * + * @param string $fileName The file name + * @return string + */ + public static function getFileNameWithoutExtension($fileName) + { + $matches = []; + + if (is_string($fileName) && (bool)preg_match('|(.+)\.(.+)|', $fileName, $matches)) { + return $matches[1]; + } + + return ''; + } + + /** + * Converts value to non-negative integer (element of the set {0, 1, 2, 3, ...}) + * + * @param mixed $value Value to convert + * @param int $negativeReplacement (optional) Replacement for negative value + * @return int + */ + public static function value2NonNegativeInteger($value, $negativeReplacement = 0) + { + $effect = (int)$value; + + if ($effect < 0) { + return $negativeReplacement; + } + + return $effect; + } + + /** + * Displays variable content as preformatted text (fixed-width font and preserves both spaces and line breaks) + * + * If xdebug php module is loaded, displays variable using var_dump(), otherwise
var_dump()
. + * You can pass as many variables as you wish. + * + * Pass each variable as argument of this function. Amount unlimited. Variables are loaded using the + * func_get_args() function (@see http://pl1.php.net/manual/en/function.func-get-args.php). + */ + public static function variableDump() + { + $xdebugLoaded = self::isPhpModuleLoaded('xdebug'); + + if (!$xdebugLoaded) { + echo '
';
+        }
+
+        $arguments = func_get_args();
+
+        foreach ($arguments as $argument) {
+            var_dump($argument);
+        }
+
+        if (!$xdebugLoaded) {
+            echo '
'; + } + } + + /** + * Returns information if given PHP module is compiled and loaded + * + * @param string $phpModuleName PHP module name + * @return bool + */ + public static function isPhpModuleLoaded($phpModuleName) + { + $phpModulesArray = get_loaded_extensions(); + + return in_array($phpModuleName, $phpModulesArray); + } + + /** + * Converts given string characters to latin characters + * + * @param string $string String to convert + * @param bool $lowerCaseHuman (optional) If is set to true, converted string is returned as lowercase and + * human-readable. Otherwise - as original. + * @param string $replacementChar (optional) Replacement character for all non-latin characters and uppercase + * letters, if 2nd argument is set to true + * @return string + */ + public static function toLatin($string, $lowerCaseHuman = true, $replacementChar = '-') + { + if (is_string($string)) { + $string = trim($string); + } + + /* + * Empty value? + * Nothing to do + */ + if (empty($string)) { + return ''; + } + + $converter = Transliterator::create('Latin-ASCII;'); + + /* + * Oops, cannot instantiate converter + * Nothing to do + */ + if ($converter === null) { + return ''; + } + + $converted = $converter->transliterate($string); + + /* + * Make the string lowercase and human-readable + */ + if ($lowerCaseHuman) { + $matches = []; + $matchCount = preg_match_all('|[A-Z]{1}[^A-Z]*|', $converted, $matches); + + if ($matchCount > 0) { + $parts = $matches[0]; + $converted = mb_strtolower(implode($replacementChar, $parts)); + } + } + + /* + * Let's replace special characters to spaces + * ...and finally spaces to $replacementChar + */ + $replaced = preg_replace('|[^a-zA-Z0-9]|', ' ', $converted); + + return preg_replace('| +|', $replacementChar, trim($replaced)); + } + + /** + * Returns unique string + * + * @param string $prefix (optional) Prefix of the unique string. May be used while generating the unique + * string simultaneously on several hosts at the same microsecond. + * @param bool $hashed (optional) If is set to true, the unique string is hashed additionally. Otherwise - not. + * @return string + */ + public static function getUniqueString($prefix = '', $hashed = false) + { + $unique = uniqid($prefix, true); + + if ($hashed) { + return sha1($unique); + } + + return $unique; + } + + /** + * Replaces part of string with other string or strings. + * There is a few combination of what should be searched and with what it should be replaced. + * + * @param string|array $subject The string or an array of strings to search and replace + * @param string|array $search String or pattern or array of patterns to find. It may be: string, an array + * of strings or an array of patterns. + * @param string|array $replacement The string or an array of strings to replace. It may be: string or an array + * of strings. + * @param bool $quoteStrings (optional) If is set to true, strings are surrounded with single quote sign + * @return string + * + * Example: + * a) an array of strings to search + * $subject = [ + * 'Lorem ipsum dolor sit amet.', + * 'Etiam ullamcorper. Suspendisse a pellentesque dui, non felis.', + * ]; + * + * b) an array of patterns + * $search = [ + * '|ipsum|', + * '|pellentesque|', + * ]; + * + * c) an array of strings to replace + * $replacement = [ + * 'commodo', + * 'interdum', + * ]; + * + * The result: + * [ + * 'Lorem commodo dolor sit amet.', + * 'Etiam ullamcorper. Suspendisse a interdum dui, non felis.', + * ]; + */ + public static function replace($subject, $search, $replacement, $quoteStrings = false) + { + $effect = $subject; + + $searchIsString = is_string($search); + $searchIsArray = is_array($search); + + /* + * Value to find is neither a string nor an array OR it's an empty string? + * Nothing to do + */ + if ((!$searchIsString && !$searchIsArray) || ($searchIsString && strlen($search) == 0)) { + return $effect; + } + + $replacementIsString = is_string($replacement); + $replacementIsArray = is_array($replacement); + + $bothAreStrings = $searchIsString && $replacementIsString; + $bothAreArrays = $searchIsArray && $replacementIsArray; + + /* + * First step: replace strings, simple operation with strings + */ + if ($searchIsString && $replacementIsString) { + if ($quoteStrings) { + $replacement = '\'' . $replacement . '\''; + } + + $effect = str_replace($search, $replacement, $subject); + } + + /* + * Second step: replace with regular expressions. + * Attention. Searched and replacement value should be the same type: strings or arrays. + */ + if ($effect == $subject && ($bothAreStrings || $bothAreArrays)) { + if ($quoteStrings && $replacementIsString) { + $replacement = '\'' . $replacement . '\''; + } + + /* + * I have to avoid string that contains spaces only, e.g. " ". + * It's required to avoid bug: preg_replace(): Empty regular expression. + */ + if ($searchIsArray || ($searchIsString && !empty(trim($search)))) { + $effect = preg_replace($search, $replacement, $subject); + } + } + + /* + * Third step: complex replace of the replacement defined as an array. + * It may be useful when you want to search for a one string and replace the string with multiple values. + */ + if ($effect == $subject && $searchIsString && $replacementIsArray) { + $subjectIsArray = is_array($subject); + $effect = ''; + + if ($subjectIsArray) { + $effect = []; + } + + /* + * I have to make the subject an array... + */ + $subject = Arrays::makeArray($subject); + + /* + * ...and use iterate through the subjects, + * because explode() function expects strings as both arguments (1st and 2nd) + */ + foreach ($subject as $subSubject) { + $subEffect = ''; + + $exploded = explode($search, $subSubject); + $explodedCount = count($exploded); + + if ($quoteStrings) { + foreach ($replacement as &$item) { + if (is_string($item)) { + $item = '\'' . $item . '\''; + } + } + + unset($item); + } + + foreach ($exploded as $key => $item) { + $subEffect .= $item; + + /* + * The replacement shouldn't be included when the searched string was not found + */ + if ($explodedCount > 1 && $key < $explodedCount - 1 && isset($replacement[$key])) { + $subEffect .= $replacement[$key]; + } + } + + if ($subjectIsArray) { + $effect[] = $subEffect; + continue; + } + + $effect .= $subEffect; + } + } + + return $effect; + } + + /** + * Returns new file name after adding prefix or suffix (or both of them) to the name + * + * @param string $fileName The file name + * @param string $prefix File name prefix + * @param string $suffix File name suffix + * @return string + */ + public static function getNewFileName($fileName, $prefix, $suffix) + { + $effect = $fileName; + + if (!empty($fileName) && (!empty($prefix) || !empty($suffix))) { + $name = self::getFileNameWithoutExtension($fileName); + $extension = self::getFileExtension($fileName); + + $effect = sprintf('%s%s%s.%s', $prefix, $name, $suffix, $extension); + } + + return $effect; + } + + /** + * Returns operating system name PHP is running on + * + * @return string + */ + public static function getOperatingSystemNameServer() + { + return php_uname('s'); + } + + /** + * Returns part of string preserving words + * + * @param string $text The string / text + * @param int $maxLength Maximum length of given string + * @param string $suffix (optional) The suffix to add at the end of string + * @return string + */ + public static function substringToWord($text, $maxLength, $suffix = '...') + { + $effect = $text; + + $textLength = mb_strlen($text, 'utf-8'); + $suffixLength = mb_strlen($suffix, 'utf-8'); + + $maxLength -= $suffixLength; + + if ($textLength > $maxLength) { + $effect = mb_substr($text, 0, $maxLength, 'utf-8'); + $lastSpacePosition = mb_strrpos($effect, ' ', 'utf-8'); + + if ($lastSpacePosition !== false) { + $effect = mb_substr($effect, 0, $lastSpacePosition, 'utf-8'); + } + + $effect .= $suffix; + } + + return $effect; + } + + /** + * Breaks long text + * + * @param string $text The text to check and break + * @param int $perLine (optional) Characters count per line + * @param string $separator (optional) Separator that is placed beetwen lines + * @param string $encoding (optional) Character encoding. Used by mb_substr(). + * @param int $proportionalAberration (optional) Proportional aberration for chars (percent value) + * @return string + */ + public static function breakLongText( + $text, + $perLine = 100, + $separator = '
', + $encoding = 'utf-8', + $proportionalAberration = 20 + ) { + $effect = $text; + $textLength = mb_strlen($text); + + if (!empty($text) && $textLength > $perLine) { + /* + * The html_entity_decode() function is used here, because while operating + * on string that contains only special characters the string is divided + * incorrectly, e.g. "<<<<<" -> "<<<<&
lt;". + */ + //$text = htmlspecialchars_decode($text); + $text = html_entity_decode($text, ENT_QUOTES); + + $effect = ''; + $currentPosition = 0; + + $charsAberration = ceil($perLine * ($proportionalAberration / 100)); + $charsPerLineDefault = $perLine; + + while ($currentPosition <= $textLength) { + $insertSeparator = false; + + /* + * Looking for spaces before and after current position. It was done, because text wasn't + * broken properly and some words were breaked and placed into two lines. + */ + if ($charsAberration > 0) { + $length = $perLine + $charsAberration; + $lineWithAberration = mb_substr($text, $currentPosition, $length, $encoding); + + if (!Regex::contains($lineWithAberration, ' ')) { + $length = $perLine - $charsAberration; + $lineWithAberration = mb_substr($text, $currentPosition, $length, $encoding); + } + + if (Regex::startsWith($lineWithAberration, ' ')) { + ++$currentPosition; + $lineWithAberration = ltrim($lineWithAberration); + } + + $spacePosition = mb_strrpos($lineWithAberration, ' ', 0, $encoding); + + if ($spacePosition > 0) { + $perLine = $spacePosition; + $insertSeparator = true; + } + } + + $charsOneLine = mb_substr($text, $currentPosition, $perLine, $encoding); + + /* + * The htmlspecialchars() function is used here, because... + * Reason and comment the same as above for html_entity_decode() function. + */ + + $effect .= htmlspecialchars($charsOneLine); + //$effect .= $charsOneLine; + + $currentPosition += $perLine; + $oneLineContainsSpace = Regex::contains($charsOneLine, ' '); + + if (($insertSeparator || !$oneLineContainsSpace) && $currentPosition <= $textLength) { + $effect .= $separator; + } + + $perLine = $charsPerLineDefault; + } + } + + return $effect; + } + + /** + * Removes the directory. + * If not empty, removes also contents. + * + * @param string $directoryPath Directory path + * @param bool $contentOnly (optional) If is set to true, only content of the directory is removed, not + * directory. Otherwise - directory is removed too. + * @return bool + */ + public static function removeDirectory($directoryPath, $contentOnly = false) + { + if (!file_exists($directoryPath)) { + return true; + } + + if (!is_dir($directoryPath)) { + return unlink($directoryPath); + } + + foreach (scandir($directoryPath) as $item) { + if ($item == '.' || $item == '..') { + continue; + } + + if (!self::removeDirectory($directoryPath . DIRECTORY_SEPARATOR . $item)) { + return false; + } + } + + if (!$contentOnly) { + return rmdir($directoryPath); + } + + return true; + } + + /** + * Returns information if value is decimal + * + * @param mixed $value The value to check + * @return bool + */ + public static function isDecimal($value) + { + return is_scalar($value) && floor($value) !== (float)$value; + } + + /** + * Returns the string in camel case + * + * @param string $string The string to convert e.g. this-is-eXamplE (return: thisIsExample) + * @param string $separator (optional) Separator used to find parts of the string, e.g. '-' or ',' + * @return string + */ + public static function getCamelCase($string, $separator = ' ') + { + if (empty($string)) { + return ''; + } + + $effect = ''; + $members = explode($separator, $string); + + foreach ($members as $key => $value) { + $value = mb_strtolower($value); + + if ($key == 0) { + $effect .= self::lowercaseFirst($value); + } else { + $effect .= self::uppercaseFirst($value); + } + } + + return $effect; + } + + /** + * Make a string's first character lowercase + * + * @param string $text The text to get first character lowercase + * @param bool|null $restLowercase (optional) Information that to do with rest of given string + * @return string + * + * Values of the $restLowercase argument: + * - null (default): nothing is done with the string + * - true: the rest of string is lowercased + * - false: the rest of string is uppercased + * + * Some explanation: + * Function lcfirst() is available for PHP >= 5.30, so I wrote my own function that lowercases first character of + * the string. + */ + public static function lowercaseFirst($text, $restLowercase = null) + { + if (empty($text)) { + return ''; + } + + $effect = $text; + + if ($restLowercase) { + $effect = mb_strtolower($effect); + } elseif ($restLowercase === false) { + $effect = mb_strtoupper($effect); + } + + if (function_exists('lcfirst')) { + $effect = lcfirst($effect); + } else { + $first = mb_strtolower($effect[0]); + $rest = mb_substr($effect, 1); + + $effect = $first . $rest; + } + + return $effect; + } + + /** + * Make a string's first character uppercase + * + * @param string $text The text to get uppercase + * @param bool|null $restLowercase (optional) Information that to do with rest of given string + * @return string + * + * Values of the $restLowercase argument: + * - null (default): nothing is done with the string + * - true: the rest of string is lowercased + * - false: the rest of string is uppercased + */ + public static function uppercaseFirst($text, $restLowercase = null) + { + if (empty($text)) { + return ''; + } + + $effect = $text; + + if ($restLowercase) { + $effect = mb_strtolower($effect); + } elseif ($restLowercase === false) { + $effect = mb_strtoupper($effect); + } + + if (function_exists('ucfirst')) { + $effect = ucfirst($effect); + } else { + $first = mb_strtoupper($effect[0]); + $rest = mb_substr($effect, 1); + + $effect = $first . $rest; + } + + return $effect; + } + + /** + * Quotes given value with apostrophes or quotation marks + * + * @param mixed $value The value to quote + * @param bool $useApostrophe (optional) If is set to true, apostrophes are used. Otherwise - quotation marks. + * @return string + */ + public static function quoteValue($value, $useApostrophe = true) + { + if (is_string($value)) { + $quotes = '"'; + + if ($useApostrophe) { + $quotes = '\''; + } + + $value = sprintf('%s%s%s', $quotes, $value, $quotes); + } + + return $value; + } + + /** + * Returns size (of file or directory) in human readable format + * + * @param int $sizeInBytes The size in bytes + * @return string + */ + public static function getHumanReadableSize($sizeInBytes) + { + $units = [ + 'B', + 'KB', + 'MB', + 'GB', + 'TB', + 'PB', + ]; + $index = floor(log($sizeInBytes, 1024)); + + $size = round($sizeInBytes / pow(1024, $index), 2); + $unit = $units[(int)$index]; + + return sprintf('%s %s', $size, $unit); + } + + /** + * Returns string without the last element. + * The string should contain given separator. + * + * @param string $string The string to check + * @param string $separator The separator which divides elements of string + * @return string + */ + public static function getStringWithoutLastElement($string, $separator) + { + $elements = self::getStringElements($string, $separator); + $lastKey = Arrays::getLastKey($elements); + + unset($elements[$lastKey]); + + return implode($separator, $elements); + } + + /** + * Returns elements of given string divided by given separator + * + * @param string $string The string to check + * @param string $separator The separator which divides elements of string + * @return array + */ + public static function getStringElements($string, $separator) + { + $matches = []; + $pattern = sprintf('|[^\%s]+|', $separator); + $matchCount = preg_match_all($pattern, $string, $matches); + + if ($matchCount > 1) { + return $matches[0]; + } + + return []; + } + + /** + * Returns the last element of given string divided by given separator + * + * @param string $string The string to check + * @param string $separator The separator which divides elements of string + * @return string|null + */ + public static function getLastElementOfString($string, $separator) + { + $elements = self::getStringElements($string, $separator); + + /* + * No elements? + * Nothing to do + */ + if (empty($elements)) { + return null; + } + + $element = Arrays::getLastElement($elements); + + return trim($element); + } + + /** + * Returns smartly trimmed string. + * If the string is empty, contains only spaces, e.g. " ", nothing is done and the original string is returned. + * + * @param string $string The string to trim + * @return string + */ + public static function trimSmart($string) + { + $trimmed = trim($string); + + if (empty($trimmed)) { + return $string; + } + + return $trimmed; + } + + /** + * Returns concatenated given paths + * + * The paths may be passed as: + * - an array of paths / strings + * - strings passed as following arguments + * + * Examples: + * - concatenatePaths(['path/first', 'path/second', 'path/third']); + * - concatenatePaths('path/first', 'path/second', 'path/third'); + * + * @param string|array $paths Paths co concatenate. As described above: an array of paths / strings or strings + * passed as following arguments. + * @return string + */ + public static function concatenatePaths($paths) + { + /* + * If paths are not provided as array, get the paths from methods' arguments + */ + if (!is_array($paths)) { + $paths = func_get_args(); + } + + /* + * No paths provided? + * Nothing to do + */ + if (empty($paths)) { + return ''; + } + + /* + * Some useful variables + */ + $concatenated = ''; + $firstWindowsBased = false; + $separator = DIRECTORY_SEPARATOR; + + foreach ($paths as $path) { + /* + * Empty paths are useless + */ + if (empty($path)) { + continue; + } + + /* + * Does the first path is a Windows-based path? + */ + if (Arrays::isFirstElement($paths, $path)) { + $firstWindowsBased = Regex::isWindowsBasedPath($path); + + if ($firstWindowsBased) { + $separator = '\\'; + } + } + + /* + * Remove the starting / beginning directory's separator + */ + $path = self::removeStartingDirectorySeparator($path, $separator); + + /* + * Removes the ending directory's separator + */ + $path = self::removeEndingDirectorySeparator($path, $separator); + + /* + * If OS is Windows, first part of the concatenated path should be the first passed path, + * because in Windows paths starts with drive letter, e.g. "C:", and the directory separator is not + * necessary at the beginning. + */ + if ($firstWindowsBased && empty($concatenated)) { + $concatenated = $path; + continue; + } + + /* + * Concatenate the paths / strings with OS-related directory separator between them (slash or backslash) + */ + $concatenated = sprintf('%s%s%s', $concatenated, $separator, $path); + } + + return $concatenated; + } + + /** + * Removes the starting / beginning directory's separator + * + * @param string $text Text that may contain a directory's separator at the start / beginning + * @param string $separator (optional) The directory's separator, e.g. "/". If is empty (not provided), separator + * provided by operating system will be used. + * @return string + */ + public static function removeStartingDirectorySeparator($text, $separator = '') + { + /* + * Not a string? + * Nothing to do + */ + if (!is_string($text)) { + return ''; + } + + if (empty($separator)) { + $separator = DIRECTORY_SEPARATOR; + } + + $effect = trim($text); + + if (Regex::startsWithDirectorySeparator($effect, $separator)) { + $effect = mb_substr($effect, mb_strlen($separator)); + } + + return $effect; + } + + /** + * Removes the ending directory's separator + * + * @param string $text Text that may contain a directory's separator at the end + * @param string $separator (optional) The directory's separator, e.g. "/". If is empty (not provided), system's + * separator is used. + * @return string + */ + public static function removeEndingDirectorySeparator($text, $separator = '') + { + /* + * Not a string? + * Nothing to do + */ + if (!is_string($text)) { + return ''; + } + + if (empty($separator)) { + $separator = DIRECTORY_SEPARATOR; + } + + $effect = trim($text); + + if (Regex::endsWithDirectorySeparator($effect, $separator)) { + $effect = mb_substr($effect, 0, mb_strlen($effect) - mb_strlen($separator)); + } + + return $effect; + } + + /** + * Returns safely value of global variable, found in one of the global arrays / variables, e.g. $_GET + * + * @param int $globalSourceType Represents the global array / variable. One of constants: INPUT_GET, INPUT_POST, + * INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV. + * @param string $variableName Name of the variable to return value + * @return mixed + */ + public static function getSafelyGlobalVariable($globalSourceType, $variableName) + { + $value = filter_input($globalSourceType, $variableName); + + if ($value === null) { + $globalSource = null; + + switch ($globalSourceType) { + case INPUT_GET: + $globalSource = $_GET; + break; + + case INPUT_POST: + $globalSource = $_POST; + break; + + case INPUT_COOKIE: + $globalSource = $_COOKIE; + break; + + case INPUT_SERVER: + $globalSource = $_SERVER; + break; + + case INPUT_ENV: + $globalSource = $_ENV; + break; + } + + if ($globalSource !== null && isset($globalSource[$variableName])) { + $value = $globalSource[$variableName]; + + if (!ini_get('magic_quotes_gpc')) { + $value = addslashes($value); + } + } + } + + return $value; + } + + /** + * Returns a CURL response with parsed HTTP headers as array with "headers", "cookies" and "content" keys + * + * The headers and cookies are parsed and returned as an array, and an array of Cookie objects. Returned array looks + * like this example: + * + * [ + * 'headers' => [ + * 'Content-Type' => 'text/html; charset=UTF-8', + * ... + * ], + * 'cookies' => [ + * new Symfony\Component\HttpFoundation\Cookie(), + * new Symfony\Component\HttpFoundation\Cookie(), + * ... + * ], + * 'content' => '...' + * ] + * + * + * If you want to attach HTTP headers into response content by CURL you need to set "CURLOPT_HEADER" option + * to "true". To read exact length of HTTP headers from CURL you can use "curl_getinfo()" function + * and read "CURLINFO_HEADER_SIZE" option. + * + * @param string $response the full content of response, including HTTP headers + * @param int $headerSize The length of HTTP headers in content + * @return array + */ + public static function getCurlResponseWithHeaders($response, $headerSize) + { + $headerContent = mb_substr($response, 0, $headerSize); + $content = mb_substr($response, $headerSize); + $headers = []; + $cookies = []; + + /* + * Let's transform headers content into two arrays: headers and cookies + */ + foreach (explode("\r\n", $headerContent) as $i => $line) { + /* + * First line is only HTTP status and is unneeded so skip it + */ + if ($i === 0) { + continue; + } + + if (Regex::contains($line, ':')) { + list($key, $value) = explode(': ', $line); + + /* + * If the header is a "set-cookie" let's save it to "cookies" array + */ + if ($key === 'Set-Cookie') { + $cookieParameters = explode(';', $value); + + $name = ''; + $value = ''; + $expire = 0; + $path = '/'; + $domain = null; + $secure = false; + $httpOnly = true; + + foreach ($cookieParameters as $j => $parameter) { + $param = explode('=', $parameter); + + /* + * First parameter will be always a cookie name and it's value. It is not needed to run + * further actions for them, so save the values and move to next parameter. + */ + if ($j === 0) { + $name = trim($param[0]); + $value = trim($param[1]); + continue; + } + + /* + * Now there would be the rest of cookie parameters, names of params are sent different way so + * I need to lowercase the names and remove unneeded spaces. + */ + $paramName = mb_strtolower(trim($param[0])); + $paramValue = true; + + /* + * Some parameters don't have value e.g. "secure", but the value for them if they're specified + * is "true". Otherwise - just read a value for parameter if exists. + */ + if (array_key_exists(1, $param)) { + $paramValue = trim($param[1]); + } + + switch ($paramName) { + case 'expires': + $expire = $paramValue; + break; + case 'path': + $path = $paramValue; + break; + case 'domain': + $domain = $paramValue; + break; + case 'secure': + $secure = $paramValue; + break; + case 'httponly': + $httpOnly = $paramValue; + break; + } + } + + /* + * Create new Cookie object and add it to "cookies" array. + * I must skip to next header as cookies shouldn't be saved in "headers" array. + */ + $cookies[] = new Cookie($name, $value, $expire, $path, $domain, $secure, $httpOnly); + continue; + } + + /* + * Save response header which is not a cookie + */ + $headers[$key] = $value; + } + } + + return [ + 'headers' => $headers, + 'cookies' => $cookies, + 'content' => $content, + ]; + } + + /** + * Adds missing the "0" characters to given number until given length is reached + * + * Example: + * - number: 201 + * - length: 6 + * - will be returned: 000201 + * + * If "before" parameter is false, zeros will be inserted after given number. If given number is longer than + * given length the number will be returned as it was given to the method. + * + * @param mixed $number Number for who the "0" characters should be inserted + * @param int $length Wanted length of final number + * @param bool $before (optional) If false, 0 characters will be inserted after given number + * @return string + */ + public static function fillMissingZeros($number, $length, $before = true) + { + /* + * It's not a number? Empty string is not a number too. + * Nothing to do + */ + if (!is_numeric($number)) { + return ''; + } + + $text = trim($number); + $textLength = mb_strlen($text); + + if ($length <= $textLength) { + return $text; + } + + for ($i = ($length - $textLength); 0 < $i; --$i) { + if ($before) { + $text = '0' . $text; + continue; + } + + $text = $text . '0'; + } + + return $text; + } + + /** + * Returns information if given value is located in interval between given utmost left and right values + * + * @param int|float $value Value to verify + * @param int|float $left Left utmost value of interval + * @param int|float $right Right utmost value of interval + * @return bool + */ + public static function isBetween($value, $left, $right) + { + return $value > $left && $value < $right; + } + + /** + * Returns type of given variable. + * If it's an object, full class name is returned. + * + * @param mixed $variable Variable who type should be returned + * @return string + */ + public static function getType($variable) + { + if (is_object($variable)) { + return Reflection::getClassName($variable); + } + + return gettype($variable); + } + + /** + * Returns valid value of color's component (e.g. red). + * If given value is greater than 0, returns the value. Otherwise - 0. + * + * @param int $colorComponent Color's component to verify. Decimal value, e.g. 255. + * @param bool $asHexadecimal (optional) If is set to true, hexadecimal value is returned (default behaviour). + * Otherwise - decimal. + * @return int|string + */ + public static function getValidColorComponent($colorComponent, $asHexadecimal = true) + { + $colorComponent = (int)$colorComponent; + + if ($colorComponent < 0 || $colorComponent > 255) { + $colorComponent = 0; + } + + if ($asHexadecimal) { + $hexadecimal = dechex($colorComponent); + + if (strlen($hexadecimal) == 1) { + return sprintf('0%s', $hexadecimal, $hexadecimal); + } + + return $hexadecimal; + } + + return $colorComponent; + } + + /** + * Returns inverted value of color for given color + * + * @param string $color Hexadecimal value of color to invert (with or without hash), e.g. "dd244c" or "#22a5fe" + * @return string + */ + public static function getInvertedColor($color) + { + /* + * Prepare the color for later usage + */ + $color = trim($color); + $withHash = Regex::startsWith($color, '#'); + + /* + * Verify and get valid value of color. + * An exception will be thrown if the value is not a color. + */ + $color = Regex::getValidColorHexValue($color); + + /* + * Grab color's components + */ + $red = hexdec(substr($color, 0, 2)); + $green = hexdec(substr($color, 2, 2)); + $blue = hexdec(substr($color, 4, 2)); + + /* + * Calculate inverted color's components + */ + $redInverted = self::getValidColorComponent(255 - $red); + $greenInverted = self::getValidColorComponent(255 - $green); + $blueInverted = self::getValidColorComponent(255 - $blue); + + /* + * Voila, here is the inverted color + */ + $invertedColor = sprintf('%s%s%s', $redInverted, $greenInverted, $blueInverted); + + if ($withHash) { + return sprintf('#%s', $invertedColor); + } + + return $invertedColor; + } +} diff --git a/src/Meritoo/Common/Utilities/QueryBuilderUtility.php b/src/Meritoo/Common/Utilities/QueryBuilderUtility.php new file mode 100644 index 0000000..3b2fd88 --- /dev/null +++ b/src/Meritoo/Common/Utilities/QueryBuilderUtility.php @@ -0,0 +1,194 @@ + + * @copyright Meritoo.pl + */ +class QueryBuilderUtility +{ + /** + * Returns root alias of given query builder. + * If null is returned, alias was not found. + * + * @param QueryBuilder $queryBuilder The query builder to retrieve root alias + * @return null|string + */ + public static function getRootAlias(QueryBuilder $queryBuilder) + { + $aliases = $queryBuilder->getRootAliases(); + + if (empty($aliases)) { + return null; + } + + return Arrays::getFirstElement($aliases); + } + + /** + * Returns alias of given property joined in given query builder + * If the join does not exist, null is returned. + * It's also information if given property is already joined in given query builder. + * + * @param QueryBuilder $queryBuilder The query builder to verify + * @param string $property Name of property that maybe is joined + * @return null|string + */ + public static function getJoinedPropertyAlias(QueryBuilder $queryBuilder, $property) + { + $joins = $queryBuilder->getDQLPart('join'); + + if (empty($joins)) { + return null; + } + + $patternTemplate = '/^.+\.%s$/'; // e.g. "SomeThing.PropertyName" + $pattern = sprintf($patternTemplate, $property); + + foreach ($joins as $joinExpressions) { + /* @var $expression Join */ + foreach ($joinExpressions as $expression) { + $joinedProperty = $expression->getJoin(); + + if (preg_match($pattern, $joinedProperty)) { + return $expression->getAlias(); + } + } + } + + return null; + } + + /** + * Sets the WHERE criteria in given query builder + * + * @param QueryBuilder $queryBuilder The query builder + * @param array $criteria (optional) The criteria used in WHERE clause. It may simple array with pairs + * key-value or an array of arrays where second element of sub-array is the + * comparison operator. Example below. + * @param string $alias (optional) Alias used in the query + * @return QueryBuilder + * + * Example of the $criteria argument: + * [ + * 'created_at' => [ + * '2012-11-17 20:00', + * '<' + * ], + * 'title' => [ + * '%test%', + * 'like' + * ], + * 'position' => 5, + * ] + */ + public static function setCriteria(QueryBuilder $queryBuilder, array $criteria = [], $alias = '') + { + if (!empty($criteria)) { + if (empty($alias)) { + $alias = self::getRootAlias($queryBuilder); + } + + foreach ($criteria as $column => $value) { + $compareOperator = '='; + + if (is_array($value) && !empty($value)) { + if (count($value) == 2) { + $compareOperator = $value[1]; + } + + $value = $value[0]; + } + + $predicate = sprintf('%s.%s %s :%s', $alias, $column, $compareOperator, $column); + + if ($value === null) { + $predicate = $queryBuilder->expr()->isNull(sprintf('%s.%s', $alias, $column)); + unset($criteria[$column]); + } else { + $queryBuilder->setParameter($column, $value); + } + + $queryBuilder = $queryBuilder->andWhere($predicate); + } + } + + return $queryBuilder; + } + + /** + * Deletes given entities + * + * @param EntityManager $entityManager The entity manager + * @param array|ArrayCollection $entities The entities to delete + * @param bool $flushDeleted (optional) If is set to true, flushes the deleted objects. + * Otherwise - not. + * @return bool + */ + public static function deleteEntities(EntityManager $entityManager, $entities, $flushDeleted = true) + { + /* + * No entities found? + * Nothing to do + */ + if (empty($entities)) { + return false; + } + + foreach ($entities as $entity) { + $entityManager->remove($entity); + } + + /* + * The deleted objects should be flushed? + */ + if ($flushDeleted) { + $entityManager->flush(); + } + + return true; + } + + /** + * Adds given parameters to given query builder. + * Attention. Existing parameters will be overridden. + * + * @param QueryBuilder $queryBuilder The query builder + * @param array|ArrayCollection $parameters Parameters to add. Collection of instances of + * Doctrine\ORM\Query\Parameter class or an array with key-value pairs. + * @return QueryBuilder + */ + public static function addParameters(QueryBuilder $queryBuilder, $parameters) + { + if (!empty($parameters)) { + foreach ($parameters as $key => $parameter) { + $name = $key; + $value = $parameter; + + if ($parameter instanceof Parameter) { + $name = $parameter->getName(); + $value = $parameter->getValue(); + } + + $queryBuilder->setParameter($name, $value); + } + } + + return $queryBuilder; + } +} diff --git a/src/Meritoo/Common/Utilities/Reflection.php b/src/Meritoo/Common/Utilities/Reflection.php new file mode 100644 index 0000000..3bc4998 --- /dev/null +++ b/src/Meritoo/Common/Utilities/Reflection.php @@ -0,0 +1,633 @@ + + * @copyright Meritoo.pl + */ +class Reflection +{ + /** + * Returns names of methods for given class / object + * + * @param object|string $class The object or name of object's class + * @param bool $withoutInheritance (optional) If is set to true, only methods for given class are returned. + * Otherwise - all methods, with inherited methods too. + * @return array + */ + public static function getMethods($class, $withoutInheritance = false) + { + $effect = []; + + $reflection = new ReflectionClass($class); + $methods = $reflection->getMethods(); + + if (!empty($methods)) { + $className = self::getClassName($class); + + foreach ($methods as $method) { + if ($method instanceof ReflectionMethod) { + if ($withoutInheritance && $className !== $method->class) { + continue; + } + + $effect[] = $method->name; + } + } + } + + return $effect; + } + + /** + * Returns constants of given class / object + * + * @param object|string $class The object or name of object's class + * @return array + */ + public static function getConstants($class) + { + $reflection = new ReflectionClass($class); + + return $reflection->getConstants(); + } + + /** + * Returns maximum constant from all constants of given class / object. + * Values of constants should be integers. + * + * @param object|string $class The object or name of object's class + * @return int|null + */ + public static function getMaxNumberConstant($class) + { + $constants = self::getConstants($class); + + if (empty($constants)) { + return null; + } + + $maxNumber = 0; + + foreach ($constants as $constant) { + if (is_numeric($constant) && $constant > $maxNumber) { + $maxNumber = $constant; + } + } + + return $maxNumber; + } + + /** + * Returns information if given class / object has given method + * + * @param object|string $class The object or name of object's class + * @param string $method Name of the method to find + * @return bool + */ + public static function hasMethod($class, $method) + { + $reflection = new ReflectionClass($class); + + return $reflection->hasMethod($method); + } + + /** + * Returns information if given class / object has given property + * + * @param object|string $class The object or name of object's class + * @param string $property Name of the property to find + * @return bool + */ + public static function hasProperty($class, $property) + { + $reflection = new ReflectionClass($class); + + return $reflection->hasProperty($property); + } + + /** + * Returns information if given class / object has given constant + * + * @param object|string $class The object or name of object's class + * @param string $constant Name of the constant to find + * @return bool + */ + public static function hasConstant($class, $constant) + { + $reflection = new ReflectionClass($class); + + return $reflection->hasConstant($constant); + } + + /** + * Returns value of given constant + * + * @param object|string $class The object or name of object's class + * @param string $constant Name of the constant that contains a value + * @return mixed + */ + public static function getConstantValue($class, $constant) + { + $reflection = new ReflectionClass($class); + + if (self::hasConstant($class, $constant)) { + return $reflection->getConstant($constant); + } + + return null; + } + + /** + * Returns value of given property. + * Looks for proper getter for the property. + * + * @param mixed $object Object that should contains given property + * @param string $property Name of the property that contains a value. It may be also multiple properties + * dot-separated, e.g. "invoice.user.email". + * @param bool $force (optional) If is set to true, try to retrieve value even if the object doesn't have + * property. Otherwise - not. + * @return mixed + */ + public static function getPropertyValue($object, $property, $force = false) + { + $value = null; + + /* + * Property is a dot-separated string? + * Let's find all values of the chain, of the dot-separated properties + */ + if (Regex::contains($property, '.')) { + $exploded = explode('.', $property); + + $property = $exploded[0]; + $object = self::getPropertyValue($object, $property, $force); + + /* + * Value of processed property from the chain is not null? + * Let's dig more and get proper value + * + * Required to avoid bug: + * ReflectionObject::__construct() expects parameter 1 to be object, null given + * (...) + * 4. at ReflectionObject->__construct (null) + * 5. at Reflection ::getPropertyValue (null, 'name', true) + * 6. at ListService->getItemValue (object(Deal), 'project.name', '0') + * + * while using "project.name" as property - $project has $name property ($project exists in the Deal class) + * and the $project equals null + * + * Krzysztof Niziol + * 2016-11-07 + */ + if ($object !== null) { + unset($exploded[0]); + + $property = implode('.', $exploded); + $value = self::getPropertyValue($object, $property, $force); + } + } else { + $className = self::getClassName($object); + $reflectionProperty = null; + + /* + * 1st try: + * Use \ReflectionObject class + */ + try { + $reflectionProperty = new ReflectionProperty($className, $property); + $value = $reflectionProperty->getValue($object); + } catch (ReflectionException $exception) { + /* + * 2nd try: + * Look for the get / has / is methods + */ + $class = new ReflectionObject($object); + $valueFound = false; + + if ($class->hasProperty($property) || $force) { + $property = Inflector::classify($property); + + $methodPrefixes = [ + 'get', + 'has', + 'is', + ]; + + foreach ($methodPrefixes as $prefix) { + $method = sprintf('%s%s', $prefix, $property); + + if ($class->hasMethod($method)) { + $value = $object->{$method}(); + $valueFound = true; + break; + } + } + } + + if (!$valueFound && $reflectionProperty !== null) { + /* + * Oops, we have got exception. + * + * 3rd try: + * Let's try modify accessibility of the property and try again to get value. + */ + $reflectionProperty->setAccessible(true); + $value = $reflectionProperty->getValue($object); + } + } + } + + return $value; + } + + /** + * Returns values of given property for given objects. + * Looks for proper getter for the property. + * + * @param Collection|object|array $objects The objects that should contain given property. It may be also one + * object. + * @param string $property Name of the property that contains a value + * @param bool $force (optional) If is set to true, try to retrieve value even if the + * object does not have property. Otherwise - not. + * @return array + */ + public static function getPropertyValues($objects, $property, $force = false) + { + if ($objects instanceof Collection) { + $objects = $objects->toArray(); + } + + $values = []; + $objects = Arrays::makeArray($objects); + + foreach ($objects as $entity) { + $value = self::getPropertyValue($entity, $property, $force); + + if ($value !== null) { + $values[] = $value; + } + } + + return $values; + } + + /** + * Returns a class name for given source + * + * @param array|object|string $source An array of objects, namespaces, object or namespace + * @param bool $withoutNamespace (optional) If is set to true, namespace is omitted. Otherwise - + * not, full name of class is returned, with namespace. + * @return string|null + */ + public static function getClassName($source, $withoutNamespace = false) + { + /* + * First argument is not proper source of class? + * Nothing to do + */ + if (empty($source) || (!is_array($source) && !is_object($source) && !is_string($source))) { + return null; + } + + $name = ''; + + /* + * An array of objects was provided? + * Let's use first of them + */ + if (is_array($source)) { + $source = Arrays::getFirstElement($source); + } + + /* + * Let's prepare name of class + */ + if (is_object($source)) { + $name = get_class($source); + } elseif (is_string($source) && (class_exists($source) || trait_exists($source))) { + $name = $source; + } + + /* + * Name of class is still unknown? + * Nothing to do + */ + if (empty($name)) { + return null; + } + + /* + * Namespace is not required? + * Let's return name of class only + */ + if ($withoutNamespace) { + $classOnly = Miscellaneous::getLastElementOfString($name, '\\'); + + if ($classOnly !== null) { + $name = $classOnly; + } + + return $name; + } + + return ClassUtils::getRealClass($name); + } + + /** + * Returns namespace of class for given source + * + * @param array|object|string $source An array of objects, namespaces, object or namespace + * @return string + */ + public static function getClassNamespace($source) + { + $fullClassName = self::getClassName($source); + + if (empty($fullClassName)) { + return ''; + } + + $className = self::getClassName($source, true); + + if ($className == $fullClassName) { + return $className; + } + + return Miscellaneous::getStringWithoutLastElement($fullClassName, '\\'); + } + + /** + * Returns information if given interface is implemented by given class / object + * + * @param array|object|string $source An array of objects, namespaces, object or namespace + * @param string $interface The interface that should be implemented + * @return bool + */ + public static function isInterfaceImplemented($source, $interface) + { + $className = self::getClassName($source); + $interfaces = class_implements($className); + + return in_array($interface, $interfaces); + } + + /** + * Returns information if given child class is a subclass of given parent class + * + * @param array|object|string $childClass The child class. An array of objects, namespaces, object or namespace. + * @param array|object|string $parentClass The parent class. An array of objects, namespaces, object or namespace. + * @return bool + */ + public static function isChildOfClass($childClass, $parentClass) + { + $childClassName = self::getClassName($childClass); + $parentClassName = self::getClassName($parentClass); + + $parents = class_parents($childClassName); + + if (is_array($parents)) { + return in_array($parentClassName, $parents); + } + + return false; + } + + /** + * Returns given object properties + * + * @param array|object|string $source An array of objects, namespaces, object or namespace + * @param int $filter (optional) Filter of properties. Uses ReflectionProperty class constants. + * By default all properties are returned. + * @return array|ReflectionProperty + */ + public static function getProperties($source, $filter = null) + { + $className = self::getClassName($source); + $reflection = new ReflectionClass($className); + + if ($filter === null) { + $filter = ReflectionProperty::IS_PRIVATE + + ReflectionProperty::IS_PROTECTED + + ReflectionProperty::IS_PUBLIC + + ReflectionProperty::IS_STATIC; + } + + return $reflection->getProperties($filter); + } + + /** + * Returns a parent class + * + * @param array|object|string $source An array of objects, namespaces, object or namespace + * @return ReflectionClass + */ + public static function getParentClass($source) + { + $className = self::getClassName($source); + $reflection = new ReflectionClass($className); + + return $reflection->getParentClass(); + } + + /** + * Returns child classes of given class. + * It's an array of namespaces of the child classes or null (if given class has not child classes). + * + * @param array|object|string $class Class who child classes should be returned. An array of objects, strings, + * object or string. + * @return array|null + * @throws CannotResolveClassNameException + */ + public static function getChildClasses($class) + { + $allClasses = get_declared_classes(); + + /* + * No classes? + * Nothing to do + */ + if (empty($allClasses)) { + return null; + } + + $className = self::getClassName($class); + + /* + * Oops, cannot resolve class + */ + if ($className === null) { + throw new CannotResolveClassNameException($class); + } + + $childClasses = []; + + foreach ($allClasses as $oneClass) { + if (self::isChildOfClass($oneClass, $className)) { + /* + * Attention. I have to use ClassUtils::getRealClass() method to avoid problem with the proxy / cache + * classes. Example: + * - My\ExtraBundle\Entity\MyEntity + * - Proxies\__CG__\My\ExtraBundle\Entity\MyEntity + * + * It's actually the same class, so I have to skip it. + */ + $realClass = ClassUtils::getRealClass($oneClass); + + if (in_array($realClass, $childClasses)) { + continue; + } + + $childClasses[] = $realClass; + } + } + + return $childClasses; + } + + /** + * Returns namespace of one child class which extends given class. + * Extended class should has only one child class. + * + * @param array|object|string $parentClass Class who child class should be returned. An array of objects, + * namespaces, object or namespace. + * @return mixed + * + * @throws MissingChildClassesException + * @throws TooManyChildClassesException + */ + public static function getOneChildClass($parentClass) + { + $childClasses = self::getChildClasses($parentClass); + + /* + * No child classes? + * Oops, the base / parent class hasn't child class + */ + if (empty($childClasses)) { + throw new MissingChildClassesException($parentClass); + } + + /* + * More than 1 child class? + * Oops, the base / parent class has too many child classes + */ + if (count($childClasses) > 1) { + throw new TooManyChildClassesException($parentClass, $childClasses); + } + + return trim($childClasses[0]); + } + + /** + * Returns property, the ReflectionProperty instance, of given object + * + * @param array|object|string $class An array of objects, namespaces, object or namespace + * @param string $property Name of the property + * @param int $filter (optional) Filter of properties. Uses ReflectionProperty class constants. + * By default all properties are allowed / processed. + * @return null|ReflectionProperty + */ + public static function getProperty($class, $property, $filter = null) + { + $className = self::getClassName($class); + $properties = self::getProperties($className, $filter); + + if (!empty($properties)) { + /* @var $reflectionProperty ReflectionProperty */ + foreach ($properties as $reflectionProperty) { + if ($reflectionProperty->getName() == $property) { + return $reflectionProperty; + } + } + } + + return null; + } + + /** + * Returns information if given class / object uses / implements given trait + * + * @param array|object|string $class An array of objects, namespaces, object or namespace + * @param array|string $trait An array of strings or string + * @param bool $verifyParents If is set to true, parent classes are verified if they use given + * trait. Otherwise - not. + * @return bool|null + * @throws CannotResolveClassNameException + */ + public static function usesTrait($class, $trait, $verifyParents = false) + { + $className = self::getClassName($class); + $traitName = self::getClassName($trait); + + /* + * Oops, cannot resolve class + */ + if (empty($className)) { + throw new CannotResolveClassNameException($class); + } + + /* + * Oops, cannot resolve trait + */ + if (empty($traitName)) { + throw new CannotResolveClassNameException($class, false); + } + + $reflection = new ReflectionClass($className); + $traitsNames = $reflection->getTraitNames(); + + $uses = in_array($traitName, $traitsNames); + + if (!$uses && $verifyParents) { + $parentClassName = self::getParentClassName($className); + + if ($parentClassName !== null) { + return self::usesTrait($parentClassName, $trait, true); + } + } + + return $uses; + } + + /** + * Returns name of the parent class. + * If given class does not extend another, returns null. + * + * @param array|object|string $class An array of objects, namespaces, object or namespace + * @return string|null + */ + public static function getParentClassName($class) + { + $className = self::getClassName($class); + $reflection = new ReflectionClass($className); + $parentClass = $reflection->getParentClass(); + + if ($parentClass === null || $parentClass === false) { + return null; + } + + return $parentClass->getName(); + } +} diff --git a/src/Meritoo/Common/Utilities/Regex.php b/src/Meritoo/Common/Utilities/Regex.php new file mode 100644 index 0000000..e6ac473 --- /dev/null +++ b/src/Meritoo/Common/Utilities/Regex.php @@ -0,0 +1,726 @@ + + * @copyright Meritoo.pl + */ +class Regex +{ + /** + * Patterns used to validate / verify values + * + * @var array + */ + private static $patterns = [ + 'email' => '/[\w-]{2,}@[\w-]+\.[\w]{2,}+/', + 'phone' => '/^\+?[0-9 ]+$/', + 'camelCasePart' => '/([a-z]|[A-Z]){1}[a-z]*/', + 'urlProtocol' => '/^([a-z]+:\/\/)', + 'urlDomain' => '([\da-z\.-]+)\.([a-z\.]{2,6})(\/)?([\w\.\-]*)?(\?)?([\w \.\-\/=&]*)\/?$/i', + 'letterOrDigit' => '/[a-zA-Z0-9]+/', + 'htmlEntity' => '/&[a-z0-9]+;/', + 'fileName' => '/.+\.\w+$/', + 'isQuoted' => '/^[\'"]{1}.+[\'"]{1}$/', + 'windowsBasedPath' => '/^[A-Z]{1}:\\\.*$/', + 'money' => '/^[-+]?\d+([\.,]{1}\d*)?$/', + 'color' => '/^[a-f0-9]{6}$/i', + ]; + + /** + * Returns information if given e-mail address is valid + * + * @param string $email E-mail address to validate / verify + * @return bool + * + * Examples: + * a) valid e-mails: + * - ni@g-m.pl + * - ni@gm.pl + * - ni@g_m.pl + * b) invalid e-mails: + * - ni@g-m.p + * - n@g-m.pl + */ + public static function isValidEmail($email) + { + $pattern = self::getEmailPattern(); + + return (bool)preg_match($pattern, $email); + } + + /** + * Returns information if given tax ID (in polish: NIP) is valid + * + * @param string $taxidString Tax ID (NIP) string + * @return bool + */ + public static function isValidTaxid($taxidString) + { + if (!empty($taxidString)) { + $weights = [ + 6, + 5, + 7, + 2, + 3, + 4, + 5, + 6, + 7, + ]; + $taxid = preg_replace('/[\s-]/', '', $taxidString); + $sum = 0; + + if (strlen($taxid) == 10 && is_numeric($taxid)) { + for ($x = 0; $x <= 8; ++$x) { + $sum += $taxid[$x] * $weights[$x]; + } + + if ((($sum % 11) % 10) == $taxid[9]) { + return true; + } + } + } + + return false; + } + + /** + * Returns information if given url address is valid + * + * @param string $url The url to validate / verify + * @param bool $requireProtocol (optional) If is set to true, the protocol is required to be passed in the url. + * Otherwise - not. + * @return bool + */ + public static function isValidUrl($url, $requireProtocol = false) + { + $pattern = self::getUrlPattern($requireProtocol); + + return (bool)preg_match($pattern, $url); + } + + /** + * Returns information if given phone number is valid + * + * @param string $phoneNumber The phone number to validate / verify + * @return bool + */ + public static function isValidPhoneNumber($phoneNumber) + { + $pattern = self::getPhoneNumberPattern(); + + return (bool)preg_match($pattern, $phoneNumber); + } + + /** + * Returns array values that matches given pattern (or values that keys matches) + * + * @param string $pattern Pattern to match + * @param array $dataArray The array + * @param bool $itsKeyPattern (optional) If is set to true, keys are checks if they match pattern. Otherwise - + * values are checks. + * @return array + */ + public static function getArrayValuesByPattern($pattern, $dataArray, $itsKeyPattern = false) + { + if ($itsKeyPattern) { + $effect = []; + + if (!empty($dataArray)) { + $matches = []; + + foreach ($dataArray as $key => $value) { + if (preg_match($pattern, $key, $matches)) { + $effect[$key] = $value; + } + } + } + + return $effect; + } + + return preg_grep($pattern, $dataArray); + } + + /** + * Filters array by given expression and column + * + * Expression can be simple compare expression, like ' == 2', or regular expression. + * Returns filtered array. + * + * @param array $array The array that should be filtered + * @param string $arrayColumnKey Column name + * @param string $filterExpression Filter expression, e.g. '== 2' or '!= \'home\'' + * @param bool $itsRegularExpression (optional) If is set to true, means that filter expression is a + * regular expression + * @return array + */ + public static function arrayFilter($array, $arrayColumnKey, $filterExpression, $itsRegularExpression = false) + { + $effect = []; + + if (!empty($array)) { + $effect = $array; + + foreach ($effect as $key => &$item) { + if (isset($item[$arrayColumnKey])) { + $value = $item[$arrayColumnKey]; + + if ($itsRegularExpression) { + $matches = []; + $pattern = '|' . $filterExpression . '|'; + $matchesCount = preg_match($pattern, $value, $matches); + + $remove = $matchesCount == 0; + } else { + if ($value == '') { + $value = '\'\''; + } elseif (is_string($value)) { + $value = '\'' . $value . '\''; + } + + eval('$isTrue = ' . $value . $filterExpression . ';'); + + /* @var bool $isTrue */ + $remove = !$isTrue; + } + + if ($remove) { + unset($effect[$key]); + } + } + } + } + + return $effect; + } + + /** + * Perform regular expression match with many given patterns. + * Returns information if given $subject matches one or all given $patterns. + * + * @param array|string $patterns The patterns to match + * @param string $subject The string to check + * @param bool $mustAllMatch (optional) If is set to true, $subject must match all $patterns. Otherwise - + * not. + * @return bool + */ + public static function pregMultiMatch($patterns, $subject, $mustAllMatch = false) + { + $effect = false; + $patterns = Arrays::makeArray($patterns); + + if (!empty($patterns)) { + if ($mustAllMatch) { + $effect = true; + } + + foreach ($patterns as $pattern) { + $matches = []; + $matched = (bool)preg_match_all($pattern, $subject, $matches); + + if ($mustAllMatch) { + $effect = $effect && $matched; + } else { + if ($matched) { + $effect = $matched; + break; + } + } + } + } + + return $effect; + } + + /** + * Returns string in human readable style generated from given camel case string / text + * + * @param string $string The string / text to convert + * @param bool $applyUpperCaseFirst (optional) If is set to true, first word / element from the converted + * string is uppercased. Otherwise - not. + * @return string + */ + public static function camelCase2humanReadable($string, $applyUpperCaseFirst = false) + { + $parts = self::getCamelCaseParts($string); + + if (!empty($parts)) { + $elements = []; + + foreach ($parts as $part) { + $elements[] = strtolower($part); + } + + $string = implode(' ', $elements); + + if ($applyUpperCaseFirst) { + $string = ucfirst($string); + } + } + + return $string; + } + + /** + * Returns parts of given camel case string / text + * + * @param string $string The string / text to retrieve parts + * @return array + */ + public static function getCamelCaseParts($string) + { + $pattern = self::getCamelCasePartPattern(); + $matches = []; + preg_match_all($pattern, $string, $matches); + + return $matches[0]; + } + + /** + * Returns simple, lowercase string generated from given camel case string / text + * + * @param string $string The string / text to convert + * @param string $separator (optional) Separator used to concatenate parts of the string, e.g. '-' or '_' + * @param bool $applyLowercase (optional) If is set to true, returned string will be lowercased. Otherwise - not. + * @return string + */ + public static function camelCase2simpleLowercase($string, $separator = '', $applyLowercase = true) + { + $parts = self::getCamelCaseParts($string); + + if (!empty($parts)) { + $string = implode($separator, $parts); + + if ($applyLowercase) { + $string = strtolower($string); + } + } + + return $string; + } + + /** + * Returns pattern used to validate / verify or get e-mail address + * + * @return string + */ + public static function getEmailPattern() + { + return self::$patterns['email']; + } + + /** + * Returns pattern used to validate / verify or get phone number + * + * @return string + */ + public static function getPhoneNumberPattern() + { + return self::$patterns['phone']; + } + + /** + * Returns pattern used to validate / verify or get camel case parts of string + * + * @return string + */ + public static function getCamelCasePartPattern() + { + return self::$patterns['camelCasePart']; + } + + /** + * Returns pattern used to validate / verify or get url address + * + * @param bool $requireProtocol (optional) If is set to true, the protocol is required to be passed in the url. + * Otherwise - not. + * @return string + */ + public static function getUrlPattern($requireProtocol = false) + { + $urlProtocol = self::$patterns['urlProtocol']; + $urlDomain = self::$patterns['urlDomain']; + $protocolPatternPart = '?'; + + if ($requireProtocol) { + $protocolPatternPart = ''; + } + + return sprintf('%s%s%s', $urlProtocol, $protocolPatternPart, $urlDomain); + } + + /** + * Returns information if given path is sub-path of another path, e.g. path file is owned by path of directory + * + * @param string $subPath Path to verify, probably sub-path + * @param string $path Main / parent path + * @return bool + */ + public static function isSubPathOf($subPath, $path) + { + /* + * Empty path? + * Nothing to do + */ + if (empty($path) || empty($subPath)) { + return false; + } + + /* + * I have to escape all slashes (directory separators): "/" -> "\/" + */ + $prepared = preg_quote($path, '/'); + + /* + * Slash at the ending is optional + */ + if (self::endsWith($path, '/')) { + $prepared .= '?'; + } + + $pattern = sprintf('/^%s.*/', $prepared); + + return (bool)preg_match($pattern, $subPath); + } + + /** + * Returns pattern used to validate / verify letter or digit + * + * @return string + */ + public static function getLetterOrDigitPattern() + { + return self::$patterns['letterOrDigit']; + } + + /** + * Returns information if given character is a letter or digit + * + * @param string $char Character to check + * @return bool + */ + public static function isLetterOrDigit($char) + { + $pattern = self::getLetterOrDigitPattern(); + + return is_scalar($char) && (bool)preg_match($pattern, $char); + } + + /** + * Returns information if the string starts with given beginning / characters + * + * @param string $string String to check + * @param string $beginning The beginning of string, one or more characters + * @return bool + */ + public static function startsWith($string, $beginning) + { + if (!empty($string) && !empty($beginning)) { + if (strlen($beginning) == 1 && !self::isLetterOrDigit($beginning)) { + $beginning = '\\' . $beginning; + } + + $pattern = sprintf('|^%s|', $beginning); + + return (bool)preg_match($pattern, $string); + } + + return false; + } + + /** + * Returns information if the string ends with given ending / characters + * + * @param string $string String to check + * @param string $ending The ending of string, one or more characters + * @return bool + */ + public static function endsWith($string, $ending) + { + if (strlen($ending) == 1 && !self::isLetterOrDigit($ending)) { + $ending = '\\' . $ending; + } + + return (bool)preg_match('|' . $ending . '$|', $string); + } + + /** + * Returns information if the string starts with directory's separator + * + * @param string $string String that may contain a directory's separator at the start / beginning + * @param string $separator (optional) The directory's separator, e.g. "/". If is empty (not provided), system's + * separator is used. + * @return bool + */ + public static function startsWithDirectorySeparator($string, $separator = '') + { + if (empty($separator)) { + $separator = DIRECTORY_SEPARATOR; + } + + return self::startsWith($string, $separator); + } + + /** + * Returns information if the string ends with directory's separator + * + * @param string $text String that may contain a directory's separator at the end + * @param string $separator (optional) The directory's separator, e.g. "/". If is empty (not provided), system's + * separator is used. + * @return string + */ + public static function endsWithDirectorySeparator($text, $separator = '') + { + if (empty($separator)) { + $separator = DIRECTORY_SEPARATOR; + } + + return self::endsWith($text, $separator); + } + + /** + * Returns information if uri contains parameter + * + * @param string $uri Uri string (e.g. $_SERVER['REQUEST_URI']) + * @param string $parameterName Uri parameter name + * @return bool + */ + public static function isSetUriParameter($uri, $parameterName) + { + return (bool)preg_match('|[?&]{1}' . $parameterName . '=|', $uri); // e.g. ?name=phil&type=4 -> '$type=' + } + + /** + * Returns pattern used to validate / verify html entity + * + * @return string + */ + public static function getHtmlEntityPattern() + { + return self::$patterns['htmlEntity']; + } + + /** + * Returns information if the string contains html entities + * + * @param string $string String to check + * @return bool + */ + public static function containsEntities($string) + { + $pattern = self::getHtmlEntityPattern(); + + return (bool)preg_match_all($pattern, $string); + } + + /** + * Returns information if one string contains another string + * + * @param string $haystack The string to search in + * @param string $needle The string to be search for + * @return bool + */ + public static function contains($haystack, $needle) + { + if (strlen($needle) == 1 && !self::isLetterOrDigit($needle)) { + $needle = '\\' . $needle; + } + + return (bool)preg_match('|.*' . $needle . '.*|', $haystack); + } + + /** + * Returns pattern used to validate / verify name of file + * + * @return string + */ + public static function getFileNamePattern() + { + return self::$patterns['fileName']; + } + + /** + * Returns information if given name of file is a really name of file. + * Verifies if given name contains a dot and an extension, e.g. "My File 001.jpg". + * + * @param string $fileName Name of file to check. It may be path of file also. + * @return bool + */ + public static function isFileName($fileName) + { + $pattern = self::getFileNamePattern(); + + return (bool)preg_match($pattern, $fileName); + } + + /** + * Returns pattern used to validate / verify if value is quoted (by apostrophes or quotation marks) + * + * @return string + */ + public static function getIsQuotedPattern() + { + return self::$patterns['isQuoted']; + } + + /** + * Returns information if given value is quoted (by apostrophes or quotation marks) + * + * @param mixed $value The value to check + * @return bool + */ + public static function isQuoted($value) + { + $pattern = self::getIsQuotedPattern(); + + return is_scalar($value) && (bool)preg_match($pattern, $value); + } + + /** + * Returns pattern used to validate / verify if given path is a Windows-based path, e.g. "C:\path\to\file.jpg" + * + * @return string + */ + public static function getWindowsBasedPathPattern() + { + return self::$patterns['windowsBasedPath']; + } + + /** + * Returns information if given path is a Windows-based path, e.g. "C:\path\to\file.jpg" + * + * @param string $path The path to verify + * @return bool + */ + public static function isWindowsBasedPath($path) + { + $pattern = self::getWindowsBasedPathPattern(); + + return (bool)preg_match($pattern, $path); + } + + /** + * Returns information if given NIP number is valid + * + * @param string $nip A given NIP number + * @return bool + * + * @see https://pl.wikipedia.org/wiki/NIP#Znaczenie_numeru + */ + public static function isValidNip($nip) + { + $nip = preg_replace('/[^0-9]/', '', $nip); + + $invalidNips = [ + '1234567890', + '0000000000', + ]; + + if (!preg_match('/^[0-9]{10}$/', $nip) || in_array($nip, $invalidNips)) { + return false; + } + + $sum = 0; + $weights = [ + 6, + 5, + 7, + 2, + 3, + 4, + 5, + 6, + 7, + ]; + + for ($i = 0; $i < 9; ++$i) { + $sum += $weights[$i] * $nip[$i]; + } + + $modulo = $sum % 11; + $numberControl = ($modulo == 10) ? 0 : $modulo; + + return $numberControl == $nip[9]; + } + + /** + * Returns pattern used to validate / verify if given value is money-related value + * + * @return string + */ + public static function getMoneyPattern() + { + return self::$patterns['money']; + } + + /** + * Returns information if given value is valid money-related value + * + * @param mixed $value Value to verify + * @return bool + */ + public static function isValidMoneyValue($value) + { + $pattern = self::getMoneyPattern(); + + return (bool)preg_match($pattern, $value); + } + + /** + * Returns valid given hexadecimal value of color. + * If the value is invalid, throws an exception or returns false. + * + * @param string $color Color to verify + * @param bool $throwException (optional) If is set to true, throws an exception if given color is invalid + * (default behaviour). Otherwise - not. + * @return string|bool + * + * @throws IncorrectColorHexLengthException + * @throws InvalidColorHexValueException + */ + public static function getValidColorHexValue($color, $throwException = true) + { + $color = Miscellaneous::replace($color, '/#/', ''); + $length = strlen($color); + + if ($length === 3) { + $color = Miscellaneous::replace($color, '/(.)(.)(.)/', '$1$1$2$2$3$3'); + } else { + if ($length !== 6) { + if ($throwException) { + throw new IncorrectColorHexLengthException($color); + } + + return false; + } + } + + $pattern = self::$patterns['color']; + $match = (bool)preg_match($pattern, $color); + + if (!$match) { + if ($throwException) { + throw new InvalidColorHexValueException($color); + } + + return false; + } + + return strtolower($color); + } +} diff --git a/src/Meritoo/Common/Utilities/Repository.php b/src/Meritoo/Common/Utilities/Repository.php new file mode 100644 index 0000000..b0246ba --- /dev/null +++ b/src/Meritoo/Common/Utilities/Repository.php @@ -0,0 +1,109 @@ + + * @copyright Meritoo.pl + */ +class Repository +{ + /** + * Replenishes positions of given items + * + * @param array $items The items + * @param bool $asLast (optional) If is set to true, items are placed at the end. Otherwise - at the top. + * @param bool $force (optional) If is set to true, positions are set even there is no extreme position. + * Otherwise - if extreme position is not found (is null) replenishment is stopped / skipped. + */ + public static function replenishPositions($items, $asLast = true, $force = false) + { + $position = self::getExtremePosition($items, $asLast); + + if ($position === null && $force) { + $position = 0; + } + + if ($position !== null && !empty($items)) { + foreach ($items as $item) { + if (method_exists($item, 'getPosition')) { + if ($item->getPosition() === null) { + if ($asLast) { + ++$position; + } else { + --$position; + } + + if (method_exists($item, 'setPosition')) { + $item->setPosition($position); + } + } + } + } + } + } + + /** + * Returns extreme position (max or min) of given items + * + * @param array $items The items + * @param bool $max (optional) If is set to true, maximum value is returned. Otherwise - minimum. + * @return int + */ + public static function getExtremePosition($items, $max = true) + { + $extreme = null; + + if (!empty($items)) { + foreach ($items as $item) { + if (Reflection::hasMethod($item, 'getPosition')) { + $position = $item->getPosition(); + + if ($max) { + if ($position > $extreme) { + $extreme = $position; + } + } else { + if ($position < $extreme) { + $extreme = $position; + } + } + } + } + } + + return $extreme; + } + + /** + * Returns query builder for given entity's repository. + * The entity should contain given property, e.g. "name". + * + * @param EntityRepository $repository Repository of the entity + * @param string $property (optional) Name of property used by the ORDER BY clause + * @param string $direction (optional) Direction used by the ORDER BY clause ("ASC" or "DESC") + * @return QueryBuilder + */ + public static function getEntityOrderedQueryBuilder( + EntityRepository $repository, + $property = 'name', + $direction = 'ASC' + ) { + $alias = 'qb'; + + return $repository + ->createQueryBuilder($alias) + ->orderBy(sprintf('%s.%s', $alias, $property), $direction); + } +} diff --git a/src/Meritoo/Common/Utilities/TestCase.php b/src/Meritoo/Common/Utilities/TestCase.php new file mode 100644 index 0000000..837d00d --- /dev/null +++ b/src/Meritoo/Common/Utilities/TestCase.php @@ -0,0 +1,103 @@ + + * @copyright Meritoo.pl + */ +class TestCase extends \PHPUnit_Framework_TestCase +{ + /** + * Provides an empty value + * + * @return Generator + */ + public function provideEmptyValue() + { + yield['']; + yield[' ']; + yield[null]; + yield[0]; + yield[false]; + yield[[]]; + } + + /** + * Provides boolean value + * + * @return Generator + */ + public function provideBooleanValue() + { + yield[false]; + yield[true]; + } + + /** + * Provides instance of DateTime class + * + * @return Generator + */ + public function provideDateTimeInstance() + { + yield[new DateTime()]; + yield[new DateTime('yesterday')]; + yield[new DateTime('now')]; + yield[new DateTime('tomorrow')]; + } + + /** + * Provides relative / compound format of DateTime + * + * @return Generator + */ + public function provideDateTimeRelativeFormat() + { + yield['now']; + yield['yesterday']; + yield['tomorrow']; + yield['back of 10']; + yield['front of 10']; + yield['last day of February']; + yield['first day of next month']; + yield['last day of previous month']; + yield['last day of next month']; + yield['Y-m-d']; + yield['Y-m-d 10:00']; + } + + /** + * Provides path of not existing file, e.g. "lorem/ipsum.jpg" + * + * @return Generator + */ + public function provideNotExistingFilePath() + { + yield['lets-test.doc']; + yield['lorem/ipsum.jpg']; + yield['suprise/me/one/more/time.txt']; + } + + /** + * Returns path of file used by tests. + * It should be placed in /data/tests directory of this project. + * + * @param string $fileName Name of file + * @param string $directoryPath (optional) Path of directory containing the file + * @return string + */ + public function getFilePathToTests($fileName, $directoryPath = '') + { + if (!empty($directoryPath)) { + $directoryPath = '/' . $directoryPath; + } + + return sprintf('%s/../../../../data/tests/%s%s', __DIR__, $fileName, $directoryPath); + } +} diff --git a/src/Meritoo/Common/Utilities/Uri.php b/src/Meritoo/Common/Utilities/Uri.php new file mode 100644 index 0000000..57b68ea --- /dev/null +++ b/src/Meritoo/Common/Utilities/Uri.php @@ -0,0 +1,310 @@ + + * @copyright Meritoo.pl + */ +class Uri +{ + /** + * Returns full uri string + * + * @param bool $withoutHost (optional) If is set to true, means that host / server name is omitted + * @return string + */ + public static function getFullUri($withoutHost = false) + { + $effect = Miscellaneous::getSafelyGlobalVariable(INPUT_SERVER, 'REQUEST_URI'); + + if ($withoutHost) { + return $effect; + } + + return self::getServerNameOrIp(true) . $effect; + } + + /** + * Returns server name or IP address + * + * @param bool $withProtocol (optional) If is set to true, protocol name is included. Otherwise isn't. + * @return string + */ + public static function getServerNameOrIp($withProtocol = false) + { + $protocol = ''; + + if ($withProtocol) { + $protocol .= self::getProtocolName() . '://'; + } + + return $protocol . Miscellaneous::getSafelyGlobalVariable(INPUT_SERVER, 'HTTP_HOST'); + } + + /** + * Returns protocol name + * + * @return string + */ + public static function getProtocolName() + { + $effect = ''; + + $matches = []; + $protocolData = Miscellaneous::getSafelyGlobalVariable(INPUT_SERVER, 'SERVER_PROTOCOL'); // e.g. HTTP/1.1 + $matchCount = preg_match('|(.+)\/(.+)|', $protocolData, $matches); + + /* + * $matches[1] - protocol name, e.g. HTTP + * $matches[2] - protocol version, e.g. 1.1 + */ + + if ($matchCount > 0) { + $effect = strtolower($matches[1]); + } + + return $effect; + } + + /** + * Returns http referer uri + * + * @return string + */ + public static function getRefererUri() + { + $effect = ''; + + if (filter_has_var(INPUT_SERVER, 'HTTP_REFERER')) { + $effect = Miscellaneous::getSafelyGlobalVariable(INPUT_SERVER, 'HTTP_REFERER'); + } + + return $effect; + } + + /** + * Returns user's IP address + * + * @return string + */ + public static function getUserAddressIp() + { + return Miscellaneous::getSafelyGlobalVariable(INPUT_SERVER, 'REMOTE_ADDR'); + } + + /** + * Returns name and version of user's web browser + * + * @param bool $withVersion (optional) If is set to true, version of the browser is returned too. Otherwise - + * name only. + * @return string + */ + public static function getUserWebBrowserName($withVersion = false) + { + $info = self::getUserWebBrowserInfo(); + + $knownBrowsers = [ + 'Firefox/([\d\.]+)$' => 'Mozilla Firefox', + 'OPR/([\d\.]+)$' => 'Opera', + 'Chrome/([\d\.]+)$' => 'Google Chrome', + 'Safari/([\d\.]+)$' => 'Apple Safari', + ]; + + foreach ($knownBrowsers as $pattern => $browserName) { + $matches = []; + $matchCount = preg_match(sprintf('|%s|', $pattern), $info, $matches); + + if ($matchCount > 0) { + if ($withVersion) { + $version = $matches[1]; + + return sprintf('%s %s', $browserName, $version); + } + + return $browserName; + } + } + + return ''; + } + + /** + * Returns user's web browser information + * + * @return string + * + * Examples: + * - Mozilla Firefox: + * 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:34.0) Gecko/20100101 Firefox/34.0' + * + * - Google Chrome: + * 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 + * Safari/537.36' + * + * - Opera: + * 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 + * Safari/537.36 OPR/26.0.1656.24' + * + * - Apple Safari: + * 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 + * Safari/600.2.5' + */ + public static function getUserWebBrowserInfo() + { + return Miscellaneous::getSafelyGlobalVariable(INPUT_SERVER, 'HTTP_USER_AGENT'); + } + + /** + * Returns name of user's operating system + * + * @return string + */ + public static function getUserOperatingSystemName() + { + $info = self::getUserWebBrowserInfo(); + + $knownSystems = [ + 'Linux' => 'Linux', + 'Win' => 'Windows', + 'Mac' => 'Mac OS', + ]; + + foreach ($knownSystems as $pattern => $systemName) { + $matches = []; + $matchCount = preg_match(sprintf('|%s|', $pattern), $info, $matches); + + if ($matchCount > 0) { + return $systemName; + } + } + + return ''; + } + + /** + * Returns information if running server is localhost + * + * @return bool + */ + public static function isServerLocalhost() + { + $serverNameOrIp = strtolower(self::getServerNameOrIp()); + + return in_array($serverNameOrIp, [ + 'localhost', + '127.0.0.1', + '127.0.1.1', + ]); + } + + /** + * Returns information if given url is external, from another server / domain + * + * @param string $url The url to check + * @return bool + */ + public static function isExternalUrl($url) + { + $currentUrl = self::getServerNameOrIp(true); + $url = self::replenishProtocol($url); + + return !Regex::contains($currentUrl, $url); + } + + /** + * Replenishes protocol in the given url + * + * @param string $url The url to check and replenish + * @param string $protocol (optional) The protocol which is replenished. If is empty, protocol of current request + * is used. + * @return string + */ + public static function replenishProtocol($url, $protocol = '') + { + /* + * Let's trim the url + */ + if (is_string($url)) { + $url = trim($url); + } + + /* + * Url is not provided? + * Nothing to do + */ + if (empty($url)) { + return ''; + } + + /* + * It's a valid url? + * Let's return it + */ + if (Regex::isValidUrl($url, true)) { + return $url; + } + + /* + * Protocol is not provided? + */ + if (empty($protocol)) { + $protocol = self::getProtocolName(); + } + + return sprintf('%s://%s', $protocol, $url); + } + + /** + * Returns url to resource secured by given htpasswd login and password + * + * @param string $url A path / url to some resource, e.g. page, image, css file + * @param string $user (optional) User name used to log in + * @param string $password (optional) User password used to log in + * @return string + */ + public static function getSecuredUrl($url, $user = '', $password = '') + { + $protocol = self::getProtocolName(); + $host = self::getServerNameOrIp(); + + if (!Regex::startsWith($url, '/')) { + $url = sprintf('/%s', $url); + } + + $url = $host . $url; + + if (!empty($user) && !empty($password)) { + $url = sprintf('%s:%s@%s', $user, $password, $url); + } + + return sprintf('%s://%s', $protocol, $url); + } + + /** + * Adds protocol to given url, if the url does not contain given protocol. + * Returns the new url. + * + * @param string $url Url string + * @param string $protocol (optional) Protocol string + * @return string + */ + public static function addProtocolToUrl($url, $protocol = 'http') + { + $pattern = sprintf('/^%s.*/', $protocol); + + if ((bool)preg_match($pattern, $url)) { + return $url; + } + + return sprintf('%s://%s', $protocol, $url); + } +} diff --git a/src/Meritoo/Common/Utilities/Xml.php b/src/Meritoo/Common/Utilities/Xml.php new file mode 100644 index 0000000..60882d4 --- /dev/null +++ b/src/Meritoo/Common/Utilities/Xml.php @@ -0,0 +1,54 @@ + + * @copyright Meritoo.pl + */ +class Xml +{ + /** + * Merges nodes of given elements. + * Returns merged instance of SimpleXMLElement. + * + * @param SimpleXMLElement $element1 First element to merge + * @param SimpleXMLElement $element2 Second element to merge + * @return SimpleXMLElement + */ + public static function mergeNodes(SimpleXMLElement $element1, SimpleXMLElement $element2) + { + $document1 = new DOMDocument(); + $document2 = new DOMDocument(); + + $document1->loadXML($element1->asXML()); + $document2->loadXML($element2->asXML()); + + $path = new DOMXPath($document2); + $query = $path->query('/*/*'); + $nodesCount = $query->length; + + if ($nodesCount == 0) { + return $element1; + } + + for ($i = 0; $i < $nodesCount; ++$i) { + $node = $document1->importNode($query->item($i), true); + $document1->documentElement->appendChild($node); + } + + return simplexml_import_dom($document1); + } +} diff --git a/tests/Meritoo/Common/Tests/Exception/Base/UnknownTypeExceptionTest.php b/tests/Meritoo/Common/Tests/Exception/Base/UnknownTypeExceptionTest.php new file mode 100644 index 0000000..3f957f6 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Exception/Base/UnknownTypeExceptionTest.php @@ -0,0 +1,90 @@ + + * @copyright Meritoo.pl + */ +class UnknownTypeExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testWithoutException() + { + self::assertEquals('Test 2', (new TestService())->getTranslatedType('test_2')); + } + + public function testTheException() + { + $this->expectException(UnknownTestTypeException::class); + self::assertEmpty((new TestService())->getTranslatedType('test_3')); + } +} + +/** + * Type of something (for testing purposes) + * + * @author Krzysztof Niziol + * @copyright Meritoo.pl + */ +class TestType extends BaseType +{ + const TEST_1 = 'test_1'; + + const TEST_2 = 'test_2'; +} + +/** + * An exception used while type of something is unknown (for testing purposes) + * + * @author Krzysztof Niziol + * @copyright Meritoo.pl + */ +class UnknownTestTypeException extends UnknownTypeException +{ + /** + * Class constructor + * + * @param int|string $unknownType The unknown type of something (for testing purposes) + */ + public function __construct($unknownType) + { + parent::__construct($unknownType, new TestType(), 'type of something used for testing'); + } +} + +/** + * Service used together with type of something (for testing purposes) + * + * @author Krzysztof Niziol + * @copyright Meritoo.pl + */ +class TestService +{ + /** + * Returns translated type (for testing purposes) + * + * @param string $type Type of something (for testing purposes) + * @return string + * + * @throws UnknownTestTypeException + */ + public function getTranslatedType($type) + { + if ((new TestType())->isCorrectType($type)) { + return ucfirst(str_replace('_', ' ', $type)); + } + + throw new UnknownTestTypeException($type); + } +} diff --git a/tests/Meritoo/Common/Tests/Type/Base/BaseTypeTest.php b/tests/Meritoo/Common/Tests/Type/Base/BaseTypeTest.php new file mode 100644 index 0000000..0575bbb --- /dev/null +++ b/tests/Meritoo/Common/Tests/Type/Base/BaseTypeTest.php @@ -0,0 +1,199 @@ + + * @copyright Meritoo.pl + */ +class BaseTypeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @param BaseType $type Type of something + * @param array $expectedTypes Expected concrete types of given instance of type + * + * @dataProvider provideType + */ + public function testGetAll(BaseType $type, array $expectedTypes) + { + $all = $type->getAll(); + self::assertEquals($expectedTypes, $all); + } + + /** + * @param BaseType $type Type of something + * @param string $toVerifyType Concrete type to verify (of given instance of type) + * @param bool $isCorrect Expected information if given type is correct + * + * @dataProvider provideTypeWithConcreteType + */ + public function testIsCorrectType(BaseType $type, $toVerifyType, $isCorrect) + { + self::assertEquals($isCorrect, $type->isCorrectType($toVerifyType)); + } + + /** + * Provides type of something for testing the getAll() method + * + * @return Generator + */ + public function provideType() + { + yield[ + new TestEmptyType(), + [], + ]; + + yield[ + new TestType(), + [ + 'TEST_1' => TestType::TEST_1, + 'TEST_2' => TestType::TEST_2, + ], + ]; + } + + /** + * Provides type of something for testing the isCorrectType() method + * + * @return Generator + */ + public function provideTypeWithConcreteType() + { + yield[ + new TestEmptyType(), + null, + false, + ]; + + yield[ + new TestEmptyType(), + false, + false, + ]; + + yield[ + new TestEmptyType(), + true, + false, + ]; + + yield[ + new TestEmptyType(), + '', + false, + ]; + + yield[ + new TestEmptyType(), + 0, + false, + ]; + + yield[ + new TestEmptyType(), + 1, + false, + ]; + + yield[ + new TestEmptyType(), + 'lorem', + false, + ]; + + yield[ + new TestType(), + null, + false, + ]; + + yield[ + new TestType(), + false, + false, + ]; + + yield[ + new TestType(), + true, + false, + ]; + + yield[ + new TestType(), + '', + false, + ]; + + yield[ + new TestType(), + 0, + false, + ]; + + yield[ + new TestType(), + 1, + false, + ]; + + yield[ + new TestType(), + 'lorem', + false, + ]; + + yield[ + new TestType(), + 'test', + false, + ]; + + yield[ + new TestType(), + TestType::TEST_1, + true, + ]; + + yield[ + new TestType(), + TestType::TEST_2, + true, + ]; + } +} + +/** + * Empty type of something used for testing + * + * @author Krzysztof Niziol + * @copyright Meritoo.pl + */ +class TestEmptyType extends BaseType +{ +} + +/** + * Type of something used for testing + * + * @author Krzysztof Niziol + * @copyright Meritoo.pl + */ +class TestType extends BaseType +{ + const TEST_1 = 'test_1'; + + const TEST_2 = 'test_2'; +} diff --git a/tests/Meritoo/Common/Tests/Type/DatePartTypeTest.php b/tests/Meritoo/Common/Tests/Type/DatePartTypeTest.php new file mode 100644 index 0000000..99bb694 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Type/DatePartTypeTest.php @@ -0,0 +1,106 @@ + + * @copyright Meritoo.pl + */ +class DatePartTypeTest extends \PHPUnit_Framework_TestCase +{ + public function testGetAll() + { + $expectedTypes = [ + 'DAY' => DatePartType::DAY, + 'HOUR' => DatePartType::HOUR, + 'MINUTE' => DatePartType::MINUTE, + 'MONTH' => DatePartType::MONTH, + 'SECOND' => DatePartType::SECOND, + 'YEAR' => DatePartType::YEAR, + ]; + + $all = (new DatePartType())->getAll(); + self::assertEquals($expectedTypes, $all); + } + + /** + * @param string $toVerifyType Concrete type to verify (of given instance of type) + * @param bool $isCorrect Expected information if given type is correct + * + * @dataProvider provideConcreteType + */ + public function testIsCorrectType($toVerifyType, $isCorrect) + { + $type = new DatePartType(); + self::assertEquals($isCorrect, $type->isCorrectType($toVerifyType)); + } + + /** + * Provides type of something for testing the isCorrectType() method + * + * @return Generator + */ + public function provideConcreteType() + { + yield[ + '', + false, + ]; + + yield[ + null, + false, + ]; + + yield[ + 0, + false, + ]; + + yield[ + 1, + false, + ]; + + yield[ + 'day', + true, + ]; + + yield[ + 'hour', + true, + ]; + + yield[ + 'minute', + true, + ]; + + yield[ + 'month', + true, + ]; + + yield[ + 'second', + true, + ]; + + yield[ + 'year', + true, + ]; + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/ArraysTest.php b/tests/Meritoo/Common/Tests/Utilities/ArraysTest.php new file mode 100644 index 0000000..52efad9 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/ArraysTest.php @@ -0,0 +1,1880 @@ + + * @copyright Meritoo.pl + */ +class ArraysTest extends \PHPUnit_Framework_TestCase +{ + private $simpleArray; + private $simpleArrayWithKeys; + private $twoDimensionsArray; + private $complexArray; + private $superComplexArray; + + public function testValues2string() + { + /* + * Simple array / string + */ + $simpleString = 'Lorem,ipsum,dolor,sit,amet'; + $simpleStringWithDots = str_replace(',', '.', $simpleString); + + self::assertEquals($simpleString, Arrays::values2string($this->simpleArray)); + self::assertEquals('ipsum', Arrays::values2string($this->simpleArray, 1)); + self::assertEquals($simpleStringWithDots, Arrays::values2string($this->simpleArray, '', '.')); + + /* + * Complex array / string + */ + $complexString = 'sit,egestas,adipiscing,1234,,donec,quis,elit,iaculis,primis'; + $complexStringWithDots = str_replace(',', '.', $complexString); + + self::assertEquals($complexString, Arrays::values2string($this->complexArray)); + self::assertEquals($complexStringWithDots, Arrays::values2string($this->complexArray, '', '.')); + + /* + * Other cases + */ + self::assertEquals('', Arrays::values2string([])); + } + + public function testValuesKeys2string() + { + self::assertEquals('0=Lorem,1=ipsum,2=dolor,3=sit,4=amet', Arrays::valuesKeys2string($this->simpleArray)); + self::assertEquals('0=Lorem;1=ipsum;2=dolor;3=sit;4=amet', Arrays::valuesKeys2string($this->simpleArray, ';')); + self::assertEquals('0=Lorem 1=ipsum 2=dolor 3=sit 4=amet', Arrays::valuesKeys2string($this->simpleArray, ' ')); + + self::assertEquals('0="Lorem" 1="ipsum" 2="dolor" 3="sit" 4="amet"', Arrays::valuesKeys2string($this->simpleArray, ' ', '=', '"')); + self::assertEquals('0="Lorem", 1="ipsum", 2="dolor", 3="sit", 4="amet"', Arrays::valuesKeys2string($this->simpleArray, ', ', '=', '"')); + } + + public function testValues2csv() + { + self::assertEquals('', Arrays::values2csv($this->simpleArray)); + + self::assertEquals("lorem,ipsum,dolor,sit,amet\n" + . "consectetur,adipiscing,elit\n" + . 'donec,sagittis,fringilla,eleifend', Arrays::values2csv($this->twoDimensionsArray)); + } + + public function testGetFirstKey() + { + /* + * Negative cases + */ + self::assertNull(Arrays::getFirstKey([])); + + /* + * Positive cases + */ + self::assertEquals(0, Arrays::getFirstKey($this->simpleArray)); + self::assertEquals('lorem', Arrays::getFirstKey($this->complexArray)); + } + + public function testGetLastKey() + { + self::assertEquals(4, Arrays::getLastKey($this->simpleArray)); + self::assertEquals('amet', Arrays::getLastKey($this->complexArray)); + } + + public function testGetFirstElement() + { + /* + * Negative cases + */ + self::assertNull(Arrays::getFirstElement([])); + + /* + * Positive cases + */ + self::assertEquals('Lorem', Arrays::getFirstElement($this->simpleArray)); + self::assertEquals('Lorem', Arrays::getFirstElement($this->simpleArray)); + self::assertEquals('lorem', Arrays::getFirstElement($this->twoDimensionsArray, false)); + self::assertEquals('sit', Arrays::getFirstElement($this->complexArray, false)); + } + + public function testIsFirstElement() + { + self::assertTrue(Arrays::isFirstElement($this->simpleArray, 'Lorem')); + self::assertFalse(Arrays::isFirstElement($this->simpleArray, 'dolor')); + self::assertFalse(Arrays::isFirstElement($this->simpleArray, ' ')); + self::assertFalse(Arrays::isFirstElement($this->simpleArray, null)); + } + + public function testGetLastElement() + { + /* + * Negative cases + */ + self::assertNull(Arrays::getLastElement([])); + + /* + * Positive cases + */ + self::assertEquals('amet', Arrays::getLastElement($this->simpleArray)); + self::assertEquals('eleifend', Arrays::getLastElement($this->twoDimensionsArray, false)); + self::assertEquals('primis', Arrays::getLastElement($this->complexArray, false)); + } + + public function testIsLastElement() + { + self::assertTrue(Arrays::isLastElement($this->simpleArray, 'amet')); + self::assertFalse(Arrays::isLastElement($this->simpleArray, 'ipsum')); + self::assertFalse(Arrays::isLastElement($this->simpleArray, '')); + self::assertFalse(Arrays::isLastElement($this->simpleArray, null)); + } + + public function testGetLastElementBreadCrumb() + { + self::assertEquals('4/amet', Arrays::getLastElementBreadCrumb($this->simpleArray)); + self::assertEquals('2/3/eleifend', Arrays::getLastElementBreadCrumb($this->twoDimensionsArray)); + self::assertEquals('amet/1/primis', Arrays::getLastElementBreadCrumb($this->complexArray)); + } + + public function testGetLastRow() + { + /* + * Negative cases + */ + self::assertNull(Arrays::getLastRow([])); + + /* + * Positive cases + */ + self::assertEquals([], Arrays::getLastRow($this->simpleArray)); + self::assertEquals([], Arrays::getLastRow($this->simpleArrayWithKeys)); + + self::assertEquals([ + 'donec', + 'sagittis', + 'fringilla', + 'eleifend', + ], Arrays::getLastRow($this->twoDimensionsArray)); + + self::assertEquals([ + 'iaculis', + 'primis', + ], Arrays::getLastRow($this->complexArray)); + } + + public function testReplaceArrayKeys() + { + $effect = [ + 'nullam' => 'donec', + 'x' => [ + 'vitae' => [ + 'x' => 'quis', + ], + ], + 'elit', + ]; + + $dataArray = $this->complexArray['sit']; + self::assertEquals($effect, Arrays::replaceArrayKeys($dataArray, '|.*li.*|', 'x')); + + self::assertEquals([ + 'x' => 'sit', + 4 => 'amet', + ], Arrays::replaceArrayKeys($this->simpleArray, '|[0-3]+|', 'x')); + } + + public function testMakeArray() + { + self::assertSame($this->simpleArray, Arrays::makeArray($this->simpleArray)); + self::assertSame(['test'], Arrays::makeArray('test')); + } + + public function testArray2JavaScript() + { + /* + * Negative cases + */ + self::assertNull(Arrays::array2JavaScript([])); + + /* + * Positive cases + */ + self::assertEquals('new Array(\'Lorem\', \'ipsum\', \'dolor\', \'sit\', \'amet\');', Arrays::array2JavaScript($this->simpleArray)); + self::assertEquals('var letsTest = new Array(\'Lorem\', \'ipsum\', \'dolor\', \'sit\', \'amet\');', Arrays::array2JavaScript($this->simpleArray, 'letsTest')); + + $effect = 'var letsTest = new Array(5); +letsTest[0] = \'Lorem\'; +letsTest[1] = \'ipsum\'; +letsTest[2] = \'dolor\'; +letsTest[3] = \'sit\'; +letsTest[4] = \'amet\';'; + + self::assertEquals($effect, Arrays::array2JavaScript($this->simpleArray, 'letsTest', true)); + + self::assertEquals('new Array(\'ipsum\', \'sit\', \'consectetur\');', Arrays::array2JavaScript($this->simpleArrayWithKeys)); + self::assertEquals('var letsTest = new Array(\'ipsum\', \'sit\', \'consectetur\');', Arrays::array2JavaScript($this->simpleArrayWithKeys, 'letsTest')); + + $effect = 'var letsTest = new Array(3); +letsTest[\'Lorem\'] = \'ipsum\'; +letsTest[\'dolor\'] = \'sit\'; +letsTest[\'amet\'] = \'consectetur\';'; + + self::assertEquals($effect, Arrays::array2JavaScript($this->simpleArrayWithKeys, 'letsTest', true)); + + $effect = 'var autoGeneratedVariable = new Array(3); +var value_0 = new Array(\'lorem\', \'ipsum\', \'dolor\', \'sit\', \'amet\'); +autoGeneratedVariable[0] = value_0; +var value_1 = new Array(\'consectetur\', \'adipiscing\', \'elit\'); +autoGeneratedVariable[1] = value_1; +var value_2 = new Array(\'donec\', \'sagittis\', \'fringilla\', \'eleifend\'); +autoGeneratedVariable[2] = value_2;'; + + self::assertEquals($effect, Arrays::array2JavaScript($this->twoDimensionsArray)); + + $effect = 'var letsTest = new Array(3); +var value_0 = new Array(\'lorem\', \'ipsum\', \'dolor\', \'sit\', \'amet\'); +letsTest[0] = value_0; +var value_1 = new Array(\'consectetur\', \'adipiscing\', \'elit\'); +letsTest[1] = value_1; +var value_2 = new Array(\'donec\', \'sagittis\', \'fringilla\', \'eleifend\'); +letsTest[2] = value_2;'; + + self::assertEquals($effect, Arrays::array2JavaScript($this->twoDimensionsArray, 'letsTest')); + + $effect = 'var letsTest = new Array(3); +var value_0 = new Array(5); +value_0[0] = \'lorem\'; +value_0[1] = \'ipsum\'; +value_0[2] = \'dolor\'; +value_0[3] = \'sit\'; +value_0[4] = \'amet\'; +letsTest[0] = value_0; +var value_1 = new Array(3); +value_1[0] = \'consectetur\'; +value_1[1] = \'adipiscing\'; +value_1[2] = \'elit\'; +letsTest[1] = value_1; +var value_2 = new Array(4); +value_2[0] = \'donec\'; +value_2[1] = \'sagittis\'; +value_2[2] = \'fringilla\'; +value_2[3] = \'eleifend\'; +letsTest[2] = value_2;'; + + self::assertEquals($effect, Arrays::array2JavaScript($this->twoDimensionsArray, 'letsTest', true)); + } + + public function testRemoveMarginalElement() + { + $array = $this->simpleArray; + $string = 'Lorem ipsum'; + + /* + * Removing first element + */ + self::assertSame([ + 0 => 'Lorem', + 1 => 'ipsum', + 2 => 'dolor', + 3 => 'sit', + ], Arrays::removeMarginalElement($array)); + self::assertEquals('Lorem ipsu', Arrays::removeMarginalElement($string)); + + /* + * Removing last element + */ + self::assertSame([ + 1 => 'ipsum', + 2 => 'dolor', + 3 => 'sit', + 4 => 'amet', + ], Arrays::removeMarginalElement($array, false)); + self::assertEquals('orem ipsum', Arrays::removeMarginalElement($string, false)); + } + + public function testRemoveElements() + { + $array1 = $this->simpleArray; + $array2 = $this->simpleArray; + + Arrays::removeElements($array1, 'ipsum'); + self::assertSame([ + 1 => 'ipsum', + 2 => 'dolor', + 3 => 'sit', + 4 => 'amet', + ], $array1); + + Arrays::removeElements($array2, 'sit', false); + self::assertSame([ + 0 => 'Lorem', + 1 => 'ipsum', + 2 => 'dolor', + 3 => 'sit', + ], $array2); + + Arrays::removeElements($this->complexArray['lorem'], 'sit', false); + self::assertSame(['ipsum' => ['dolor' => 'sit']], $this->complexArray['lorem']); + } + + public function testRemoveElement() + { + self::assertFalse(Arrays::removeElement($this->simpleArray, 'eeee')); + self::assertTrue(is_array(Arrays::removeElement($this->simpleArray, 'Lorem'))); + + Arrays::removeElement($this->simpleArray, 'amet'); + self::assertFalse(isset($this->simpleArray['amet'])); + } + + public function testSetKeysAsValuesEmptyArray() + { + self::assertEquals([], Arrays::setKeysAsValues([])); + } + + public function testSetKeysAsValuesSameKeysValues() + { + $array = [ + 0, + 1, + 2, + 3, + ]; + + self::assertEquals($array, Arrays::setKeysAsValues($array)); + } + + /** + * @param array $array The array to change values with keys + * @param array $replaced The array with replaced values with keys + * + * @dataProvider provideSimpleArrayToSetKeysAsValues + */ + public function testSetKeysAsValuesSimpleArray($array, $replaced) + { + self::assertEquals($replaced, Arrays::setKeysAsValues($array)); + } + + public function testSetKeysAsValuesTwoDimensionsArray() + { + $replaced = [ + [ + 'lorem' => 0, + 'ipsum' => 1, + 'dolor' => 2, + 'sit' => 3, + 'amet' => 4, + ], + [ + 'consectetur' => 0, + 'adipiscing' => 1, + 'elit' => 2, + ], + [ + 'donec' => 0, + 'sagittis' => 1, + 'fringilla' => 2, + 'eleifend' => 3, + ], + ]; + + self::assertEquals($replaced, Arrays::setKeysAsValues($this->twoDimensionsArray)); + } + + /** + * @param array $array The array to change values with keys + * @param array $replaced The array with replaced values with keys + * + * @dataProvider provideArrayWithDuplicatedValuesToSetKeysAsValues + */ + public function testSetKeysAsValuesDuplicatedValues($array, $replaced) + { + self::assertEquals($replaced, Arrays::setKeysAsValues($array, false)); + } + + public function testGetNonArrayElementsCount() + { + /* + * Negative cases + */ + self::assertNull(Arrays::getNonArrayElementsCount([])); + + /* + * Positive cases + */ + self::assertEquals(5, Arrays::getNonArrayElementsCount($this->simpleArray)); + self::assertEquals(3, Arrays::getNonArrayElementsCount($this->simpleArrayWithKeys)); + self::assertEquals(12, Arrays::getNonArrayElementsCount($this->twoDimensionsArray)); + } + + public function testString2array() + { + /* + * Negative cases + */ + self::assertNull(Arrays::string2array('')); + self::assertNull(Arrays::string2array(null)); + + /* + * Positive cases + */ + $array = [ + 'light' => '#fff', + 'dark' => '#000', + ]; + + self::assertEquals($array, Arrays::string2array('light:#fff|dark:#000')); + self::assertEquals($array, Arrays::string2array('light: #fff | dark: #000')); + + $array = [ + 'red' => '#f00', + 'green' => '#0f0', + 'blue' => '#00f', + ]; + + self::assertEquals($array, Arrays::string2array('red:#f00|green:#0f0|blue:#00f')); + self::assertEquals($array, Arrays::string2array('red: #f00 | green: #0f0 | blue: #00f')); + self::assertEquals($array, Arrays::string2array('red : #f00 | green : #0f0 | blue : #00f')); + } + + public function testAreKeysInArray() + { + /* + * Negative cases + */ + self::assertFalse(Arrays::areKeysInArray([null], $this->simpleArray)); + self::assertFalse(Arrays::areKeysInArray([''], $this->simpleArray)); + self::assertFalse(Arrays::areKeysInArray(['dolorrr'], $this->simpleArrayWithKeys)); + + $keys1 = [ + 1, + 3, + 9, + ]; + + self::assertFalse(Arrays::areKeysInArray($keys1, $this->simpleArray)); + self::assertFalse(Arrays::areKeysInArray($keys1, $this->complexArray)); + self::assertFalse(Arrays::areKeysInArray($keys1, $this->complexArray, false)); + + /* + * Positive cases + */ + $keys12 = [ + 2, + 'mollis', + ]; + + $keys13 = [ + 1, + 3, + ]; + + $keys14 = [ + 'dolor', + 'amet', + ]; + + $keys15 = [ + 'dolor', + 'amet', + ]; + + $keys16 = [ + 'a' => 'lorem', + 11 => 'amet', + ]; + + $keys17 = [ + 'a' => 'lorem', + 11 => 'amet', + 'c' => 'sit__', + ]; + + self::assertTrue(Arrays::areKeysInArray([1], $this->simpleArray)); + self::assertTrue(Arrays::areKeysInArray($keys12, $this->simpleArray, false)); + self::assertTrue(Arrays::areKeysInArray($keys12, $this->complexArray)); + self::assertTrue(Arrays::areKeysInArray($keys13, $this->simpleArray)); + self::assertTrue(Arrays::areKeysInArray($keys14, $this->simpleArrayWithKeys)); + self::assertTrue(Arrays::areKeysInArray($keys15, $this->simpleArrayWithKeys)); + + self::assertTrue(Arrays::areKeysInArray(['a' => 'dolor'], $this->simpleArrayWithKeys)); + self::assertTrue(Arrays::areKeysInArray(['a' => 'dolor'], $this->simpleArrayWithKeys, false)); + + self::assertTrue(Arrays::areKeysInArray($keys16, $this->complexArray)); + self::assertTrue(Arrays::areKeysInArray($keys17, $this->complexArray, false)); + } + + public function testGetLastElementsPaths() + { + /* + * Using default separator and other default arguments + */ + $expected = [ + 'lorem.ipsum.dolor' => 'sit', + 'lorem.ipsum.diam.non' => 'egestas', + 'consectetur' => 'adipiscing', + 'mollis' => 1234, + 2 => [], + 'sit.nullam' => 'donec', + 'sit.aliquet.vitae.ligula' => 'quis', + 'sit.0' => 'elit', + 'amet.0' => 'iaculis', + 'amet.1' => 'primis', + ]; + + self::assertEquals($expected, Arrays::getLastElementsPaths($this->complexArray)); + + /* + * Using custom separator + */ + $separator = ' -> '; + $expected = [ + sprintf('lorem%sipsum%sdolor', $separator, $separator) => 'sit', + sprintf('lorem%sipsum%sdiam%snon', $separator, $separator, $separator) => 'egestas', + 'consectetur' => 'adipiscing', + 'mollis' => 1234, + 2 => [], + sprintf('sit%snullam', $separator) => 'donec', + sprintf('sit%saliquet%svitae%sligula', $separator, $separator, $separator) => 'quis', + sprintf('sit%s0', $separator) => 'elit', + sprintf('amet%s0', $separator) => 'iaculis', + sprintf('amet%s1', $separator) => 'primis', + ]; + + self::assertEquals($expected, Arrays::getLastElementsPaths($this->complexArray, $separator)); + + /* + * Special exception: do not use, stop recursive on the "diam" key + */ + $expected = [ + 'lorem.ipsum.dolor' => 'sit', + 'consectetur' => 'adipiscing', + 'mollis' => 1234, + 2 => [], + 'sit.nullam' => 'donec', + 'sit.aliquet.vitae.ligula' => 'quis', + 'sit.0' => 'elit', + 'amet.0' => 'iaculis', + 'amet.1' => 'primis', + 'lorem.ipsum.diam' => [ + 'non' => 'egestas', + ], + ]; + + $stopIfMatchedBy = 'diam'; + self::assertEquals($expected, Arrays::getLastElementsPaths($this->complexArray, '.', '', $stopIfMatchedBy)); + + /* + * Stop building of paths on these keys: + * - "diam" + * - "aliquet" + */ + $expected = [ + 'lorem . ipsum . dolor' => 'sit', + 'consectetur' => 'adipiscing', + 'mollis' => 1234, + 2 => [], + 'sit . nullam' => 'donec', + 'sit . 0' => 'elit', + 'amet . 0' => 'iaculis', + 'amet . 1' => 'primis', + 'lorem . ipsum . diam' => [ + 'non' => 'egestas', + ], + 'sit . aliquet' => [ + 'vitae' => [ + 'ligula' => 'quis', + ], + ], + ]; + + $stopIfMatchedBy = [ + 'diam', + 'aliquet', + ]; + + self::assertEquals($expected, Arrays::getLastElementsPaths($this->complexArray, ' . ', '', $stopIfMatchedBy)); + + $expected = [ + 'ipsum > quis > vestibulum > porta-1' => [ + 'turpis', + 'urna', + ], + 'ipsum > quis > vestibulum > porta-2' => [ + 'tortor' => [ + 'in' => [ + 'dui', + 'dolor' => [ + 'aliquam', + ], + ], + ], + ], + 'ipsum > quis > vestibulum > porta-3' => [ + 1, + 2, + 3, + ], + 'primis > 0' => [ + 'in', + 'faucibus', + 'orci', + ], + 'primis > 1' => [ + 'luctus', + 'et', + 'ultrices', + ], + ]; + + /* + * Stop building of paths on more sophisticated keys + */ + $stopIfMatchedBy = [ + 'porta\-\d+', + '^\d+$', + ]; + + self::assertEquals($expected, Arrays::getLastElementsPaths($this->superComplexArray, ' > ', '', $stopIfMatchedBy)); + + /* + * Stop building of paths on these: + * - keys + * and + * - paths (verify paths too) + */ + $expected = [ + 'lorem > ipsum > dolor' => 'sit', + 'consectetur' => 'adipiscing', + 'mollis' => 1234, + 2 => [], + 'sit > nullam' => 'donec', + 'sit > 0' => 'elit', + 'amet > 0' => 'iaculis', + 'amet > 1' => 'primis', + 'lorem > ipsum > diam' => [ + 'non' => 'egestas', + ], + 'sit > aliquet > vitae' => [ + 'ligula' => 'quis', + ], + ]; + + $stopIfMatchedBy = [ + 'diam', + 'sit > aliquet > vitae', + ]; + + self::assertEquals($expected, Arrays::getLastElementsPaths($this->complexArray, ' > ', '', $stopIfMatchedBy)); + + /* + * Stop building of paths on these paths (verify paths only) + */ + $expected = [ + 'ipsum > quis > vestibulum > porta-1' => [ + 'turpis', + 'urna', + ], + 'ipsum > quis > vestibulum > porta-2 > tortor' => [ + 'in' => [ + 'dui', + 'dolor' => [ + 'aliquam', + ], + ], + ], + 'ipsum > quis > vestibulum > porta-3 > 0' => 1, + 'ipsum > quis > vestibulum > porta-3 > 1' => 2, + 'ipsum > quis > vestibulum > porta-3 > 2' => 3, + 'primis > 0 > 0' => 'in', + 'primis > 0 > 1' => 'faucibus', + 'primis > 0 > 2' => 'orci', + 'primis > 1' => [ + 'luctus', + 'et', + 'ultrices', + ], + ]; + + $stopIfMatchedBy = [ + 'ipsum > quis > vestibulum > porta-1', + 'ipsum > quis > vestibulum > porta-2 > tortor', + 'primis > 1', + ]; + + self::assertEquals($expected, Arrays::getLastElementsPaths($this->superComplexArray, ' > ', '', $stopIfMatchedBy)); + + /* + * Stop building of paths if path contains any of these part (verify part of paths only) + */ + $expected = [ + 'ipsum > quis > vestibulum > porta-1' => [ + 'turpis', + 'urna', + ], + 'ipsum > quis > vestibulum > porta-2 > tortor > in' => [ + 'dui', + 'dolor' => [ + 'aliquam', + ], + ], + 'ipsum > quis > vestibulum > porta-3 > 0' => 1, + 'ipsum > quis > vestibulum > porta-3 > 1' => 2, + 'ipsum > quis > vestibulum > porta-3 > 2' => 3, + 'primis > 0' => [ + 'in', + 'faucibus', + 'orci', + ], + 'primis > 1' => [ + 'luctus', + 'et', + 'ultrices', + ], + ]; + + $stopIfMatchedBy = [ + 'vestibulum > porta-1', + 'tortor > in', + '[a-z]+ > \d+', + ]; + + self::assertEquals($expected, Arrays::getLastElementsPaths($this->superComplexArray, ' > ', '', $stopIfMatchedBy)); + } + + public function testAreAllKeysMatchedByPattern() + { + $pattern = '\d+'; + + /* + * Complex array with strings and integers as keys + */ + self::assertFalse(Arrays::areAllKeysMatchedByPattern($this->complexArray, $pattern)); + + /* + * Simple array with integers as keys only + */ + self::assertTrue(Arrays::areAllKeysMatchedByPattern($this->simpleArray, $pattern)); + + /* + * Empty array + */ + self::assertFalse(Arrays::areAllKeysMatchedByPattern([], $pattern)); + + $array = [ + 'a' => 'b', + 'c' => 'd', + ]; + + /* + * Yet another simple array, but with strings as keys + */ + self::assertFalse(Arrays::areAllKeysMatchedByPattern($array, $pattern)); + + /* + * The same array with another pattern + */ + $pattern = '\w+'; + self::assertTrue(Arrays::areAllKeysMatchedByPattern($array, $pattern)); + + /* + * The same array with mixed keys (strings and integers as keys) + */ + $array[1] = 'x'; + $pattern = '\d+'; + self::assertFalse(Arrays::areAllKeysMatchedByPattern($array, $pattern)); + + /* + * Multidimensional array - negative case + */ + $array['e'] = ['f' => 'g']; + self::assertFalse(Arrays::areAllKeysMatchedByPattern($array, $pattern)); + + /* + * Multidimensional array - positive case + */ + unset($array[1]); + $pattern = '\w+'; + self::assertTrue(Arrays::areAllKeysMatchedByPattern($array, $pattern)); + } + + public function testAreAllKeysIntegers() + { + self::assertEquals(1, Arrays::areAllKeysIntegers($this->simpleArray)); + self::assertEquals(2, Arrays::areAllKeysIntegers($this->simpleArray)); + self::assertEquals('', Arrays::areAllKeysIntegers($this->complexArray)); + } + + public function testIssetRecursive() + { + /* + * Negative cases + */ + self::assertFalse(Arrays::issetRecursive([], [])); + + /* + * Positive cases + */ + $unExistingKeys = [ + 'a', + 'b', + 3, + ]; + + $existingKeys = [ + 'simpleArray' => [ + 1, + 3, + 4, + ], + 'simpleArrayWithKeys' => [ + 'dolor', + 'amet', + ], + 'twoDimensionsArray' => [ + 2, + 3, + ], + 'complexArray' => [ + 'sit', + 'aliquet', + 'vitae', + ], + ]; + + self::assertFalse(Arrays::issetRecursive($this->simpleArray, $unExistingKeys)); + self::assertTrue(Arrays::issetRecursive($this->simpleArray, $existingKeys['simpleArray'])); + + self::assertFalse(Arrays::issetRecursive($this->simpleArrayWithKeys, $unExistingKeys)); + self::assertTrue(Arrays::issetRecursive($this->simpleArrayWithKeys, $existingKeys['simpleArrayWithKeys'])); + + self::assertFalse(Arrays::issetRecursive($this->twoDimensionsArray, $unExistingKeys)); + self::assertTrue(Arrays::issetRecursive($this->twoDimensionsArray, $existingKeys['twoDimensionsArray'])); + + self::assertFalse(Arrays::issetRecursive($this->complexArray, $unExistingKeys)); + self::assertTrue(Arrays::issetRecursive($this->complexArray, $existingKeys['complexArray'])); + } + + public function testGetValueByKeysPath() + { + /* + * Negative cases + */ + self::assertNull(Arrays::getValueByKeysPath([], [])); + + /* + * Positive cases + */ + self::assertNull(Arrays::getValueByKeysPath($this->simpleArray, [])); + self::assertEquals('ipsum', Arrays::getValueByKeysPath($this->simpleArray, [1])); + self::assertEquals('sit', Arrays::getValueByKeysPath($this->simpleArray, [3])); + self::assertEquals('sit', Arrays::getValueByKeysPath($this->simpleArrayWithKeys, ['dolor'])); + + $keys = [ + 'twoDimensionsArray' => [ + 2, + 2, + ], + 'complexArray' => [ + [ + 'lorem', + 'ipsum', + 'diam', + ], + [ + 'sit', + 'aliquet', + ], + ], + ]; + + $value = [ + [ + 'non' => 'egestas', + ], + [ + 'vitae' => [ + 'ligula' => 'quis', + ], + ], + ]; + + self::assertEquals('fringilla', Arrays::getValueByKeysPath($this->twoDimensionsArray, $keys['twoDimensionsArray'])); + self::assertEquals($value[0], Arrays::getValueByKeysPath($this->complexArray, $keys['complexArray'][0])); + self::assertEquals($value[1], Arrays::getValueByKeysPath($this->complexArray, $keys['complexArray'][1])); + } + + public function testGetAllValuesOfKeyEmptyValue() + { + self::assertNull(Arrays::getAllValuesOfKey([], '')); + self::assertNull(Arrays::getAllValuesOfKey([], 'test')); + } + + public function testGetAllValuesOfKeyNotExistingKey() + { + self::assertNull(Arrays::getAllValuesOfKey([], 'ipsum')); + self::assertEquals([], Arrays::getAllValuesOfKey($this->simpleArray, 'ipsum')); + } + + public function testGetAllValuesOfKey() + { + /* + * Positive case - 1-dimension array + */ + self::assertEquals(['ipsum'], Arrays::getAllValuesOfKey($this->simpleArray, 1)); + + /* + * Positive case - 2-dimensions array + */ + $effect = [ + [ + 'lorem', + 'ipsum', + 'dolor', + 'sit', + 'amet', + ], + 1 => 'consectetur', + 2 => 'donec', + ]; + + self::assertEquals($effect, Arrays::getAllValuesOfKey($this->twoDimensionsArray, 0)); + + /* + * Positive case - multi-dimensions array + */ + self::assertEquals(['primis'], Arrays::getAllValuesOfKey($this->complexArray, 1)); + + /* + * Positive case - multi-dimensions array + */ + $effect = [ + 0 => [ + 'in' => [ + 'dui', + 'dolor' => [ + 'aliquam', + ], + ], + ], + ]; + + self::assertEquals($effect, Arrays::getAllValuesOfKey($this->superComplexArray, 'tortor')); + } + + public function testKsortRecursive() + { + /* + * Negative cases + */ + $array = []; + self::assertNull(Arrays::ksortRecursive($array)); + + /* + * Positive cases + */ + self::assertEquals($this->simpleArray, Arrays::ksortRecursive($this->simpleArray)); + self::assertEquals($this->simpleArray, Arrays::ksortRecursive($this->simpleArray, SORT_NUMERIC)); + self::assertEquals($this->twoDimensionsArray, Arrays::ksortRecursive($this->twoDimensionsArray)); + + /* + * Positive case - multi-dimensions array + */ + $effect = [ + 'amet' => [ + 'iaculis', + 'primis', + ], + 'consectetur' => 'adipiscing', + 'lorem' => [ + 'ipsum' => [ + 'dolor' => 'sit', + 'diam' => [ + 'non' => 'egestas', + ], + ], + ], + 'mollis' => 1234, + 'sit' => [ + 'nullam' => 'donec', + 'aliquet' => [ + 'vitae' => [ + 'ligula' => 'quis', + ], + ], + 'elit', + ], + 2 => [], + ]; + + self::assertEquals($effect, Arrays::ksortRecursive($this->complexArray)); + + /* + * Positive case - multi-dimensions array - with options of ksort() function + */ + $effect = [ + 2 => [], + 'amet' => [ + 'iaculis', + 'primis', + ], + 'consectetur' => 'adipiscing', + 'lorem' => [ + 'ipsum' => [ + 'dolor' => 'sit', + 'diam' => [ + 'non' => 'egestas', + ], + ], + ], + 'mollis' => 1234, + 'sit' => [ + 'nullam' => 'donec', + 'aliquet' => [ + 'vitae' => [ + 'ligula' => 'quis', + ], + ], + 'elit', + ], + ]; + + self::assertEquals($effect, Arrays::ksortRecursive($this->complexArray, SORT_STRING & SORT_NATURAL)); + } + + public function testSetPositions() + { + /* + * Negative cases + */ + self::assertEmpty(Arrays::setPositions([])); + self::assertEquals([], Arrays::setPositions([])); + + /* + * Positive case - 1-dimension array + */ + $array = [ + 'abc', + ]; + + self::assertEquals($array, Arrays::setPositions($array)); + + /* + * Positive case - 2-dimensions array + */ + $effect = $this->twoDimensionsArray; + $effect[0][Arrays::POSITION_KEY_NAME] = 1; + $effect[1][Arrays::POSITION_KEY_NAME] = 2; + $effect[2][Arrays::POSITION_KEY_NAME] = 3; + + self::assertEquals($effect, Arrays::setPositions($this->twoDimensionsArray)); + + /* + * Positive case - multi-level array + */ + $array = [ + 'lorem', + 'ipsum' => [ + 'dolor', + 'sit', + ], + 'amet' => [ + 'consectetur', + 'adipiscing' => [ + 'elit' => [ + 'cras', + 'quis', + 'ligula', + ], + ], + ], + ]; + + $effect = [ + 'lorem', + 'ipsum' => [ + 'dolor', + 'sit', + Arrays::POSITION_KEY_NAME => 1, + ], + 'amet' => [ + 'consectetur', + 'adipiscing' => [ + 'elit' => [ + 'cras', + 'quis', + 'ligula', + Arrays::POSITION_KEY_NAME => 1, + ], + Arrays::POSITION_KEY_NAME => 1, + ], + Arrays::POSITION_KEY_NAME => 2, + ], + ]; + + self::assertEquals($effect, Arrays::setPositions($array)); + + /* + * Positive case - non-default name of key with position value & 2-dimensions array + */ + $keyName = 'level'; + $effect = $this->twoDimensionsArray; + + $effect[0][$keyName] = 1; + $effect[1][$keyName] = 2; + $effect[2][$keyName] = 3; + + self::assertEquals($effect, Arrays::setPositions($this->twoDimensionsArray, $keyName)); + + /* + * Positive case - non-default start value of position & 2-dimensions array + */ + $startPosition = 5; + $effect = $this->twoDimensionsArray; + + $effect[Arrays::POSITION_KEY_NAME] = $startPosition; + $effect[0][Arrays::POSITION_KEY_NAME] = 1; + $effect[1][Arrays::POSITION_KEY_NAME] = 2; + $effect[2][Arrays::POSITION_KEY_NAME] = 3; + + self::assertEquals($effect, Arrays::setPositions($this->twoDimensionsArray, Arrays::POSITION_KEY_NAME, $startPosition)); + } + + public function testTrimRecursive() + { + /* + * Negative cases + */ + self::assertEquals([], Arrays::trimRecursive([])); + + /* + * Positive cases + */ + self::assertEquals(['a'], Arrays::trimRecursive([' a '])); + self::assertEquals([ + 'a', + 'b', + ], Arrays::trimRecursive([ + ' a ', + 'b ', + ])); + + $array = [ + 'abc ', + [ + 'def', + 'ghi ', + ], + ]; + + $result = [ + 'abc', + [ + 'def', + 'ghi', + ], + ]; + + self::assertEquals($result, Arrays::trimRecursive($array)); + + $array = [ + 'abc ', + [ + ' def ', + 'ghi ', + 'oo' => [ + ' ee ', + 'g' => 'h hhh ', + ], + ], + ]; + + $result = [ + 'abc', + [ + 'def', + 'ghi', + 'oo' => [ + 'ee', + 'g' => 'h hhh', + ], + ], + ]; + + self::assertEquals($result, Arrays::trimRecursive($array)); + } + + public function testSortByCustomKeysOrder() + { + /* + * Negative cases + */ + self::assertNull(Arrays::sortByCustomKeysOrder([], [])); + + /* + * Positive cases + */ + self::assertEquals([0], Arrays::sortByCustomKeysOrder([0], [])); + self::assertEquals($this->simpleArray, Arrays::sortByCustomKeysOrder($this->simpleArray, [])); + + $keysOrder = [ + 'amet', + 'dolor', + 'Lorem', + ]; + + $sorted = [ + 'dolor' => 'sit', + 'amet' => 'consectetur', + 'Lorem' => 'ipsum', + ]; + + self::assertEquals($sorted, Arrays::sortByCustomKeysOrder($this->simpleArrayWithKeys, $keysOrder)); + + $array = [ + 'Lorem', + 'ipsum', + 'dolor', + 'sit', + 'amet', + ]; + + $keysOrder = [ + 0, + 3, + 1, + 2, + ]; + + $sorted = [ + 0 => 'Lorem', + 3 => 'sit', + 1 => 'ipsum', + 2 => 'dolor', + 4 => 'amet', + ]; + + self::assertEquals($sorted, Arrays::sortByCustomKeysOrder($array, $keysOrder)); + + $array = [ + 'Lorem', + 'ipsum', + 'dolor', + 'sit', + 'amet', + ]; + + $keysOrder = [ + 0, + 3, + ]; + + $sorted = [ + 0 => 'Lorem', + 3 => 'sit', + 1 => 'ipsum', + 2 => 'dolor', + 4 => 'amet', + ]; + + self::assertEquals($sorted, Arrays::sortByCustomKeysOrder($array, $keysOrder)); + } + + public function testImplodeSmart() + { + $separator = '/'; + + /* + * Empty array + */ + self::assertEmpty(Arrays::implodeSmart([], $separator)); + + /* + * Simple, one-dimension array + */ + self::assertEquals(implode($separator, $this->simpleArray), Arrays::implodeSmart($this->simpleArray, $separator)); + + /* + * An array with elements that contain separator + */ + $array = [ + 'lorem' . $separator, + 'ipsum', + $separator . 'dolor', + ]; + + self::assertEquals(implode($separator, [ + 'lorem', + 'ipsum', + 'dolor', + ]), Arrays::implodeSmart($array, $separator)); + + /* + * Complex array + */ + self::assertEquals(implode($separator, [ + 'donec', + 'quis', + 'elit', + ]), Arrays::implodeSmart($this->complexArray['sit'], $separator)); + } + + public function testGetNextElement() + { + /* + * Negative cases + */ + self::assertNull(Arrays::getNextElement($this->simpleArray, 'amet')); + self::assertNull(Arrays::getNextElement($this->simpleArray, 'xyz')); + self::assertNull(Arrays::getNextElement($this->simpleArray, 0)); + self::assertNull(Arrays::getNextElement([], '')); + self::assertNull(Arrays::getNextElement([], null)); + + /* + * Positive cases + */ + self::assertEquals('ipsum', Arrays::getNextElement($this->simpleArray, 'Lorem')); + self::assertEquals('sit', Arrays::getNextElement($this->simpleArray, 'dolor')); + } + + public function testGetPreviousElement() + { + /* + * Negative cases + */ + self::assertNull(Arrays::getPreviousElement($this->simpleArray, 'Lorem')); + self::assertNull(Arrays::getPreviousElement($this->simpleArray, 'xyz')); + self::assertNull(Arrays::getPreviousElement($this->simpleArray, 0)); + self::assertNull(Arrays::getPreviousElement([], '')); + self::assertNull(Arrays::getPreviousElement([], null)); + + /* + * Positive cases + */ + self::assertEquals('ipsum', Arrays::getPreviousElement($this->simpleArray, 'dolor')); + self::assertEquals('sit', Arrays::getPreviousElement($this->simpleArray, 'amet')); + } + + public function testGetIndexOf() + { + /* + * Negative cases + */ + self::assertFalse(Arrays::getIndexOf([], 'a')); + self::assertNull(Arrays::getIndexOf($this->simpleArray, 'loremmm')); + + /* + * Positive cases + */ + self::assertEquals(1, Arrays::getIndexOf($this->simpleArray, 'ipsum')); + self::assertEquals('dolor', Arrays::getIndexOf($this->simpleArrayWithKeys, 'sit')); + self::assertEquals('mollis', Arrays::getIndexOf($this->complexArray, 1234)); + } + + public function testIncrementIndexes() + { + /* + * Negative cases + */ + self::assertEquals([], Arrays::incrementIndexes([])); + + /* + * Positive cases + */ + $array = [ + 1 => 'Lorem', + 2 => 'ipsum', + 3 => 'dolor', + 4 => 'sit', + 5 => 'amet', + ]; + self::assertEquals($array, Arrays::incrementIndexes($this->simpleArray)); + + $array = [ + 0 => 'Lorem', + 2 => 'ipsum', + 3 => 'dolor', + 4 => 'sit', + 5 => 'amet', + ]; + self::assertEquals($array, Arrays::incrementIndexes($this->simpleArray, 1)); + + $array = [ + 0 => 'Lorem', + 1 => 'ipsum', + 3 => 'dolor', + 4 => 'sit', + 5 => 'amet', + ]; + self::assertEquals($array, Arrays::incrementIndexes($this->simpleArray, 2)); + } + + public function testAreAllValuesEmpty() + { + /* + * Negative cases + */ + self::assertFalse(Arrays::areAllValuesEmpty([])); + self::assertFalse(Arrays::areAllValuesEmpty([], true)); + self::assertFalse(Arrays::areAllValuesEmpty($this->simpleArray)); + self::assertFalse(Arrays::areAllValuesEmpty($this->simpleArray, true)); + + $array = [ + null, + 0, + ]; + self::assertFalse(Arrays::areAllValuesEmpty($array, true)); + + $array = [ + null, + [ + null, + ], + ]; + self::assertFalse(Arrays::areAllValuesEmpty($array, true)); + + /* + * Positive cases + */ + $array = [ + '', + 0, + ]; + self::assertTrue(Arrays::areAllValuesEmpty($array)); + + $array = [ + null, + null, + ]; + self::assertTrue(Arrays::areAllValuesEmpty($array, true)); + } + + public function testDiffRecursive() + { + /* + * Negative cases + */ + self::assertEquals([], Arrays::arrayDiffRecursive([], [])); + self::assertEquals([], Arrays::arrayDiffRecursive([], [], true)); + + /* + * Positive cases - full comparison (keys and values) + */ + self::assertEquals(['a'], Arrays::arrayDiffRecursive(['a'], [])); + self::assertEquals([], Arrays::arrayDiffRecursive([], ['a'])); + self::assertEquals([], Arrays::arrayDiffRecursive($this->simpleArray, $this->simpleArray)); + + $array = [ + 'Lorem', + 'ipsum', + 'dolor', + ]; + self::assertEquals([], Arrays::arrayDiffRecursive($array, $this->simpleArray)); + + $array = [ + 'Lorem', + 'ipsum', + 'dolor', + ]; + + $diff = [ + 3 => 'sit', + 4 => 'amet', + ]; + self::assertEquals($diff, Arrays::arrayDiffRecursive($this->simpleArray, $array)); + + $array1 = [ + [ + 'a', + 'b', + 'c', + ], + [ + 'd', + 'e', + ], + ]; + + $array2 = [ + [ + 'a', + 'b', + ], + 'f', + ]; + + $diff = [ + [ + 2 => 'c', + ], + ]; + self::assertEquals($diff, Arrays::arrayDiffRecursive($array1, $array2)); + + self::assertEquals($this->twoDimensionsArray[1], Arrays::arrayDiffRecursive($this->twoDimensionsArray[1], $this->twoDimensionsArray)); + + /* + * Positive cases - simple comparison (values only) + */ + self::assertEquals(['a'], Arrays::arrayDiffRecursive(['a'], [], true)); + + $array = [ + 'amet', + 'sit', + 1234, + 'ipsum', + ]; + + $diff = [ + 0 => 'Lorem', + 2 => 'dolor', + ]; + + self::assertEquals($diff, Arrays::arrayDiffRecursive($this->simpleArray, $array, true)); + + $array = [ + [ + 'lorem', + 'ipsum', + 'dolor', + ], + [ + 'consectetur', + 'adipiscing', + ], + [ + 'sagittis', + ], + ]; + + $diff = [ + [ + 3 => 'sit', + 4 => 'amet', + ], + [ + 2 => 'elit', + ], + [ + 0 => 'donec', + 2 => 'fringilla', + 3 => 'eleifend', + ], + ]; + + self::assertEquals($diff, Arrays::arrayDiffRecursive($this->twoDimensionsArray, $array, true)); + + $array = [ + [ + 'lorem', + 'ipsum', + 'dolor', + ], + [ + 'consectetur', + 'adipiscing', + ], + 'Lorem ipsum', + ]; + + $diff = [ + [ + 3 => 'sit', + 4 => 'amet', + ], + [ + 2 => 'elit', + ], + ]; + + self::assertEquals($diff, Arrays::arrayDiffRecursive($this->twoDimensionsArray, $array, true)); + + $array = [ + 'Lorem ipsum', + [ + 'lorem', + 'ipsum', + 'dolor', + ], + 'donec sagittis', + ]; + + $diff = [ + 0 => 'Lorem ipsum', + 1 => [ + 'lorem', + 'ipsum', + 'dolor', + ], + 2 => 'donec sagittis', + ]; + + self::assertEquals($diff, Arrays::arrayDiffRecursive($array, $this->twoDimensionsArray, true)); + } + + public function testGetDimensionsCount() + { + /* + * Basic cases + */ + self::assertEquals(1, Arrays::getDimensionsCount([])); + self::assertEquals(1, Arrays::getDimensionsCount([''])); + + /* + * Simple cases + */ + self::assertEquals(1, Arrays::getDimensionsCount($this->simpleArray)); + self::assertEquals(1, Arrays::getDimensionsCount($this->simpleArrayWithKeys)); + + /* + * Complex cases + */ + self::assertEquals(2, Arrays::getDimensionsCount($this->twoDimensionsArray)); + self::assertEquals(4, Arrays::getDimensionsCount($this->complexArray)); + } + + public function testIsMultiDimensional() + { + /* + * Negative cases + */ + self::assertNull(Arrays::isMultiDimensional([])); + + /* + * Positive cases + */ + self::assertFalse(Arrays::isMultiDimensional($this->simpleArray)); + self::assertFalse(Arrays::isMultiDimensional($this->simpleArrayWithKeys)); + + self::assertTrue(Arrays::isMultiDimensional($this->twoDimensionsArray)); + self::assertTrue(Arrays::isMultiDimensional($this->complexArray)); + } + + /** + * Provides simple array to set/replace values with keys + * + * @return \Generator + */ + public function provideSimpleArrayToSetKeysAsValues() + { + yield[ + [ + 1, + 2, + 3, + 4, + ], + [ + 1 => 0, + 2 => 1, + 3 => 2, + 4 => 3, + ], + ]; + + yield[ + [ + 'Lorem', + 'ipsum', + 'dolor', + 'sit', + 'amet', + ], + [ + 'Lorem' => 0, + 'ipsum' => 1, + 'dolor' => 2, + 'sit' => 3, + 'amet' => 4, + ], + ]; + } + + /** + * Provides an array with duplicated values to set/replace values with keys + * + * @return \Generator + */ + public function provideArrayWithDuplicatedValuesToSetKeysAsValues() + { + yield[ + [ + 'lorem' => 'ipsum', + 'dolor' => 'ipsum', + 'sit' => 'amet', + 'diam' => 'non', + 'elit' => 'non', + 'in' => 'non', + ], + [ + 'ipsum' => [ + 'lorem', + 'dolor', + ], + 'amet' => 'sit', + 'non' => [ + 'diam', + 'elit', + 'in', + ], + ], + ]; + + yield[ + [ + 'lorem' => [ + 'diam' => 'non', + 'elit' => 'non', + 'in' => 'non', + ], + 'dolor1' => 'ipsum', + 'dolor2' => 'ipsum', + 'sit' => 'amet', + ], + [ + 'lorem' => [ + 'non' => [ + 'diam', + 'elit', + 'in', + ], + ], + 'ipsum' => [ + 'dolor1', + 'dolor2', + ], + 'amet' => 'sit', + ], + ]; + } + + /** + * {@inheritdoc} + */ + protected function setUp() + { + parent::setUp(); + + $this->simpleArray = [ + 'Lorem', + 'ipsum', + 'dolor', + 'sit', + 'amet', + ]; + + $this->simpleArrayWithKeys = [ + 'Lorem' => 'ipsum', + 'dolor' => 'sit', + 'amet' => 'consectetur', + ]; + + $this->twoDimensionsArray = [ + [ + 'lorem', + 'ipsum', + 'dolor', + 'sit', + 'amet', + ], + [ + 'consectetur', + 'adipiscing', + 'elit', + ], + [ + 'donec', + 'sagittis', + 'fringilla', + 'eleifend', + ], + ]; + + $this->complexArray = [ + 'lorem' => [ + 'ipsum' => [ + 'dolor' => 'sit', + 'diam' => [ + 'non' => 'egestas', + ], + ], + ], + 'consectetur' => 'adipiscing', + 'mollis' => 1234, + 2 => [], + 'sit' => [ + 'nullam' => 'donec', + 'aliquet' => [ + 'vitae' => [ + 'ligula' => 'quis', + ], + ], + 'elit', + ], + 'amet' => [ + 'iaculis', + 'primis', + ], + ]; + + $this->superComplexArray = [ + 'ipsum' => [ + 'quis' => [ + 'vestibulum' => [ + 'porta-1' => [ + 'turpis', + 'urna', + ], + 'porta-2' => [ + 'tortor' => [ + 'in' => [ + 'dui', + 'dolor' => [ + 'aliquam', + ], + ], + ], + ], + 'porta-3' => [ + 1, + 2, + 3, + ], + ], + ], + ], + 'primis' => [ + [ + 'in', + 'faucibus', + 'orci', + ], + [ + 'luctus', + 'et', + 'ultrices', + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + protected function tearDown() + { + parent::tearDown(); + + unset($this->simpleArray); + unset($this->simpleArrayWithKeys); + unset($this->twoDimensionsArray); + unset($this->complexArray); + unset($this->superComplexArray); + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/BundleTest.php b/tests/Meritoo/Common/Tests/Utilities/BundleTest.php new file mode 100644 index 0000000..ec65954 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/BundleTest.php @@ -0,0 +1,39 @@ + + * @copyright Meritoo.pl + */ +class BundleTest extends \PHPUnit_Framework_TestCase +{ + public function testGetBundleViewPathEmptyPathAndBundle() + { + self::assertNull(Bundle::getBundleViewPath('', '')); + self::assertNull(Bundle::getBundleViewPath('test', '')); + self::assertNull(Bundle::getBundleViewPath('', 'test')); + } + + public function testGetBundleViewPathWithDefaultExtension() + { + self::assertEquals('Lorem:Ipsum.html.twig', Bundle::getBundleViewPath('Ipsum', 'Lorem')); + self::assertEquals('LobortisTincidunt:FusceElementum.html.twig', Bundle::getBundleViewPath('FusceElementum', 'LobortisTincidunt')); + } + + public function testGetBundleViewPathWithCustomExtension() + { + self::assertNull(Bundle::getBundleViewPath('Ipsum', 'Lorem', '')); + self::assertEquals('Lorem:Ipsum.js.twig', Bundle::getBundleViewPath('Ipsum', 'Lorem', 'js.twig')); + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/ComposerTest.php b/tests/Meritoo/Common/Tests/Utilities/ComposerTest.php new file mode 100644 index 0000000..45723a2 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/ComposerTest.php @@ -0,0 +1,87 @@ + + * @copyright Meritoo.pl + */ +class ComposerTest extends TestCase +{ + /** + * Path of existing composer.json used as source of data for tests + * + * @var string + */ + private $composerJsonPath; + + /** + * @param string $composerJsonPath Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetValueNotExistingComposerJson($composerJsonPath) + { + self::assertNull(Composer::getValue($composerJsonPath, '')); + self::assertNull(Composer::getValue('not/existing/composer.json', '')); + } + + /** + * @param string $nodeName Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetValueNotExistingNode($nodeName) + { + self::assertNull(Composer::getValue($this->composerJsonPath, $nodeName)); + self::assertNull(Composer::getValue($this->composerJsonPath, 'not_existing_node')); + } + + /** + * @param string $nodeName Name of existing node + * @param string $nodeValue Value of existing node + * + * @dataProvider getExistingNode + */ + public function testGetValueExistingNode($nodeName, $nodeValue) + { + self::assertEquals($nodeValue, Composer::getValue($this->composerJsonPath, $nodeName)); + } + + /** + * Provides names and values of existing nodes + * + * @return \Generator + */ + public function getExistingNode() + { + yield[ + 'name', + 'test/test', + ]; + + yield[ + 'version', + '1.0.2', + ]; + } + + /** + * {@inheritdoc} + */ + protected function setUp() + { + parent::setUp(); + + $this->composerJsonPath = $this->getFilePathToTests(Composer::FILE_NAME_MAIN); + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/DatePeriodTest.php b/tests/Meritoo/Common/Tests/Utilities/DatePeriodTest.php new file mode 100644 index 0000000..11ff297 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/DatePeriodTest.php @@ -0,0 +1,261 @@ + + * @copyright Meritoo.pl + */ +class DatePeriodTest extends TestCase +{ + /** + * @param DateTime $startDate (optional) Start date of period + * @param DateTime $endDate (optional) End date of period + * + * @dataProvider provideDatePeriod + */ + public function testConstruct(DateTime $startDate = null, DateTime $endDate = null) + { + $period = new DatePeriod($startDate, $endDate); + + self::assertEquals($startDate, $period->getStartDate()); + self::assertEquals($endDate, $period->getEndDate()); + } + + /** + * @param DateTime $startDate (optional) Start date of period + * @param DateTime $endDate (optional) End date of period + * + * @dataProvider provideDatePeriod + */ + public function testGettersAndSetters(DateTime $startDate = null, DateTime $endDate = null) + { + $period = new DatePeriod(); + + $period->setStartDate($startDate); + self::assertEquals($startDate, $period->getStartDate()); + + $period->setEndDate($endDate); + self::assertEquals($endDate, $period->getEndDate()); + } + + /** + * @param mixed $period Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testIsCorrectPeriodEmptyPeriod($period) + { + self::assertFalse(DatePeriod::isCorrectPeriod($period)); + } + + /** + * @param int $period Incorrect period to verify + * @dataProvider provideIncorrectPeriod + */ + public function testIsCorrectPeriodIncorrectPeriod($period) + { + self::assertFalse(DatePeriod::isCorrectPeriod($period)); + } + + /** + * @param int $period The period to verify + * @dataProvider providePeriod + */ + public function testIsCorrectPeriod($period) + { + self::assertTrue(DatePeriod::isCorrectPeriod($period)); + } + + /** + * @param DatePeriod $period The date period to verify + * @param string $format Format used to format the date + * + * @dataProvider provideDatePeriodAndIncorrectDateFormat + */ + public function testGetFormattedDateIncorrectDateFormat(DatePeriod $period, $format) + { + self::assertEquals('', $period->getFormattedDate($format)); + } + + /** + * @param DatePeriod $period The date period to verify + * @param string $format Format used to format the date + * @param bool $startDate If is set to true, start date is formatted. Otherwise - end date. + * @param string $expected Expected, formatted date + * + * @dataProvider provideDatePeriodAndDateFormat + */ + public function testGetFormattedDate(DatePeriod $period, $format, $startDate, $expected) + { + self::assertEquals($expected, $period->getFormattedDate($format, $startDate)); + } + + /** + * Provides the start and end date of date period + * + * @return Generator + */ + public function provideDatePeriod() + { + $startDate = new DateTime('2001-01-01'); + $endDate = new DateTime('2002-02-02'); + + yield[ + null, + null, + ]; + + yield[ + $startDate, + $startDate, + null, + ]; + + yield[ + null, + null, + $endDate, + ]; + + yield[ + $startDate, + $endDate, + ]; + } + + /** + * Provides incorrect period + * + * @return Generator + */ + public function provideIncorrectPeriod() + { + yield[-1]; + yield[0]; + yield[10]; + } + + /** + * Provides period to verify + * + * @return Generator + */ + public function providePeriod() + { + yield[DatePeriod::LAST_WEEK]; + yield[DatePeriod::THIS_WEEK]; + yield[DatePeriod::NEXT_WEEK]; + yield[DatePeriod::LAST_MONTH]; + yield[DatePeriod::THIS_MONTH]; + yield[DatePeriod::NEXT_MONTH]; + yield[DatePeriod::LAST_YEAR]; + yield[DatePeriod::THIS_YEAR]; + yield[DatePeriod::NEXT_YEAR]; + } + + /** + * Provides period and incorrect format of date to verify + * + * @return Generator + */ + public function provideDatePeriodAndIncorrectDateFormat() + { + $startDate = new DateTime('2001-01-01'); + $endDate = new DateTime('2002-02-02'); + + yield[ + new DatePeriod($startDate, $endDate), + '', + ]; + + yield[ + new DatePeriod($startDate, $endDate), + null, + ]; + + yield[ + new DatePeriod($startDate, $endDate), + false, + ]; + } + + /** + * Provides period and format of date to verify + * + * @return Generator + */ + public function provideDatePeriodAndDateFormat() + { + $startDate = new DateTime('2001-01-01'); + $endDate = new DateTime('2002-02-02'); + + /* + * For start date + */ + yield[ + new DatePeriod($startDate, $endDate), + 'Y', + true, + '2001', + ]; + + yield[ + new DatePeriod($startDate, $endDate), + 'D', + true, + 'Mon', + ]; + + yield[ + new DatePeriod($startDate, $endDate), + 'Y-m-d', + true, + '2001-01-01', + ]; + + yield[ + new DatePeriod($startDate, $endDate), + 'Y-m-d H:i', + true, + '2001-01-01 00:00', + ]; + + /* + * For end date + */ + yield[ + new DatePeriod($startDate, $endDate), + 'Y', + false, + '2002', + ]; + + yield[ + new DatePeriod($startDate, $endDate), + 'D', + false, + 'Sat', + ]; + + yield[ + new DatePeriod($startDate, $endDate), + 'Y-m-d', + false, + '2002-02-02', + ]; + + yield[ + new DatePeriod($startDate, $endDate), + 'Y-m-d H:i', + false, + '2002-02-02 00:00', + ]; + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/DateTest.php b/tests/Meritoo/Common/Tests/Utilities/DateTest.php new file mode 100644 index 0000000..841a6f9 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/DateTest.php @@ -0,0 +1,748 @@ + + * @copyright Meritoo.pl + */ +class DateTest extends TestCase +{ + /** + * @param mixed $value Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetDateTimeEmptyValue($value) + { + self::assertFalse(Date::getDateTime($value)); + } + + /** + * @param mixed $value Incorrect source of DateTime + * @dataProvider provideIncorrectDateTimeValue + */ + public function testGetDateTimeIncorrectValue($value) + { + self::assertFalse(Date::getDateTime($value)); + } + + /** + * @param bool $value The value which maybe is a date + * @dataProvider provideBooleanValue + */ + public function testGetDateTimeBoolean($value) + { + self::assertFalse(Date::getDateTime($value)); + } + + /** + * @param string $relativeFormat Relative / compound format of DateTime + * @dataProvider provideDateTimeRelativeFormat + */ + public function testGetDateTimeRelativeFormats($relativeFormat) + { + /* + * Values based on relative / compound formats, but... without explicitly declaring them as compound + * (2nd argument set to false by default) + * + * http://php.net/manual/en/datetime.formats.compound.php + */ + self::assertFalse(Date::getDateTime($relativeFormat)); + + /* + * Values based on relative / compound formats + * http://php.net/manual/en/datetime.formats.compound.php + */ + self::assertInstanceOf(DateTime::class, Date::getDateTime($relativeFormat, true)); + } + + /** + * @param DateTime $dateTime Instance of DateTime class + * @dataProvider provideDateTimeInstance + */ + public function testGetDateTimeInstanceDateTime(DateTime $dateTime) + { + self::assertInstanceOf(DateTime::class, Date::getDateTime($dateTime)); + } + + public function testGetDateTimeConcreteDates() + { + /* + * Using the standard date format provided by the tested method + */ + self::assertInstanceOf(DateTime::class, Date::getDateTime('2015-03-20')); + + /* + * Using custom date format + */ + self::assertInstanceOf(DateTime::class, Date::getDateTime('2015-03-20 11:30', false, 'Y-m-d H:i')); + self::assertInstanceOf(DateTime::class, Date::getDateTime('20.03.2015', false, 'd.m.Y')); + } + + /** + * @param mixed $value Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testIsValidDateEmptyDates($value) + { + self::assertFalse(Date::isValidDate($value)); + } + + /** + * @param mixed $value Incorrect source of DateTime + * @dataProvider provideIncorrectDateTimeValue + */ + public function testIsValidDateIncorrectDates($value) + { + self::assertFalse(Date::isValidDate($value)); + } + + public function testIsValidDateValidDates() + { + self::assertTrue(Date::isValidDate('2017-01-01')); + self::assertTrue(Date::isValidDate('2017-01-01 10:30', true)); + self::assertTrue(Date::isValidDate('2017-01-01 14:00', true)); + + self::assertTrue(Date::isValidDate(new DateTime())); + self::assertTrue(Date::isValidDate(new DateTime('now'))); + self::assertTrue(Date::isValidDate(new DateTime('tomorrow'))); + self::assertTrue(Date::isValidDate(new DateTime('m'))); + } + + /** + * @param mixed $value Empty source of date format + * @dataProvider provideEmptyValue + */ + public function testIsValidDateFormatEmptyFormats($value) + { + self::assertFalse(Date::isValidDateFormat($value)); + } + + /** + * @param mixed $format Invalid format of date + * @dataProvider provideInvalidDateFormats + */ + public function testIsValidDateFormatInvalidFormats($format) + { + self::assertFalse(Date::isValidDateFormat($format)); + } + + public function testIsValidDateFormatValidFormats() + { + self::assertTrue(Date::isValidDateFormat('Y')); + self::assertTrue(Date::isValidDateFormat('yy')); + self::assertTrue(Date::isValidDateFormat('M')); + self::assertTrue(Date::isValidDateFormat('i')); + self::assertTrue(Date::isValidDateFormat('l')); + self::assertTrue(Date::isValidDateFormat('l, d F')); + self::assertTrue(Date::isValidDateFormat('Y-m-d')); + self::assertTrue(Date::isValidDateFormat('H:i:s')); + self::assertTrue(Date::isValidDateFormat('Y/m/d H:i:s')); + } + + /** + * @param mixed $value Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGenerateRandomTimeEmptyFormat($value) + { + self::assertNull(Date::generateRandomTime($value)); + } + + public function testGenerateRandomTimeIncorrectFormat() + { + self::assertNull(Date::generateRandomTime(',')); + self::assertNull(Date::generateRandomTime(';')); + self::assertNull(Date::generateRandomTime('|')); + self::assertNull(Date::generateRandomTime('?')); + } + + public function testGenerateRandomTimeDefaultFormat() + { + self::assertRegExp('/\d{2}:\d{2}:\d{2}/', Date::generateRandomTime()); + } + + public function testGenerateRandomTimeCustomFormat() + { + self::assertRegExp('/^0[1-9]{1}|1[0-2]{1}$/', Date::generateRandomTime('h')); // 01 through 12 + self::assertRegExp('/^[0-5]?[0-9]$/', Date::generateRandomTime('i')); // 00 through 59 + self::assertRegExp('/^[0-5]?[0-9]$/', Date::generateRandomTime('s')); // 00 through 59 + + self::assertRegExp('/^\d{2}:\d{2}$/', Date::generateRandomTime('H:i')); + self::assertRegExp('/^[1-9]|1[0-2]:\d{2}$/', Date::generateRandomTime('g:i')); + } + + public function testGetCurrentDayOfWeek() + { + self::assertRegExp('/^[0-6]{1}$/', (string)Date::getCurrentDayOfWeek()); + } + + public function testGetCurrentDayOfWeekName() + { + $days = [ + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', + ]; + + $pattern = sprintf('/^%s$/', implode('|', $days)); + + self::assertRegExp($pattern, Date::getCurrentDayOfWeekName()); + } + + /** + * @param int $year The year value + * @param int $month The month value + * @param int $day The day value + * + * @dataProvider provideIncorrectYearMonthDay + */ + public function testGetDayOfWeekIncorrectValues($year, $month, $day) + { + $this->expectException(IncorrectDatePartException::class); + self::assertEmpty(Date::getDayOfWeek($year, $month, $day)); + } + + /** + * @param int $year The year value + * @param int $month The month value + * @param int $day The day value + * + * @dataProvider provideYearMonthDay + */ + public function testGetDayOfWeek($year, $month, $day) + { + self::assertRegExp('/^[0-6]{1}$/', (string)Date::getDayOfWeek($year, $month, $day)); + } + + /** + * @param string|DateTime $dateStart The start date + * @param string|DateTime $dateEnd The end date + * + * @dataProvider provideEmptyDatesForDateDifference + */ + public function testGetDateDifferenceEmptyDates($dateStart, $dateEnd) + { + self::assertNull(Date::getDateDifference($dateStart, $dateEnd)); + } + + public function testGetDateDifferenceInvalidDates() + { + self::assertNull(Date::getDateDifference('2017-01-40', '2017-13-01')); + self::assertNull(Date::getDateDifference('xyz', 'lorem')); + } + + public function testGetDateDifferenceOneDay() + { + /* + * Difference of 1 day + */ + $dateStart = '2017-01-01'; + $dateEnd = '2017-01-02'; + + $effect = [ + Date::DATE_DIFFERENCE_UNIT_YEARS => 0, + Date::DATE_DIFFERENCE_UNIT_MONTHS => 0, + Date::DATE_DIFFERENCE_UNIT_DAYS => 1, + Date::DATE_DIFFERENCE_UNIT_HOURS => 0, + Date::DATE_DIFFERENCE_UNIT_MINUTES => 0, + ]; + + self::assertEquals($effect, Date::getDateDifference($dateStart, $dateEnd)); + self::assertEquals($effect, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd))); + + self::assertEquals(0, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_YEARS)); + self::assertEquals(0, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd), Date::DATE_DIFFERENCE_UNIT_YEARS)); + + self::assertEquals(1, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_DAYS)); + self::assertEquals(1, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd), Date::DATE_DIFFERENCE_UNIT_DAYS)); + + /* + * Difference of 1 day (using the relative date format) + */ + $effect = [ + Date::DATE_DIFFERENCE_UNIT_YEARS => 0, + Date::DATE_DIFFERENCE_UNIT_MONTHS => 0, + Date::DATE_DIFFERENCE_UNIT_DAYS => 1, + Date::DATE_DIFFERENCE_UNIT_HOURS => 0, + Date::DATE_DIFFERENCE_UNIT_MINUTES => 0, + ]; + + self::assertEquals($effect, Date::getDateDifference(new DateTime('yesterday'), new DateTime('midnight'))); + self::assertEquals(1, Date::getDateDifference(new DateTime('yesterday'), new DateTime('midnight'), Date::DATE_DIFFERENCE_UNIT_DAYS)); + self::assertEquals(0, Date::getDateDifference(new DateTime('yesterday'), new DateTime('midnight'), Date::DATE_DIFFERENCE_UNIT_HOURS)); + } + + public function testGetDateDifferenceOneDayTwoHours() + { + /* + * Difference of 1 day, 2 hours and 15 minutes + */ + $dateStart = '2017-01-01 12:00'; + $dateEnd = '2017-01-02 14:15'; + + $effect = [ + Date::DATE_DIFFERENCE_UNIT_YEARS => 0, + Date::DATE_DIFFERENCE_UNIT_MONTHS => 0, + Date::DATE_DIFFERENCE_UNIT_DAYS => 1, + Date::DATE_DIFFERENCE_UNIT_HOURS => 2, + Date::DATE_DIFFERENCE_UNIT_MINUTES => 15, + ]; + + self::assertEquals($effect, Date::getDateDifference($dateStart, $dateEnd)); + self::assertEquals($effect, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd))); + + self::assertEquals(0, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_YEARS)); + self::assertEquals(0, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd), Date::DATE_DIFFERENCE_UNIT_YEARS)); + + self::assertEquals(2, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_HOURS)); + self::assertEquals(2, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd), Date::DATE_DIFFERENCE_UNIT_HOURS)); + + self::assertEquals(15, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_MINUTES)); + self::assertEquals(15, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd), Date::DATE_DIFFERENCE_UNIT_MINUTES)); + } + + public function testGetDateDifferenceOneMonthFortyOneDays() + { + /* + * Difference of 1 month, 41 days, 4 hours and 30 minutes + */ + $dateStart = '2017-01-01 12:00'; + $dateEnd = '2017-02-11 16:30'; + + $effect = [ + Date::DATE_DIFFERENCE_UNIT_YEARS => 0, + Date::DATE_DIFFERENCE_UNIT_MONTHS => 1, + Date::DATE_DIFFERENCE_UNIT_DAYS => 41, + Date::DATE_DIFFERENCE_UNIT_HOURS => 4, + Date::DATE_DIFFERENCE_UNIT_MINUTES => 30, + ]; + + self::assertEquals($effect, Date::getDateDifference($dateStart, $dateEnd)); + self::assertEquals($effect, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd))); + + self::assertEquals(0, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_YEARS)); + self::assertEquals(0, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd), Date::DATE_DIFFERENCE_UNIT_YEARS)); + + self::assertEquals(1, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_MONTHS)); + self::assertEquals(1, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd), Date::DATE_DIFFERENCE_UNIT_MONTHS)); + + self::assertEquals(41, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_DAYS)); + self::assertEquals(41, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd), Date::DATE_DIFFERENCE_UNIT_DAYS)); + + self::assertEquals(30, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_MINUTES)); + self::assertEquals(30, Date::getDateDifference(new DateTime($dateStart), new DateTime($dateEnd), Date::DATE_DIFFERENCE_UNIT_MINUTES)); + } + + public function testGetDateDifferenceNoDifference() + { + /* + * No difference + */ + $dateStart = '2017-01-01 12:00'; + $dateEnd = $dateStart; + + $effect = [ + Date::DATE_DIFFERENCE_UNIT_YEARS => 0, + Date::DATE_DIFFERENCE_UNIT_MONTHS => 0, + Date::DATE_DIFFERENCE_UNIT_DAYS => 0, + Date::DATE_DIFFERENCE_UNIT_HOURS => 0, + Date::DATE_DIFFERENCE_UNIT_MINUTES => 0, + ]; + + self::assertEquals($effect, Date::getDateDifference($dateStart, $dateEnd)); + self::assertEquals($effect, Date::getDateDifference(new DateTime(), new DateTime())); + + self::assertEquals(0, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_YEARS)); + self::assertEquals(0, Date::getDateDifference(new DateTime(), new DateTime(), Date::DATE_DIFFERENCE_UNIT_YEARS)); + + self::assertEquals(0, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_MONTHS)); + self::assertEquals(0, Date::getDateDifference(new DateTime(), new DateTime(), Date::DATE_DIFFERENCE_UNIT_MONTHS)); + + self::assertEquals(0, Date::getDateDifference($dateStart, $dateEnd, Date::DATE_DIFFERENCE_UNIT_MINUTES)); + self::assertEquals(0, Date::getDateDifference(new DateTime(), new DateTime(), Date::DATE_DIFFERENCE_UNIT_MINUTES)); + } + + /** + * @param mixed $invalidCount Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetDatesCollectionInvalidCount($invalidCount) + { + self::assertEquals([], Date::getDatesCollection(new DateTime(), $invalidCount)); + self::assertEquals([], Date::getDatesCollection(new DateTime(), -1)); + } + + /** + * @param mixed $invalidInterval Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetDatesCollectionInvalidInterval($invalidInterval) + { + self::assertEquals([], Date::getDatesCollection(new DateTime(), 2, $invalidInterval)); + self::assertEquals([], Date::getDatesCollection(new DateTime(), 2, 'lorem')); + self::assertEquals([], Date::getDatesCollection(new DateTime(), 2, '%d')); + } + + public function testGetDatesCollection() + { + /* + * 1 date only + */ + $effect = [ + 1 => new DateTime('2017-01-02'), + ]; + + self::assertEquals($effect, Date::getDatesCollection(new DateTime('2017-01-01'), 1)); + + /* + * 3 dates with default date interval (days) + */ + $effect = [ + 1 => new DateTime('2017-01-02'), + 2 => new DateTime('2017-01-03'), + 3 => new DateTime('2017-01-04'), + ]; + + self::assertEquals($effect, Date::getDatesCollection(new DateTime('2017-01-01'), 3)); + + /* + * 3 dates with custom date interval (hours) + */ + $effect = [ + 1 => new DateTime('2017-01-01 10:30'), + 2 => new DateTime('2017-01-01 11:30'), + 3 => new DateTime('2017-01-01 12:30'), + ]; + + self::assertEquals($effect, Date::getDatesCollection(new DateTime('2017-01-01 09:30'), 3, 'PT%dH')); + + /* + * 3 dates with custom date interval (months) + */ + $effect = [ + 1 => new DateTime('2017-02-01'), + 2 => new DateTime('2017-03-01'), + 3 => new DateTime('2017-04-01'), + ]; + + self::assertEquals($effect, Date::getDatesCollection(new DateTime('2017-01-01'), 3, 'P%dM')); + } + + public function testGetRandomDateUsingDefaults() + { + $startDate = new DateTime(); + $start = 1; + $end = 100; + + $intervalMinDate = (clone $startDate)->add(new DateInterval(sprintf('P%dD', $start))); + $intervalMaxDate = (clone $startDate)->add(new DateInterval(sprintf('P%dD', $end))); + + $randomDate = Date::getRandomDate(); + self::assertTrue($randomDate >= $intervalMinDate && $randomDate <= $intervalMaxDate); + } + + /** + * @param DateTime $startDate The start date. Start of the random date. + * @param int $start Start of random partition + * @param int $end End of random partition + * + * @dataProvider provideDataOfRandomDateIncorrectEnd + */ + public function testGetRandomDateIncorrectEnd(DateTime $startDate, $start, $end) + { + $randomDate = Date::getRandomDate($startDate, $start, $end); + $intervalDate = (clone $startDate)->add(new DateInterval(sprintf('P%dD', $start))); + + self::assertTrue($randomDate >= $intervalDate && $randomDate <= $intervalDate); + } + + /** + * @param DateTime $startDate The start date. Start of the random date. + * @param int $start Start of random partition + * @param int $end End of random partition + * + * @dataProvider provideDataOfRandomDate + */ + public function testGetRandomDate(DateTime $startDate, $start, $end) + { + $randomDate = Date::getRandomDate($startDate, $start, $end); + + $intervalMinDate = (clone $startDate)->add(new DateInterval(sprintf('P%dD', $start))); + $intervalMaxDate = (clone $startDate)->add(new DateInterval(sprintf('P%dD', $end))); + + self::assertTrue($randomDate >= $intervalMinDate && $randomDate <= $intervalMaxDate); + } + + /** + * Provides incorrect invalidCount of DateTime + * + * @return Generator + */ + public function provideIncorrectDateTimeValue() + { + /* + * Incorrect one-character values + */ + yield['a']; + yield['m']; + + /* + * Incorrect strings + */ + yield['ss']; + yield['sss']; + yield['mm']; + yield['yy']; + yield['yyyy']; + + /* + * Incorrect integer values + */ + yield[1]; + yield[10]; + yield[15]; + yield[100]; + yield[1000]; + + /* + * Incorrect string / numeric values + */ + yield['1']; + yield['10']; + yield['15']; + yield['100']; + yield['1000']; + + /* + * Incorrect dates + */ + yield['0-0-0']; + yield['20-01-01']; + yield['2015-0-0']; + yield['2015-00-00']; + yield['2015-16-01']; + } + + /** + * Provides invalid format of date + * + * @return Generator + */ + public function provideInvalidDateFormats() + { + yield[0]; + yield[9]; + yield['[]']; + yield['invalid']; + yield['Q']; + yield[',']; + yield['.']; + yield['aa###']; + yield['Y/m/d H:i:invalid']; + } + + /** + * Provide empty dates for date difference + * + * @return Generator + */ + public function provideEmptyDatesForDateDifference() + { + yield[ + null, + null, + ]; + + yield[ + '', + '', + ]; + + yield[ + null, + new DateTime(), + ]; + + yield[ + new DateTime(), + null, + ]; + } + + /** + * Provides incorrect values of year, month and day + * + * @return Generator + */ + public function provideIncorrectYearMonthDay() + { + yield[ + null, + null, + null, + ]; + + yield[ + '', + '', + '', + ]; + + yield[ + 0, + 0, + 0, + ]; + + yield[ + -1, + -1, + -1, + ]; + + yield[ + 5000, + 50, + 50, + ]; + + yield[ + 2000, + 13, + 01, + ]; + + yield[ + 2000, + 01, + 40, + ]; + } + + /** + * Provides values of year, month and day + * + * @return Generator + */ + public function provideYearMonthDay() + { + yield[ + 2000, + 01, + 01, + ]; + + yield[ + 2000, + 1, + 1, + ]; + + yield[ + 2000, + 2, + 2, + ]; + + yield[ + 2000, + 6, + 1, + ]; + + yield[ + 2000, + 12, + 01, + ]; + + yield[ + 2000, + 12, + 1, + ]; + + yield[ + 2000, + 12, + 31, + ]; + } + + /** + * Provides data for the random date with incorrect end of random partition + * + * @return Generator + */ + public function provideDataOfRandomDateIncorrectEnd() + { + yield[ + new DateTime('2000-01-01'), + 100, + 1, + ]; + } + + /** + * Provides data for the random date + * + * @return Generator + */ + public function provideDataOfRandomDate() + { + yield[ + new DateTime('2000-01-01'), + 1, + 100, + ]; + + yield[ + new DateTime('2000-12-01'), + 1, + 100, + ]; + yield[ + new DateTime('2000-01-01'), + '1', + '100', + ]; + + yield[ + new DateTime('2000-12-01'), + '1', + '100', + ]; + + yield[ + new DateTime('2000-01-01'), + 10, + 50, + ]; + + yield[ + new DateTime('2000-12-01'), + 10, + 50, + ]; + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/GeneratorUtilityTest.php b/tests/Meritoo/Common/Tests/Utilities/GeneratorUtilityTest.php new file mode 100644 index 0000000..9bbb692 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/GeneratorUtilityTest.php @@ -0,0 +1,56 @@ + + * @copyright Meritoo.pl + */ +class GeneratorUtilityTest extends TestCase +{ + public function testGetGeneratorElements() + { + /* + * Generator that provides boolean value + */ + $elements = [ + [false], + [true], + ]; + + $generator = $this->provideBooleanValue(); + self::assertEquals($elements, GeneratorUtility::getGeneratorElements($generator)); + + $elements = [ + [''], + [' '], + [null], + [0], + [false], + [[]], + ]; + + /* + * Generator that provides an empty value + */ + $generator = $this->provideEmptyValue(); + self::assertEquals($elements, GeneratorUtility::getGeneratorElements($generator)); + + /* + * Generator that provides instance of DateTime class + */ + $generator = $this->provideDateTimeInstance(); + self::assertCount(4, GeneratorUtility::getGeneratorElements($generator)); + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/LocaleTest.php b/tests/Meritoo/Common/Tests/Utilities/LocaleTest.php new file mode 100644 index 0000000..6e355d5 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/LocaleTest.php @@ -0,0 +1,129 @@ + + * @copyright Meritoo.pl + */ +class LocaleTest extends TestCase +{ + /** + * @param mixed $languageCode Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetLongFormEmptyLanguageCode($languageCode) + { + self::assertEquals('', Locale::getLongForm($languageCode)); + } + + /** + * @param string $languageCode Language code, in ISO 639-1 format. Short form of the locale, e.g. "fr". + * @param string $countryCode Country code, in ISO 3166-1 alpha-2 format, e.g. "FR" + * @param string $encoding Encoding of the final locale + * @param string $expected Expected long form of the locale + * + * @dataProvider provideLanguageAndCountryCode + */ + public function testGetLongForm($languageCode, $countryCode, $encoding, $expected) + { + self::assertEquals($expected, Locale::getLongForm($languageCode, $countryCode, $encoding)); + } + + /** + * @param mixed $emptyValue Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testSetLocaleEmptyCategoryAndLanguageCode($emptyValue) + { + self::assertFalse(Locale::setLocale($emptyValue, $emptyValue)); + } + + /** + * @param int $category Named constant specifying the category of the functions affected by the locale + * setting. It's the same constant as required by setlocale() function. + * @param string $languageCode Language code, in ISO 639-1 format. Short form of the locale, e.g. "fr". + * + * @dataProvider provideCategoryAndLanguageCode + */ + public function testSetLocale($category, $languageCode) + { + self::assertTrue(Locale::setLocale($category, $languageCode)); + } + + /** + * Provides language and country code + * + * @return Generator + */ + public function provideLanguageAndCountryCode() + { + yield[ + 'fr', + '', + '', + 'fr_FR', + ]; + + yield[ + 'fr', + '', + 'UTF-8', + 'fr_FR.UTF-8', + ]; + + yield[ + 'fr', + 'FR', + '', + 'fr_FR', + ]; + + yield[ + 'fr', + 'FR', + 'UTF-8', + 'fr_FR.UTF-8', + ]; + } + + /** + * Provides category and language + * + * @return Generator + */ + public function provideCategoryAndLanguageCode() + { + yield[ + LC_ALL, + 'fr', + ]; + + yield[ + LC_COLLATE, + 'fr', + ]; + + yield[ + LC_CTYPE, + 'en', + ]; + + yield[ + LC_NUMERIC, + 'en', + ]; + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/MimeTypesTest.php b/tests/Meritoo/Common/Tests/Utilities/MimeTypesTest.php new file mode 100644 index 0000000..4bd4a7a --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/MimeTypesTest.php @@ -0,0 +1,472 @@ + + * @copyright Meritoo.pl + */ +class MimeTypesTest extends TestCase +{ + /** + * @param mixed $mimeType Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetExtensionEmptyMimeType($mimeType) + { + self::assertEquals('', MimeTypes::getExtension($mimeType)); + } + + /** + * @param bool $mimeType The mime type, e.g. "video/mpeg" + * @dataProvider provideBooleanValue + */ + public function testGetExtensionBooleanMimeType($mimeType) + { + self::assertEquals('', MimeTypes::getExtension($mimeType)); + } + + /** + * @param string $mimeType Not existing mime type, e.g. "lorem/ipsum" + * @dataProvider provideNotExistingMimeType + */ + public function testGetExtensionNotExistingMimeType($mimeType) + { + self::assertEquals('', MimeTypes::getExtension($mimeType)); + } + + /** + * @param string $mimeType The mime type, e.g. "video/mpeg" + * @param string $extension Expected extension + * + * @dataProvider provideMimeTypeToGetSingleExtension + */ + public function testGetExtensionSingle($mimeType, $extension) + { + self::assertEquals($extension, MimeTypes::getExtension($mimeType)); + } + + /** + * @param string $mimeType The mime type, e.g. "video/mpeg" + * @param array $extensions Expected extensions + * + * @dataProvider provideMimeTypeToGetMultipleExtension + */ + public function testGetExtensionMultiple($mimeType, $extensions) + { + self::assertEquals($extensions, MimeTypes::getExtension($mimeType)); + } + + /** + * @param array $mimesTypes Not existing mimes types, e.g. ['lorem/ipsum'] + * @dataProvider provideNotExistingMimeTypes + */ + public function testGetExtensionsNotExistingMimeTypes($mimesTypes) + { + self::assertEquals([], MimeTypes::getExtensions($mimesTypes)); + } + + /** + * @param array $mimesTypes The mimes types, e.g. ['video/mpeg', 'image/jpeg'] + * @param array $extensions Expected extensions + * + * @dataProvider provideMimesTypesToGetExtensions + */ + public function testGetExtensions($mimesTypes, $extensions) + { + self::assertEquals($extensions, MimeTypes::getExtensions($mimesTypes)); + } + + /** + * @param array $mimesTypes The mimes types, e.g. ['video/mpeg', 'image/jpeg'] + * @param array $extensions Expected extensions + * + * @dataProvider provideMimesTypesToGetExtensionsUpperCase + */ + public function testGetExtensionsUpperCase($mimesTypes, $extensions) + { + self::assertEquals($extensions, MimeTypes::getExtensions($mimesTypes, true)); + } + + /** + * @param mixed $filePath Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetMimeTypeEmptyPath($filePath) + { + self::assertEquals('', MimeTypes::getMimeType($filePath)); + } + + /** + * @param string $filePath Path of the file to check + * @param string $mimeType Expected mime type + * + * @dataProvider provideFilePathToGetMimeTypeOfRealFile + */ + public function testGetMimeTypeOfRealFile($filePath, $mimeType) + { + self::assertEquals($mimeType, MimeTypes::getMimeType($filePath)); + } + + /** + * @param mixed $mimeType Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testIsImageEmptyMimeType($mimeType) + { + self::assertFalse(MimeTypes::isImage($mimeType)); + } + + /** + * @param string $mimeType Not existing mime type, e.g. "lorem/ipsum" + * @dataProvider provideNotExistingMimeType + */ + public function testIsImageNotExistingMimeType($mimeType) + { + self::assertFalse(MimeTypes::isImage($mimeType)); + } + + /** + * @param string $mimeType Mime type of non-image, e.g. "text/plain" + * @dataProvider provideNonImageMimeType + */ + public function testIsImageNonImageMimeType($mimeType) + { + self::assertFalse(MimeTypes::isImage($mimeType)); + } + + /** + * @param mixed $path Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testIsImagePathEmptyPath($path) + { + self::assertFalse(MimeTypes::isImagePath($path)); + } + + /** + * @param mixed $path Path of not existing file, e.g. "lorem/ipsum.jpg" + * @dataProvider provideNotExistingFilePath + */ + public function testIsImagePathNotExistingPath($path) + { + self::assertFalse(MimeTypes::isImagePath($path)); + } + + /** + * @param string $path Path of the file to check + * @param bool $isImage Expected information if the file is an image + * + * @dataProvider provideExistingFilePathToCheckIsImagePath + */ + public function testIsImagePathExistingPath($path, $isImage) + { + self::assertEquals($isImage, MimeTypes::isImagePath($path)); + } + + /** + * @param string $mimeType Mime type of image, e.g. "image/jpeg" + * @dataProvider provideImageMimeType + */ + public function testIsImageImageMimeType($mimeType) + { + self::assertTrue(MimeTypes::isImage($mimeType)); + } + + /** + * Provides not existing mime type + * + * @return Generator + */ + public function provideNotExistingMimeType() + { + yield['lorem/ipsum']; + yield['dolor']; + yield['x/y/z']; + } + + /** + * Provides mime type of non-image + * + * @return Generator + */ + public function provideNonImageMimeType() + { + yield['application/rtf']; + yield['audio/mp4']; + yield['text/plain']; + yield['text/html']; + } + + /** + * Provides mime type of image + * + * @return Generator + */ + public function provideImageMimeType() + { + yield['image/bmp']; + yield['image/jpeg']; + yield['image/png']; + yield['image/tiff']; + yield['image/vnd.microsoft.icon']; + yield['image/x-rgb']; + } + + /** + * Provides existing mime type used to get single, one extension + * + * @return Generator + */ + public function provideMimeTypeToGetSingleExtension() + { + yield[ + 'application/x-7z-compressed', + '7z', + ]; + + yield[ + 'application/json', + 'json', + ]; + + yield[ + 'application/zip', + 'zip', + ]; + } + + /** + * Provides existing mime type used to get multiple, more than one extension + * + * @return Generator + */ + public function provideMimeTypeToGetMultipleExtension() + { + yield[ + 'application/postscript', + [ + 'ai', + 'eps', + 'ps', + ], + ]; + + yield[ + 'audio/midi', + [ + 'mid', + 'midi', + 'kar', + 'rmi', + ], + ]; + + yield[ + 'image/jpeg', + [ + 'jpeg', + 'jpe', + 'jpg', + ], + ]; + + yield[ + 'text/html', + [ + 'html', + 'htm', + ], + ]; + + yield[ + 'text/plain', + [ + 'txt', + 'text', + 'conf', + 'def', + 'list', + 'log', + 'in', + ], + ]; + + yield[ + 'video/mp4', + [ + 'mp4', + 'mp4v', + 'mpg4', + 'm4v', + ], + ]; + } + + /** + * Provides not existing mime types + * + * @return Generator + */ + public function provideNotExistingMimeTypes() + { + yield[ + [], + ]; + + yield[ + [ + '', + null, + false, + 0, + ], + ]; + + yield[ + [ + 'lorem/ipsum', + 'dolor/sit', + ], + ]; + } + + /** + * Provides mime types used to get extensions + * + * @return Generator + */ + public function provideMimesTypesToGetExtensions() + { + yield[ + [ + 'application/x-7z-compressed', + 'application/json', + ], + [ + 'application/x-7z-compressed' => '7z', + 'application/json' => 'json', + ], + ]; + + yield[ + [ + 'application/mathematica', + 'application/xml', + 'audio/mp4', + 'video/mp4', + ], + [ + 'application/mathematica' => [ + 'ma', + 'nb', + 'mb', + ], + 'application/xml' => [ + 'xml', + 'xsl', + ], + 'audio/mp4' => 'mp4a', + 'video/mp4' => [ + 'mp4', + 'mp4v', + 'mpg4', + 'm4v', + ], + ], + ]; + } + + /** + * Provides mime types used to get extensions as upper case + * + * @return Generator + */ + public function provideMimesTypesToGetExtensionsUpperCase() + { + yield[ + [ + 'application/x-7z-compressed', + 'application/json', + ], + [ + 'application/x-7z-compressed' => '7Z', + 'application/json' => 'JSON', + ], + ]; + + yield[ + [ + 'application/xml', + 'audio/mp4', + 'text/html', + 'video/mp4', + ], + [ + 'application/xml' => [ + 'XML', + 'XSL', + ], + 'audio/mp4' => 'MP4A', + 'text/html' => [ + 'HTML', + 'HTM', + ], + 'video/mp4' => [ + 'MP4', + 'MP4V', + 'MPG4', + 'M4V', + ], + ], + ]; + } + + /** + * Provides real file path to get mime type + * + * @return Generator + */ + public function provideFilePathToGetMimeTypeOfRealFile() + { + yield[ + $this->getFilePathToTests('minion.jpg'), + 'image/jpeg', + ]; + + yield[ + $this->getFilePathToTests('lorem-ipsum.txt'), + 'text/plain', + ]; + } + + /** + * Provides real file path to get information if the file is an image + * + * @return Generator + */ + public function provideExistingFilePathToCheckIsImagePath() + { + yield[ + $this->getFilePathToTests('minion.jpg'), + true, + ]; + + yield[ + $this->getFilePathToTests('lorem-ipsum.txt'), + false, + ]; + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/MiscellaneousTest.php b/tests/Meritoo/Common/Tests/Utilities/MiscellaneousTest.php new file mode 100644 index 0000000..b722f4a --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/MiscellaneousTest.php @@ -0,0 +1,1156 @@ + + * @copyright Meritoo.pl + */ +class MiscellaneousTest extends TestCase +{ + private $stringSmall; + private $stringCommaSeparated; + private $stringDotSeparated; + + public function testGetDirectoryContent() + { + $directoryPath = __DIR__ . '/../'; + $filePath = __FILE__; + + self::assertNull(Miscellaneous::getDirectoryContent(null)); + self::assertNull(Miscellaneous::getDirectoryContent('')); + + self::assertGreaterThanOrEqual(0, count(Miscellaneous::getDirectoryContent($directoryPath))); + self::assertGreaterThanOrEqual(0, count(Miscellaneous::getDirectoryContent($directoryPath, true))); + self::assertGreaterThanOrEqual(0, count(Miscellaneous::getDirectoryContent($directoryPath, true, 5))); + + self::assertGreaterThanOrEqual(0, count(Miscellaneous::getDirectoryContent($filePath))); + self::assertGreaterThanOrEqual(0, count(Miscellaneous::getDirectoryContent($filePath, true))); + } + + public function testCheckboxValue2Boolean() + { + self::assertTrue(Miscellaneous::checkboxValue2Boolean('on')); + self::assertFalse(Miscellaneous::checkboxValue2Boolean(' off')); + self::assertFalse(Miscellaneous::checkboxValue2Boolean(null)); + } + + public function testCheckboxValue2Integer() + { + self::assertEquals(1, Miscellaneous::checkboxValue2Integer('on')); + self::assertEquals(0, Miscellaneous::checkboxValue2Integer(' off')); + self::assertEquals(0, Miscellaneous::checkboxValue2Integer(null)); + } + + public function testGetFileExtension() + { + $fileName = 'Lorem.ipsum-dolor.sit.JPG'; + self::assertEquals('JPG', Miscellaneous::getFileExtension($fileName)); + self::assertEquals('jpg', Miscellaneous::getFileExtension($fileName, true)); + } + + /** + * @param string $fileName Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetFileNameWithoutExtensionEmptyValue($fileName) + { + self::assertEquals('', Miscellaneous::getFileNameWithoutExtension($fileName)); + } + + /** + * @param string $fileName The file name with extension + * @param string $withoutExtension The file name without extension + * + * @dataProvider provideFileNames + */ + public function testGetFileNameWithoutExtension($fileName, $withoutExtension) + { + self::assertEquals($withoutExtension, Miscellaneous::getFileNameWithoutExtension($fileName)); + } + + public function testGetFileNameFromPath() + { + /* + * Path with file + */ + self::assertEquals('sit.amet.JPG', Miscellaneous::getFileNameFromPath('lorem/ipsum-dolor/sit.amet.JPG')); + + /* + * Path without file + */ + self::assertEquals('', Miscellaneous::getFileNameFromPath('lorem/ipsum-dolor/sit-amet')); + + /* + * Path with a dot "." in name of directory + */ + self::assertEquals('sit.amet.JPG', Miscellaneous::getFileNameFromPath('lorem/ipsum.dolor/sit.amet.JPG')); + + /* + * Relative path + */ + self::assertEquals('sit.amet.JPG', Miscellaneous::getFileNameFromPath('lorem/ipsum/../dolor/sit.amet.JPG')); + } + + public function testGetUniqueFileName() + { + $originalFileName = 'Lorem.ipsum-dolor.sit.JPG'; + $pattern = '|^lorem\-ipsum\-dolor\-sit\-[a-z0-9.-]+\.jpg$|'; + + /* + * With object ID + */ + $uniqueFileName1 = Miscellaneous::getUniqueFileName($originalFileName, 123); + + /* + * Without object ID + */ + $uniqueFileName2 = Miscellaneous::getUniqueFileName($originalFileName); + + $isCorrect1 = (bool)preg_match($pattern, $uniqueFileName1); + $isCorrect2 = (bool)preg_match($pattern, $uniqueFileName2); + + self::assertTrue($isCorrect1); + self::assertTrue($isCorrect2); + } + + public function testValue2NonNegativeInteger() + { + self::assertEquals(2, Miscellaneous::value2NonNegativeInteger('2')); + self::assertEquals(0, Miscellaneous::value2NonNegativeInteger('a')); + self::assertEquals('-', Miscellaneous::value2NonNegativeInteger('-4', '-')); + } + + public function testIsPhpModuleLoaded() + { + $loadedExtensions = get_loaded_extensions(); + $firstExtension = $loadedExtensions[0]; + + self::assertTrue(Miscellaneous::isPhpModuleLoaded($firstExtension)); + self::assertFalse(Miscellaneous::isPhpModuleLoaded('xyz123')); + } + + public function testVariableDump() + { + $xdebugLoaded = Miscellaneous::isPhpModuleLoaded('xdebug'); + + $variable = 123; + $expected = "int(123)\n"; + + if ($xdebugLoaded) { + $libraryPath = realpath(sprintf('%s%s', dirname(__FILE__), '/../../../../..')); + $filePath = sprintf('%s%s', $libraryPath, '/src/Meritoo/Common/Utilities/Miscellaneous.php:'); + + /* + * Attention. I have to use "\d+" at the end of $filePath, because number of line may be different if new + * method / function will be created. + */ + $filePathQuoted = sprintf('%s\d+\:', preg_quote($filePath, '/')); + + $expectedPattern = sprintf("/%s\n%s/", $filePathQuoted, preg_quote($expected, '/')); + $this->expectOutputRegex($expectedPattern); + } else { + $expected = sprintf('
%s
', $expected); + $this->expectOutputString($expected); + } + + Miscellaneous::variableDump($variable); + } + + /** + * @param mixed $string Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testToLatinEmptyValue($string) + { + self::assertEquals('', Miscellaneous::toLatin($string)); + } + + /** + * @param string $expected Expected/converted string + * @param string $string String to convert + * @param string $replacementChar (optional) Replacement character for all non-latin characters + * + * @dataProvider provideStringToLatinNotLowerCaseHuman + */ + public function testToLatinNotLowerCaseHuman($expected, $string, $replacementChar = '-') + { + self::assertEquals($expected, Miscellaneous::toLatin($string, false, $replacementChar)); + } + + /** + * @param string $expected Expected/converted string + * @param string $string String to convert + * @param string $replacementChar (optional) Replacement character for all non-latin characters + * + * @dataProvider provideStringToLatinLowerCaseHuman + */ + public function testToLatinLowerCaseHuman($expected, $string, $replacementChar = '-') + { + self::assertEquals($expected, Miscellaneous::toLatin($string, true, $replacementChar)); + } + + public function testGetUniqueString() + { + $prefix = ''; + $hashed = false; + self::assertEquals(23, strlen(Miscellaneous::getUniqueString($prefix, $hashed))); + + $prefix = 'xyz'; + $hashed = false; + self::assertEquals(26, strlen(Miscellaneous::getUniqueString($prefix, $hashed))); + + $hashed = true; + self::assertEquals(40, strlen(Miscellaneous::getUniqueString($prefix, $hashed))); + } + + /** + * @param string|array $search An empty value to find + * @dataProvider provideEmptyValue + */ + public function testReplaceEmptyValue($search) + { + $replacement1 = ''; + $replacement2 = []; + + $replacement3 = [ + 'commodo', + 'interdum', + ]; + + self::assertEquals($this->stringSmall, Miscellaneous::replace($this->stringSmall, $search, $replacement1)); + self::assertEquals($this->stringSmall, Miscellaneous::replace($this->stringSmall, $search, $replacement2)); + self::assertEquals($this->stringSmall, Miscellaneous::replace($this->stringSmall, $search, $replacement3)); + + self::assertEquals($this->stringSmall, Miscellaneous::replace($this->stringSmall, $search, $replacement1, true)); + self::assertEquals($this->stringSmall, Miscellaneous::replace($this->stringSmall, $search, $replacement2, true)); + self::assertEquals($this->stringSmall, Miscellaneous::replace($this->stringSmall, $search, $replacement3, true)); + } + + public function testReplace() + { + /* + * Common variables + */ + $quoteStrings = true; + + $subject = [ + $this->stringSmall, + $this->stringDotSeparated, + ]; + + /* + * Testing replace with an array + */ + $search = [ + '|ipsum|', + '|pellentesque|', + ]; + + $replacement = [ + 'commodo', + 'interdum', + ]; + + $replaced1 = Miscellaneous::replace($subject, $search, $replacement); + $replaced2 = Miscellaneous::replace($subject, $search, $replacement, true); + + self::assertEquals('Lorem commodo dolor sit amet.', $replaced1[0]); + self::assertEquals('Etiam ullamcorper. Suspendisse a interdum dui, non felis.', $replaced1[1]); + + self::assertEquals('Lorem commodo dolor sit amet.', $replaced2[0]); + self::assertEquals('Etiam ullamcorper. Suspendisse a interdum dui, non felis.', $replaced2[1]); + + /* + * Testing replace with string + */ + $searchString = 'ipsum'; + $replacementString = 'commodo'; + + $replaced = Miscellaneous::replace($subject, $searchString, $replacementString, $quoteStrings); + self::assertEquals('Lorem \'commodo\' dolor sit amet.', $replaced[0]); + + /* + * Testing replace with mixed values: + * - subject: array + * - search: string + * - replacement: string + */ + $replacedMixed = Miscellaneous::replace($subject, $searchString, $replacement, $quoteStrings); + self::assertEquals('Lorem \'commodo\' dolor sit amet.', $replacedMixed[0]); + } + + public function testUppercaseFirst() + { + self::assertEquals('', Miscellaneous::uppercaseFirst('')); + self::assertEquals('', Miscellaneous::uppercaseFirst(null)); + self::assertEquals('', Miscellaneous::uppercaseFirst(false)); + + $text = 'lorEM ipsum dolor sit Amet'; + self::assertEquals('LorEM ipsum dolor sit Amet', Miscellaneous::uppercaseFirst($text)); + + $restLowercase = true; + self::assertEquals('Lorem ipsum dolor sit amet', Miscellaneous::uppercaseFirst($text, $restLowercase)); + + $restLowercase = false; + self::assertEquals('LOREM IPSUM DOLOR SIT AMET', Miscellaneous::uppercaseFirst($text, $restLowercase)); + } + + public function testLowercaseFirst() + { + self::assertEquals('', Miscellaneous::lowercaseFirst('')); + self::assertEquals('', Miscellaneous::lowercaseFirst(null)); + self::assertEquals('', Miscellaneous::lowercaseFirst(false)); + + $text = 'LorEM ipsum dolor sit Amet'; + self::assertEquals('lorEM ipsum dolor sit Amet', Miscellaneous::lowercaseFirst($text)); + + $restLowercase = true; + self::assertEquals('lorem ipsum dolor sit amet', Miscellaneous::lowercaseFirst($text, $restLowercase)); + + $restLowercase = false; + self::assertEquals('lOREM IPSUM DOLOR SIT AMET', Miscellaneous::lowercaseFirst($text, $restLowercase)); + } + + public function testGetNewFileName() + { + self::assertEquals('test.jpg', Miscellaneous::getNewFileName('test.jpg', '', '')); + self::assertEquals('my-test.jpg', Miscellaneous::getNewFileName('test.jpg', 'my-', '')); + self::assertEquals('test-file.jpg', Miscellaneous::getNewFileName('test.jpg', '', '-file')); + self::assertEquals('my-test-file.jpg', Miscellaneous::getNewFileName('test.jpg', 'my-', '-file')); + } + + public function testGetOperatingSystemNameServer() + { + self::assertEquals(php_uname('s'), Miscellaneous::getOperatingSystemNameServer()); + } + + public function testSubstringToWord() + { + $suffix = '...'; + + self::assertEquals('Lorem ipsum' . $suffix, Miscellaneous::substringToWord($this->stringCommaSeparated, 20)); + self::assertEquals('Lorem ipsum dolor sit' . $suffix, Miscellaneous::substringToWord($this->stringCommaSeparated, 25)); + + self::assertEquals('Lorem ipsum dolor', Miscellaneous::substringToWord($this->stringCommaSeparated, 20, '')); + self::assertEquals('Lorem ipsum dolor sit amet, consectetur', Miscellaneous::substringToWord($this->stringCommaSeparated, 40, '')); + } + + public function testBreakLongText() + { + self::assertEquals('Lorem ipsum dolor sit
amet, consectetur
adipiscing
elit', Miscellaneous::breakLongText($this->stringCommaSeparated, 20)); + self::assertEquals('Lorem ipsum dolor sit---amet, consectetur---adipiscing---elit', Miscellaneous::breakLongText($this->stringCommaSeparated, 20, '---')); + } + + public function testRemoveDirectory() + { + /* + * Removing not existing directory + */ + self::assertTrue(Miscellaneous::removeDirectory('/abc/def/ghi')); + + /* + * Removing not directory + */ + $directoryPath = sys_get_temp_dir() . '/ipsum.txt'; + touch($directoryPath); + self::assertTrue(Miscellaneous::removeDirectory($directoryPath)); + + /* + * Removing simple directory + */ + $directoryPath = sys_get_temp_dir() . '/lorem/ipsum'; + mkdir($directoryPath, 0777, true); + self::assertTrue(Miscellaneous::removeDirectory($directoryPath)); + + /* + * Removing more complex directory + */ + $directory1Path = sys_get_temp_dir() . '/lorem/ipsum'; + $directory2Path = sys_get_temp_dir() . '/lorem/dolor/sit'; + + mkdir($directory1Path, 0777, true); + mkdir($directory2Path, 0777, true); + self::assertTrue(Miscellaneous::removeDirectory(sys_get_temp_dir() . '/lorem', false)); + } + + /** + * @param string $text Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testRemoveStartingDirectorySeparatorEmptyValue($text) + { + self::assertEquals('', Miscellaneous::removeStartingDirectorySeparator($text)); + } + + /** + * @param string $text Text that may contain a directory's separator at the start / beginning + * @param string $separator The directory's separator, e.g. "/" + * @param string $expected Text without the starting / beginning directory's separator + * + * @dataProvider providePathsToRemoveStartingDirectorySeparator + */ + public function testRemoveStartingDirectorySeparator($text, $separator, $expected) + { + self::assertEquals($expected, Miscellaneous::removeStartingDirectorySeparator($text, $separator)); + } + + /** + * @param string $text Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testRemoveEndingDirectorySeparatorEmptyValue($text) + { + self::assertEquals('', Miscellaneous::removeEndingDirectorySeparator($text)); + } + + /** + * @param string $text Text that may contain a directory's separator at the end + * @param string $separator (optional) The directory's separator, e.g. "/" + * @param string $expected Text without the ending directory's separator + * + * @dataProvider providePathsToRemoveEndingDirectorySeparator + */ + public function testRemoveEndingDirectorySeparator($text, $separator, $expected) + { + self::assertEquals($expected, Miscellaneous::removeEndingDirectorySeparator($text, $separator)); + } + + public function testIsDecimal() + { + self::assertTrue(Miscellaneous::isDecimal(1.2)); + self::assertTrue(Miscellaneous::isDecimal('1.2')); + self::assertFalse(Miscellaneous::isDecimal('a')); + self::assertFalse(Miscellaneous::isDecimal(1)); + } + + public function testIsFilePath() + { + $filePath = __FILE__; + $directoryPath = dirname($filePath); + + self::assertTrue(Miscellaneous::isFilePath($filePath)); + self::assertFalse(Miscellaneous::isFilePath($directoryPath)); + } + + /** + * @param string $string The string to convert e.g. this-is-eXamplE (return: thisIsExample) + * @dataProvider provideEmptyValue + */ + public function testGetCamelCaseEmptyValue($string) + { + self::assertEquals('', Miscellaneous::getCamelCase($string)); + } + + /** + * @param string $string The string to convert e.g. this-is-eXamplE (return: thisIsExample) + * @param string $separator (optional) Separator used to find parts of the string, e.g. '-' or ',' + * @param string $expected String in camel case + * + * @dataProvider provideStringToCamelCase + */ + public function testGetCamelCase($string, $separator, $expected) + { + self::assertEquals($expected, Miscellaneous::getCamelCase($string, $separator)); + } + + public function testQuoteValue() + { + self::assertEquals(123, Miscellaneous::quoteValue(123)); + self::assertEquals('\'lorem ipsum\'', Miscellaneous::quoteValue('lorem ipsum')); + self::assertEquals('"lorem ipsum"', Miscellaneous::quoteValue('lorem ipsum', false)); + } + + public function testGetHumanReadableSize() + { + Locale::setLocale(LC_ALL, 'en', 'US'); + + self::assertEquals('400 B', Miscellaneous::getHumanReadableSize(400)); + self::assertEquals('1 KB', Miscellaneous::getHumanReadableSize(1024)); + self::assertEquals('1 MB', Miscellaneous::getHumanReadableSize(1024 * 1024)); + self::assertEquals('1.75 MB', Miscellaneous::getHumanReadableSize(1024 * 1024 * 1.75)); + } + + public function testGetLastElementOfString() + { + self::assertEquals('elit', Miscellaneous::getLastElementOfString($this->stringCommaSeparated, ' ')); + self::assertEquals('consectetur adipiscing elit', Miscellaneous::getLastElementOfString($this->stringCommaSeparated, ',')); + self::assertEquals(null, Miscellaneous::getLastElementOfString($this->stringCommaSeparated, ';')); + self::assertEquals(null, Miscellaneous::getLastElementOfString($this->stringCommaSeparated, '.')); + } + + public function testTrimSmart() + { + self::assertNull(Miscellaneous::trimSmart(null)); + self::assertEquals(' ', Miscellaneous::trimSmart(' ')); + self::assertEquals('lorem ipsum', Miscellaneous::trimSmart(' lorem ipsum')); + self::assertEquals('lorem ipsum', Miscellaneous::trimSmart(' lorem ipsum ')); + } + + public function testConcatenatePaths() + { + /* + * Common cases + */ + self::assertEquals('', Miscellaneous::concatenatePaths(null)); + self::assertEquals('', Miscellaneous::concatenatePaths([])); + + /* + * *nix operating system + */ + $paths1 = [ + 'first/directory', + 'second/one', + 'and/the/third', + ]; + + self::assertEquals('/' . implode('/', $paths1), Miscellaneous::concatenatePaths($paths1)); + self::assertEquals('/' . implode('/', $paths1), Miscellaneous::concatenatePaths($paths1[0], $paths1[1], $paths1[2])); + + /* + * For Windows operating system + */ + $paths2 = [ + 'C:\first\directory', + 'second\one', + 'and\the\third', + ]; + + self::assertEquals(implode('\\', $paths2), Miscellaneous::concatenatePaths($paths2)); + } + + public function testIncludeFileExtension() + { + $fileName = 'lorem-ipsum.jpg'; + + self::assertEquals($fileName, Miscellaneous::includeFileExtension($fileName, 'jpg')); + self::assertEquals(sprintf('%s.%s', $fileName, 'txt'), Miscellaneous::includeFileExtension($fileName, 'txt')); + } + + public function testGetStringElements() + { + $elements = [ + 'Lorem ipsum dolor sit amet', + ' consectetur adipiscing elit', + ]; + + self::assertEquals($elements, Miscellaneous::getStringElements($this->stringCommaSeparated, ',')); + self::assertEquals([], Miscellaneous::getStringElements($this->stringCommaSeparated, ';')); + } + + public function testGetStringWithoutLastElement() + { + self::assertEquals('Lorem ipsum dolor sit', Miscellaneous::getStringWithoutLastElement($this->stringSmall, ' ')); + self::assertEquals('', Miscellaneous::getStringWithoutLastElement($this->stringSmall, ';')); + } + + public function testGetSafelyGlobalVariable() + { + self::assertEquals('', Miscellaneous::getSafelyGlobalVariable(INPUT_GET, 'lorem')); + self::assertEquals('', Miscellaneous::getSafelyGlobalVariable(INPUT_POST, 'lorem')); + self::assertEquals('', Miscellaneous::getSafelyGlobalVariable(INPUT_COOKIE, 'lorem')); + self::assertEquals('', Miscellaneous::getSafelyGlobalVariable(INPUT_SERVER, 'lorem')); + self::assertEquals('', Miscellaneous::getSafelyGlobalVariable(INPUT_ENV, 'lorem')); + + $_GET['lorem'] = 123; + self::assertEquals(123, Miscellaneous::getSafelyGlobalVariable(INPUT_GET, 'lorem')); + } + + public function testIsBetween() + { + /* + * Negative cases + */ + self::assertFalse(Miscellaneous::isBetween(0, 0, 0)); + self::assertFalse(Miscellaneous::isBetween('0', '0', '0')); + self::assertFalse(Miscellaneous::isBetween(0, 0, 1)); + self::assertFalse(Miscellaneous::isBetween(-1, -1, -1)); + self::assertFalse(Miscellaneous::isBetween(1.2, 0.1, 1.1)); + + /* + * Positive cases + */ + self::assertTrue(Miscellaneous::isBetween(1, 0, 2)); + self::assertTrue(Miscellaneous::isBetween('1', '0', '2')); + self::assertTrue(Miscellaneous::isBetween(-1, -2, 2)); + self::assertTrue(Miscellaneous::isBetween(1.1, 0.1, 1.2)); + } + + public function testGetType() + { + self::assertEquals('NULL', Miscellaneous::getType(null)); + self::assertEquals('string', Miscellaneous::getType($this->stringSmall)); + self::assertEquals('integer', Miscellaneous::getType(123)); + self::assertEquals('double', Miscellaneous::getType(1.23)); + self::assertEquals('array', Miscellaneous::getType([])); + self::assertEquals('stdClass', Miscellaneous::getType(new stdClass())); + self::assertEquals(__CLASS__, Miscellaneous::getType(new self())); + } + + public function testGetValidColorComponent() + { + /* + * Negative cases + */ + self::assertEquals(0, Miscellaneous::getValidColorComponent(null)); + self::assertEquals(0, Miscellaneous::getValidColorComponent('')); + self::assertEquals(0, Miscellaneous::getValidColorComponent('0')); + self::assertEquals(0, Miscellaneous::getValidColorComponent(0)); + self::assertEquals(0, Miscellaneous::getValidColorComponent(256)); + self::assertEquals(0, Miscellaneous::getValidColorComponent(256, false)); + + /* + * Positive cases - part 1 + */ + self::assertEquals(1, Miscellaneous::getValidColorComponent(1)); + self::assertEquals('0a', Miscellaneous::getValidColorComponent(10)); + self::assertEquals('0f', Miscellaneous::getValidColorComponent(15)); + self::assertEquals(64, Miscellaneous::getValidColorComponent(100)); + self::assertEquals('ff', Miscellaneous::getValidColorComponent(255)); + + /* + * Positive cases - part 2 + */ + self::assertEquals(1, Miscellaneous::getValidColorComponent(1, false)); + self::assertEquals(10, Miscellaneous::getValidColorComponent(10, false)); + self::assertEquals(15, Miscellaneous::getValidColorComponent(15, false)); + self::assertEquals(100, Miscellaneous::getValidColorComponent(100, false)); + self::assertEquals(255, Miscellaneous::getValidColorComponent(255, false)); + } + + public function testGetInvertedColorWithIncorrectLength() + { + $this->expectException(IncorrectColorHexLengthException::class); + Miscellaneous::getInvertedColor(null); + Miscellaneous::getInvertedColor(''); + Miscellaneous::getInvertedColor(1); + Miscellaneous::getInvertedColor(12); + Miscellaneous::getInvertedColor(1234567); + Miscellaneous::getInvertedColor('1'); + Miscellaneous::getInvertedColor('12'); + Miscellaneous::getInvertedColor('1234567'); + } + + public function testGetInvertedColorWithInvalidValue() + { + $this->expectException(InvalidColorHexValueException::class); + Miscellaneous::getInvertedColor('0011zz'); + Miscellaneous::getInvertedColor('001#zz'); + Miscellaneous::getInvertedColor('001!zz'); + Miscellaneous::getInvertedColor('001-zz'); + Miscellaneous::getInvertedColor('00ppqq'); + } + + public function testGetInvertedColor() + { + /* + * Simple cases + */ + self::assertEquals('000000', Miscellaneous::getInvertedColor('fff')); + self::assertEquals('ffffff', Miscellaneous::getInvertedColor('000')); + self::assertEquals('000000', Miscellaneous::getInvertedColor('ffffff')); + self::assertEquals('ffffff', Miscellaneous::getInvertedColor('000000')); + self::assertEquals('#000000', Miscellaneous::getInvertedColor('#ffffff')); + self::assertEquals('#ffffff', Miscellaneous::getInvertedColor('#000000')); + + /* + * Advanced cases - part 1 + */ + self::assertEquals('ffffee', Miscellaneous::getInvertedColor('001')); + self::assertEquals('ffeeff', Miscellaneous::getInvertedColor('010')); + self::assertEquals('eeffff', Miscellaneous::getInvertedColor('100')); + self::assertEquals('333333', Miscellaneous::getInvertedColor('ccc')); + self::assertEquals('333333', Miscellaneous::getInvertedColor('CCC')); + + /* + * Advanced cases - part 2 + */ + self::assertEquals('3e3e3e', Miscellaneous::getInvertedColor('c1c1c1')); + self::assertEquals('3e3e3e', Miscellaneous::getInvertedColor('C1C1C1')); + self::assertEquals('#dd5a01', Miscellaneous::getInvertedColor('#22a5fe')); + self::assertEquals('#22dbb3', Miscellaneous::getInvertedColor('#dd244c')); + self::assertEquals('#464646', Miscellaneous::getInvertedColor('#b9b9b9')); + self::assertEquals('#080808', Miscellaneous::getInvertedColor('#f7f7f7')); + + /* + * Advanced cases - verification + */ + self::assertEquals('000011', Miscellaneous::getInvertedColor('ffffee')); + self::assertEquals('cccccc', Miscellaneous::getInvertedColor('333333')); + self::assertEquals('#22a5fe', Miscellaneous::getInvertedColor('#dd5a01')); + self::assertEquals('#22a5fe', Miscellaneous::getInvertedColor('#DD5A01')); + self::assertEquals('#f7f7f7', Miscellaneous::getInvertedColor('#080808')); + } + + /** + * @param mixed $number Number for who the "0" characters should be inserted + * @dataProvider provideEmptyValueToFillMissingZeros + */ + public function testFillMissingZerosEmptyValue($number) + { + self::assertEquals('', Miscellaneous::fillMissingZeros($number, 1)); + } + + /** + * @param mixed $number Number for who the "0" characters should be inserted + * @param int $length Wanted length of final number + * @param bool $before If false, 0 characters will be inserted after given number + * @param string $expected String with added missing the "0" characters + * + * @dataProvider provideNumberToFillMissingZeros + */ + public function testFillMissingZeros($number, $length, $before, $expected) + { + self::assertSame($expected, Miscellaneous::fillMissingZeros($number, $length, $before)); + } + + /** + * Provides string to convert characters to latin characters and not lower cased and not human-readable + * + * @return Generator + */ + public function provideStringToLatinNotLowerCaseHuman() + { + yield[ + 'asuo', + 'ąśüö', + ]; + + yield[ + 'eoaslzzcn', + 'ęóąśłżźćń', + ]; + + yield[ + 'loremipsum', + 'loremipsum', + ]; + + yield[ + 'LoremIpsum', + 'LoremIpsum', + ]; + + yield[ + 'Lorem.Ipsum', + 'Lorem Ipsum', + '.', + ]; + + yield[ + 'Lorem.Ipsum', + 'Lorem=Ipsum', + '.', + ]; + + yield[ + 'LoremIpsumD', + 'LoremIpsumD', + ]; + + yield[ + 'LoremIpsumD', + 'LoremIpsumD', + '.', + ]; + + yield[ + 'lorem-ipsum', + 'lorem ipsum', + ]; + + yield[ + 'lorem-ipsum', + 'lorem;ipsum', + ]; + + yield[ + 'lorem1ipsum2', + 'lorem1ipsum2', + ]; + + yield[ + 'lorem_ipsum', + 'lorem ipsum', + '_', + ]; + + yield[ + 'LoremIpsum', + 'LoremIpsum', + ]; + + yield[ + 'Lorem Ipsum', + 'Lorem!Ipsum', + ' ', + ]; + + yield[ + 'Lorem Ipsum', + 'Lorem.Ipsum', + ' ', + ]; + + yield[ + 'Lorem|Ipsum', + 'Lorem.Ipsum', + '|', + ]; + } + + /** + * Provides string to convert characters to latin characters and lower cased and human-readable + * + * @return Generator + */ + public function provideStringToLatinLowerCaseHuman() + { + yield[ + 'asuo', + 'ąśüö', + ]; + + yield[ + 'eoaslzzcn', + 'ęóąśłżźćń', + ]; + + yield[ + 'loremipsum', + 'loremipsum', + ]; + + yield[ + 'lorem-ipsum', + 'lorem ipsum', + ]; + + yield[ + 'lorem-ipsum', + 'lorem;ipsum', + ]; + + yield[ + 'lorem1ipsum2', + 'lorem1ipsum2', + ]; + + yield[ + 'lorem_ipsum', + 'lorem ipsum', + '_', + ]; + + yield[ + 'lorem-ipsum', + 'lorem-ipsum', + ]; + + yield[ + 'lorem ipsum', + 'Lorem!Ipsum', + ' ', + ]; + + yield[ + 'lorem ipsum', + 'Lorem.Ipsum', + ' ', + ]; + + yield[ + 'lorem|ipsum', + 'Lorem.Ipsum', + '|', + ]; + + yield[ + 'lorem-ipsum', + 'LoremIpsum', + ]; + + yield[ + 'lorem.ipsum', + 'Lorem Ipsum', + '.', + ]; + + yield[ + 'lorem.ipsum', + 'Lorem=Ipsum', + '.', + ]; + + yield[ + 'lorem-ipsum-d', + 'LoremIpsumD', + ]; + + yield[ + 'lorem.ipsum.d', + 'LoremIpsumD', + '.', + ]; + } + + /** + * Provides names of files + * + * @return Generator + */ + public function provideFileNames() + { + yield[ + 'Lorem.ipsum-dolor.sit.JPG', + 'Lorem.ipsum-dolor.sit', + ]; + + yield[ + 'lets-test.doc', + 'lets-test', + ]; + + yield[ + 'something/else.txt', + 'something/else', + ]; + + yield[ + 'public/js/user.js', + 'public/js/user', + ]; + } + + /** + * Provides string to convert to camel case + * + * @return Generator + */ + public function provideStringToCamelCase() + { + yield[ + 'lorem ipsum', + ' ', + 'loremIpsum', + ]; + + yield[ + 'Lorem ipSum Dolor', + ' ', + 'loremIpsumDolor', + ]; + + yield[ + 'abc;def;ghi', + ';', + 'abcDefGhi', + ]; + } + + /** + * Provides path used to remove the starting / beginning directory's separator + * + * @return Generator + */ + public function providePathsToRemoveStartingDirectorySeparator() + { + yield[ + '/lorem/ipsum/dolor', + '/', + 'lorem/ipsum/dolor', + ]; + + yield[ + 'lorem/ipsum/dolor', + '/', + 'lorem/ipsum/dolor', + ]; + + yield[ + '\\lorem\ipsum\dolor', + '\\', + 'lorem\ipsum\dolor', + ]; + + yield[ + 'lorem\ipsum\dolor', + '\\', + 'lorem\ipsum\dolor', + ]; + + yield[ + ';lorem;ipsum;dolor', + ';', + 'lorem;ipsum;dolor', + ]; + + yield[ + 'lorem;ipsum;dolor', + ';', + 'lorem;ipsum;dolor', + ]; + } + + /** + * Provides path used to remove the ending directory's separator + * + * @return Generator + */ + public function providePathsToRemoveEndingDirectorySeparator() + { + yield[ + 'lorem/ipsum/dolor/', + '/', + 'lorem/ipsum/dolor', + ]; + + yield[ + 'lorem/ipsum/dolor', + '/', + 'lorem/ipsum/dolor', + ]; + + yield[ + 'lorem\ipsum\dolor\\', + '\\', + 'lorem\ipsum\dolor', + ]; + + yield[ + 'lorem\ipsum\dolor', + '\\', + 'lorem\ipsum\dolor', + ]; + + yield[ + 'lorem;ipsum;dolor;', + ';', + 'lorem;ipsum;dolor', + ]; + + yield[ + 'lorem;ipsum;dolor', + ';', + 'lorem;ipsum;dolor', + ]; + } + + /** + * Provides empty value used to fill missing zeros + * + * @return Generator + */ + public function provideEmptyValueToFillMissingZeros() + { + yield['']; + yield[' ']; + yield[null]; + yield[false]; + yield[[]]; + } + + /** + * Provides number used to fill missing zeros + * + * @return Generator + */ + public function provideNumberToFillMissingZeros() + { + yield[ + 0, + 0, + true, + '0', + ]; + + yield[ + 0, + 0, + false, + '0', + ]; + + yield[ + 1, + 0, + true, + '1', + ]; + + yield[ + 1, + 0, + false, + '1', + ]; + + yield[ + 1, + 1, + true, + '1', + ]; + + yield[ + 1, + 1, + false, + '1', + ]; + + yield[ + 123, + 5, + true, + '00123', + ]; + + yield[ + 123, + 5, + false, + '12300', + ]; + } + + /** + * {@inheritdoc} + */ + protected function setUp() + { + parent::setUp(); + + $this->stringSmall = 'Lorem ipsum dolor sit amet.'; + $this->stringCommaSeparated = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'; + $this->stringDotSeparated = 'Etiam ullamcorper. Suspendisse a pellentesque dui, non felis.'; + } + + /** + * {@inheritdoc} + */ + protected function tearDown() + { + parent::tearDown(); + + unset($this->stringSmall); + unset($this->stringCommaSeparated); + unset($this->stringDotSeparated); + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/Reflection/A.php b/tests/Meritoo/Common/Tests/Utilities/Reflection/A.php new file mode 100644 index 0000000..7416328 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/Reflection/A.php @@ -0,0 +1,20 @@ + + * @copyright Meritoo.pl + */ +class A +{ + use E; + + protected function lorem() + { + return 'ipsum'; + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/Reflection/B.php b/tests/Meritoo/Common/Tests/Utilities/Reflection/B.php new file mode 100644 index 0000000..f6bfbba --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/Reflection/B.php @@ -0,0 +1,14 @@ + + * @copyright Meritoo.pl + */ +class B extends A +{ +} diff --git a/tests/Meritoo/Common/Tests/Utilities/Reflection/C.php b/tests/Meritoo/Common/Tests/Utilities/Reflection/C.php new file mode 100644 index 0000000..36309df --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/Reflection/C.php @@ -0,0 +1,23 @@ + + * @copyright Meritoo.pl + */ +class C extends B +{ + public function getPositive() + { + return true; + } + + public function getNegative() + { + return false; + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/Reflection/D.php b/tests/Meritoo/Common/Tests/Utilities/Reflection/D.php new file mode 100644 index 0000000..fbd2687 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/Reflection/D.php @@ -0,0 +1,14 @@ + + * @copyright Meritoo.pl + */ +class D +{ +} diff --git a/tests/Meritoo/Common/Tests/Utilities/Reflection/E.php b/tests/Meritoo/Common/Tests/Utilities/Reflection/E.php new file mode 100644 index 0000000..9f15f44 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/Reflection/E.php @@ -0,0 +1,14 @@ + + * @copyright Meritoo.pl + */ +trait E +{ +} diff --git a/tests/Meritoo/Common/Tests/Utilities/ReflectionTest.php b/tests/Meritoo/Common/Tests/Utilities/ReflectionTest.php new file mode 100644 index 0000000..9c5ab80 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/ReflectionTest.php @@ -0,0 +1,246 @@ + + * @copyright Meritoo.pl + */ +class ReflectionTest extends TestCase +{ + /** + * @param mixed $invalidClass Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetClassNameInvalidClass($invalidClass) + { + self::assertNull(Reflection::getClassName($invalidClass)); + self::assertNull(Reflection::getClassName(123)); + } + + public function testGetClassNameNotExistingClass() + { + /* + * Not existing class + */ + self::assertEquals('', Reflection::getClassName('xyz')); + self::assertEquals('', Reflection::getClassName('xyz', true)); + } + + public function testGetClassNameExistingClass() + { + /* + * Existing class + */ + self::assertEquals(self::class, Reflection::getClassName(self::class)); + self::assertEquals('ReflectionTest', Reflection::getClassName(self::class, true)); + self::assertEquals(DateTime::class, Reflection::getClassName(new DateTime())); + self::assertEquals(DateTime::class, Reflection::getClassName(new DateTime(), true)); + + self::assertEquals(DateTime::class, Reflection::getClassName([ + new DateTime(), + new DateTime('yesterday'), + ])); + } + + public function testGetClassNameDuplicatedName() + { + /* + * Class with namespace containing name of class (duplicated string) + */ + if (class_exists('Symfony\Bundle\SecurityBundle\SecurityBundle')) { + self::assertEquals('Symfony\Bundle\SecurityBundle\SecurityBundle', Reflection::getClassName('Symfony\Bundle\SecurityBundle\SecurityBundle')); + self::assertEquals('SecurityBundle', Reflection::getClassName('Symfony\Bundle\SecurityBundle\SecurityBundle', true)); + } + } + + public function testGetClassNamespaceNotExistingClass() + { + /* + * Not existing class + */ + self::assertEquals('', Reflection::getClassNamespace('xyz')); + } + + public function testGetClassNamespaceExistingClass() + { + /* + * Existing class + */ + self::assertEquals('Meritoo\Common\Tests\Utilities', Reflection::getClassNamespace(self::class)); + self::assertEquals(DateTime::class, Reflection::getClassNamespace(new DateTime())); + + self::assertEquals(DateTime::class, Reflection::getClassNamespace([ + new DateTime(), + new DateTime('yesterday'), + ])); + } + + public function testGetClassNamespaceDuplicatedName() + { + /* + * Class with namespace containing name of class (duplicated string) + */ + if (class_exists('Symfony\Bundle\SecurityBundle\SecurityBundle')) { + self::assertEquals('Symfony\Bundle\SecurityBundle', Reflection::getClassNamespace('Symfony\Bundle\SecurityBundle\SecurityBundle')); + } + } + + /** + * @param mixed $invalidClass Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testGetChildClassesInvalidClass($invalidClass) + { + $this->expectException(CannotResolveClassNameException::class); + + self::assertNull(Reflection::getChildClasses($invalidClass)); + self::assertNull(Reflection::getChildClasses(123)); + } + + public function testGetChildClassesNotExistingClass() + { + $this->expectException(CannotResolveClassNameException::class); + self::assertEquals('', Reflection::getChildClasses('xyz')); + } + + public function testGetChildClassesExistingClass() + { + /* + * Attention. I have to create instances of these classes to load them and be available while using + * get_declared_classes() function in the Reflection::getChildClasses() method. Without these instances the + * Reflection::getChildClasses() method returns an empty array even if given class has child classes. + */ + new A(); + new B(); + new C(); + + $effect = [ + C::class, + ]; + + self::assertEquals($effect, Reflection::getChildClasses(B::class)); + + $effect = [ + B::class, + C::class, + ]; + + self::assertEquals($effect, Reflection::getChildClasses(A::class)); + } + + public function testGetOneChildClassWithMissingChildClasses() + { + $this->expectException(MissingChildClassesException::class); + self::assertEquals('LoremIpsum', Reflection::getOneChildClass(C::class)); + } + + public function testGetOneChildClassWithTooManyChildClasses() + { + $this->expectException(TooManyChildClassesException::class); + + self::assertEquals(B::class, Reflection::getOneChildClass(A::class)); + self::assertEquals(C::class, Reflection::getOneChildClass(A::class)); + } + + public function testGetOneChildClass() + { + self::assertEquals(C::class, Reflection::getOneChildClass(B::class)); + } + + public function testGetMethods() + { + self::assertEquals(0, count(Reflection::getMethods(B::class, true))); + self::assertEquals(1, count(Reflection::getMethods(B::class))); + self::assertEquals(1, count(Reflection::getMethods(A::class))); + self::assertEquals(2, count(Reflection::getMethods(C::class, true))); + self::assertEquals(3, count(Reflection::getMethods(C::class))); + } + + /** + * @param array|object|string $class An array of objects, namespaces, object or namespace + * @param array|string $trait An array of strings or string + * + * @dataProvider provideInvalidClassAndTrait + */ + public function testUsesTraitInvalidClass($class, $trait) + { + $this->expectException(CannotResolveClassNameException::class); + self::assertNull(Reflection::usesTrait($class, $trait)); + } + + /** + * @param mixed $trait Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testUsesTraitInvalidTrait($trait) + { + $this->expectException(CannotResolveClassNameException::class); + self::assertNull(Reflection::usesTrait(DateTime::class, $trait)); + } + + public function testUsesTraitExistingClass() + { + self::assertTrue(Reflection::usesTrait(A::class, E::class)); + self::assertFalse(Reflection::usesTrait(B::class, E::class)); + self::assertFalse(Reflection::usesTrait(C::class, E::class)); + self::assertFalse(Reflection::usesTrait(D::class, E::class)); + } + + public function testUsesTraitExistingClassAndVerifyParents() + { + self::assertTrue(Reflection::usesTrait(A::class, E::class, true)); + self::assertTrue(Reflection::usesTrait(B::class, E::class, true)); + self::assertTrue(Reflection::usesTrait(C::class, E::class, true)); + self::assertFalse(Reflection::usesTrait(D::class, E::class, true)); + } + + /** + * Provides invalid class and trait + * + * @return Generator + */ + public function provideInvalidClassAndTrait() + { + yield[ + '', + '', + ]; + + yield[ + null, + null, + ]; + + yield[ + 0, + 0, + ]; + + yield[ + [], + [], + ]; + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/RegexTest.php b/tests/Meritoo/Common/Tests/Utilities/RegexTest.php new file mode 100644 index 0000000..da76e8f --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/RegexTest.php @@ -0,0 +1,291 @@ + + * @copyright Meritoo.pl + */ +class RegexTest extends \PHPUnit_Framework_TestCase +{ + private $simpleText; + + private $camelCaseText; + + public function testGetCamelCaseParts() + { + $parts = []; + self::assertEquals($parts, Regex::getCamelCaseParts('')); + + $parts = [ + 'lorem', + ]; + + self::assertEquals($parts, Regex::getCamelCaseParts('lorem')); + + $parts = [ + 'lorem', + 'Ipsum', + 'Dolor', + 'Sit', + ]; + + self::assertEquals($parts, Regex::getCamelCaseParts($this->camelCaseText)); + + $parts = [ + 'Lorem', + 'Ipsum', + 'Dolor', + 'Sit', + ]; + + $string = ucfirst($this->camelCaseText); // 'LoremIpsumDolorSit' + self::assertEquals($parts, Regex::getCamelCaseParts($string)); + } + + public function testCamelCase2humanReadable() + { + self::assertEquals('', Regex::camelCase2humanReadable('')); + self::assertEquals('lorem', Regex::camelCase2humanReadable('lorem')); + + self::assertEquals($this->simpleText, Regex::camelCase2humanReadable($this->camelCaseText)); + self::assertEquals(ucfirst($this->simpleText), Regex::camelCase2humanReadable($this->camelCaseText, true)); + } + + public function testCamelCase2simpleLowercase() + { + self::assertEquals('', Regex::camelCase2simpleLowercase('')); + self::assertEquals('lorem', Regex::camelCase2simpleLowercase('lorem')); + self::assertEquals('Lorem', Regex::camelCase2simpleLowercase('Lorem', '', false)); + self::assertEquals('lorem-ipsum-dolor-sit', Regex::camelCase2simpleLowercase($this->camelCaseText, '-')); + self::assertEquals('lorem-Ipsum-Dolor-Sit', Regex::camelCase2simpleLowercase($this->camelCaseText, '-', false)); + } + + public function testIsValidUrl() + { + $validUrls = [ + 'http://php.net', + 'http://php.net/', + 'http://php.net/docs.php', + 'http://php.net/get-involved.php', + 'http://php.net/manual/en/function.preg-match.php', + 'http://domain.com/BigLetters', + 'http://domain.com/Another-Big-Letters', + 'http://domain.com/?a=1&b=c2d', + 'http://domAin.COM/?a=1&B=c2D', + 'http://domain.com/index.php?a=1&b=c2d', + 'http://domain.com/another-page-2.php?a=1&b=c2d', + 'https://domain.com', + 'https://domain.com/', + ]; + + $invalidUrls = [ + '', + null, + false, + true, + 0, + 1, + 123, + '123', + 'http:', + 'http://', + 'http://abc', + 'ftp://def', + ]; + + foreach ($validUrls as $url) { + self::assertTrue(Regex::isValidUrl($url)); + } + + foreach ($invalidUrls as $url) { + self::assertFalse(Regex::isValidUrl($url)); + } + } + + public function testIsSubPathOf() + { + self::assertFalse(Regex::isSubPathOf(null, null)); + self::assertFalse(Regex::isSubPathOf('', '')); + + self::assertFalse(Regex::isSubPathOf('', '/my/directory')); + self::assertFalse(Regex::isSubPathOf('/my/file', '')); + self::assertFalse(Regex::isSubPathOf('/my/file', '/my/directory')); + + self::assertTrue(Regex::isSubPathOf('/my/directory', '/my/directory')); + self::assertTrue(Regex::isSubPathOf('/my/directory/', '/my/directory')); + self::assertTrue(Regex::isSubPathOf('/my/directory', '/my/directory/')); + self::assertTrue(Regex::isSubPathOf('/my/directory/', '/my/directory/')); + + self::assertTrue(Regex::isSubPathOf('/my/another/directory/another/file', '/my/another/directory')); + } + + public function testIsLetterOrDigit() + { + self::assertTrue(Regex::isLetterOrDigit('a')); + self::assertTrue(Regex::isLetterOrDigit(10)); + self::assertFalse(Regex::isLetterOrDigit(';')); + } + + public function testStartsWith() + { + $string = 'Lorem ipsum dolor sit amet'; + + $beginning = 'Lor'; + self::assertTrue(Regex::startsWith($string, $beginning)); + + $beginning = 'L'; + self::assertTrue(Regex::startsWith($string, $beginning)); + + $beginning = 'X'; + self::assertFalse(Regex::startsWith($string, $beginning)); + + $string = '1234567890'; + $beginning = '1'; + self::assertTrue(Regex::startsWith($string, $beginning)); + + $beginning = ';'; + self::assertFalse(Regex::startsWith($string, $beginning)); + } + + public function testStartsWithDirectorySeparator() + { + /* + * Slash as separator + */ + $separatorSlash = '/'; + + self::assertTrue(Regex::startsWithDirectorySeparator('/my/extra/directory', $separatorSlash)); + self::assertFalse(Regex::startsWithDirectorySeparator('my/extra/directory', $separatorSlash)); + + /* + * Backslash as separator + */ + $separatorBackslash = '\\'; + + self::assertTrue(Regex::startsWithDirectorySeparator('\my\extra\directory', $separatorBackslash)); + self::assertFalse(Regex::startsWithDirectorySeparator('my\extra\directory', $separatorBackslash)); + } + + public function testEndsWithDirectorySeparator() + { + /* + * Slash as separator + */ + $separatorSlash = '/'; + + self::assertTrue(Regex::endsWithDirectorySeparator('my simple text/', $separatorSlash)); + self::assertFalse(Regex::endsWithDirectorySeparator('my simple text', $separatorSlash)); + + /* + * Backslash as separator + */ + $separatorBackslash = '\\'; + + self::assertTrue(Regex::endsWithDirectorySeparator('my simple text\\', $separatorBackslash)); + self::assertFalse(Regex::endsWithDirectorySeparator('my simple text', $separatorBackslash)); + } + + public function testEndsWith() + { + self::assertFalse(Regex::endsWith($this->simpleText, '\.\.\.')); + self::assertFalse(Regex::endsWith($this->simpleText, '\.')); + self::assertTrue(Regex::endsWith($this->simpleText, 't')); + } + + public function testIsSetUriParameter() + { + $uri = 'www.domain.com/?name=phil&type=4'; + + $parameterName = 'type'; + self::assertTrue(Regex::isSetUriParameter($uri, $parameterName)); + + $parameterName = 'color'; + self::assertFalse(Regex::isSetUriParameter($uri, $parameterName)); + } + + public function testContainsEntities() + { + self::assertFalse(Regex::containsEntities('Lorem ipsum')); + self::assertTrue(Regex::containsEntities('Lorem ipsum »')); + } + + public function testContains() + { + self::assertTrue(Regex::contains($this->simpleText, 'ipsum')); + self::assertFalse(Regex::contains($this->simpleText, 'neque')); + } + + public function testIsFileName() + { + $filePath = __FILE__; + $directoryPath = dirname($filePath); + + self::assertTrue(Regex::isFileName($filePath)); + self::assertFalse(Regex::isFileName($directoryPath)); + } + + public function testIsQuoted() + { + self::assertTrue(Regex::isQuoted('\'lorem ipsum\'')); + self::assertTrue(Regex::isQuoted('"lorem ipsum"')); + + self::assertFalse(Regex::isQuoted('lorem ipsum')); + self::assertFalse(Regex::isQuoted(new \stdClass())); + } + + public function testIsWindowsBasedPath() + { + self::assertTrue(Regex::isWindowsBasedPath('C:\path\to\directory')); + self::assertTrue(Regex::isWindowsBasedPath('C:\path\to\file.jpg')); + + self::assertFalse(Regex::isWindowsBasedPath('/path/to/directory')); + self::assertFalse(Regex::isWindowsBasedPath('/path/to/file.jpg')); + } + + public function testIsValidNip() + { + self::assertFalse(Regex::isValidNip(null)); + self::assertFalse(Regex::isValidNip('')); + self::assertFalse(Regex::isValidNip(1234)); + self::assertFalse(Regex::isValidNip(1234567890)); + self::assertFalse(Regex::isValidNip(0000000000)); + self::assertFalse(Regex::isValidNip('1234567890')); + self::assertFalse(Regex::isValidNip('0000000000')); + self::assertFalse(Regex::isValidNip('abc')); + self::assertFalse(Regex::isValidNip($this->simpleText)); + + self::assertTrue(Regex::isValidNip('7340009469')); // Onet S.A. + self::assertTrue(Regex::isValidNip('5252530705')); // Facebook Poland sp. z o.o. + } + + /** + * {@inheritdoc} + */ + protected function setUp() + { + parent::setUp(); + + $this->simpleText = 'lorem ipsum dolor sit'; + $this->camelCaseText = str_replace(' ', '', lcfirst(ucwords($this->simpleText))); // 'loremIpsumDolorSit' + } + + /** + * {@inheritdoc} + */ + protected function tearDown() + { + parent::tearDown(); + + unset($this->simpleText); + unset($this->camelCaseText); + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/UriTest.php b/tests/Meritoo/Common/Tests/Utilities/UriTest.php new file mode 100644 index 0000000..edff3b7 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/UriTest.php @@ -0,0 +1,78 @@ + + * @copyright Meritoo.pl + */ +class UriTest extends TestCase +{ + public function testAddProtocolToUrl() + { + $http = 'http'; + $https = 'https'; + + $url = 'my.domain/some/url'; + $httpUrl = sprintf('%s://%s', $http, $url); + $httpsUrl = sprintf('%s://%s', $https, $url); + + self::assertEquals($httpUrl, Uri::addProtocolToUrl($httpUrl)); + self::assertEquals($httpUrl, Uri::addProtocolToUrl($url)); + + self::assertEquals($httpsUrl, Uri::addProtocolToUrl($url, $https)); + self::assertEquals($httpsUrl, Uri::addProtocolToUrl($httpsUrl, $http)); + } + + /** + * @param mixed $url Empty value, e.g. "" + * @dataProvider provideEmptyValue + */ + public function testReplenishProtocolEmptyUrl($url) + { + self::assertEquals('', Uri::replenishProtocol($url)); + } + + /** + * @param string $expected Expected result + * @param string $url The url to check and replenish + * @param string $protocol (optional) The protocol which is replenished. If is empty, protocol of current request + * is used. + * + * @dataProvider provideUrlsToReplenishProtocol + */ + public function testReplenishProtocol($expected, $url, $protocol = '') + { + self::assertSame($expected, Uri::replenishProtocol($url, $protocol)); + } + + /** + * Provides urls to replenish protocol + * + * @return \Generator + */ + public function provideUrlsToReplenishProtocol() + { + yield[ + '://test', + 'test', + '', + ]; + + yield[ + 'ftp://lorem.ipsum', + 'lorem.ipsum', + 'ftp', + ]; + } +} diff --git a/tests/Meritoo/Common/Tests/Utilities/XmlTest.php b/tests/Meritoo/Common/Tests/Utilities/XmlTest.php new file mode 100644 index 0000000..7faf5c1 --- /dev/null +++ b/tests/Meritoo/Common/Tests/Utilities/XmlTest.php @@ -0,0 +1,90 @@ + + * @copyright Meritoo.pl + */ +class XmlTest extends \PHPUnit_Framework_TestCase +{ + private $simpleXml; + private $advancedXml; + + public function testMergeNodes() + { + /* + * An empty XMLs + */ + $element1 = new SimpleXMLElement(''); + $element2 = new SimpleXMLElement(''); + + $merged = Xml::mergeNodes($element1, $element2); + self::assertEquals('', (string)$merged); + + /* + * XMLs with data + */ + $element1 = new SimpleXMLElement($this->simpleXml); + $element2 = new SimpleXMLElement($this->advancedXml); + + $merged = Xml::mergeNodes($element1, $element2); + self::assertEquals('John', (string)$merged->author[0]->first_name); + } + + /** + * {@inheritdoc} + */ + protected function setUp() + { + parent::setUp(); + + $this->simpleXml = ' + + Lorem ipsum + Dolor sit amet + Consectetur adipiscing elit + Donec ut + Mi a magna + Dapibus bibendum + + '; + + $this->advancedXml = ' + + + John + Scott + john.scott@fake.email + + + Julia + Brown + julia.brown@fake.email + + + '; + } + + /** + * {@inheritdoc} + */ + protected function tearDown() + { + parent::tearDown(); + + unset($this->simpleXml); + unset($this->advancedXml); + } +}