]> git.andy128k.dev Git - missing-tools.git/commitdiff
cleanup. add tests
authorAndrey Kutejko <andy128k@gmail.com>
Tue, 19 Mar 2019 22:49:43 +0000 (23:49 +0100)
committerAndrey Kutejko <andy128k@gmail.com>
Tue, 19 Mar 2019 23:36:07 +0000 (00:36 +0100)
37 files changed:
.gitignore
composer.json
composer.lock
src/algorithm.php [deleted file]
src/cli.php [deleted file]
src/collection.php
src/container.php [deleted file]
src/dateformat.php
src/function.php [deleted file]
src/htmlbuilder.php
src/io/pushback.php [deleted file]
src/map.php [new file with mode: 0644]
src/maplike.php [new file with mode: 0644]
src/mixins.php [deleted file]
src/multidimensionalarray.php [deleted file]
src/native_template.php
src/regexp.php
src/shell.php [deleted file]
src/str.php
src/symbol.php [deleted file]
src/topsort.php [new file with mode: 0644]
t/ArrTest.php
t/CollectionTest.php
t/ContainerTest.php [deleted file]
t/FormatTest.php
t/FunctionTest.php [deleted file]
t/HtmlBuilderTest.php
t/MapLikeTest.php [new file with mode: 0644]
t/MapTest.php [new file with mode: 0644]
t/MixinsTest.php [deleted file]
t/MultidimensionalArrayTest.php [deleted file]
t/RegexpTest.php [new file with mode: 0644]
t/TopSortTest.php [new file with mode: 0644]
t/native_template/NativeTemplateTest.php
t/native_template/layout.html [new file with mode: 0644]
t/native_template/page.html [new file with mode: 0644]
t/native_template/simple.html

index fcac4a70c7dcb7dd5a38f322d2af8ff6a07cc04c..bf00556f4cf4d535bf3787b7db2863f1da771a35 100644 (file)
@@ -1,2 +1,4 @@
 /vendor
-
+/coverage
+*.iml
+.idea
index 8dbdfc7c5db558a57cadfc8bd22e307b341ee2d1..8a78decc7c0aeceaf60caac9afca731a09f9e080 100644 (file)
     "classmap" : ["src/"]
   },
   "require": {
-    "php": ">=5.3",
+    "php": ">=5.6",
     "andy128k/pegp": "0.2"
   },
   "require-dev": {
-    "phpunit/phpunit": "4.4.*"
+    "phpunit/phpunit": "~5"
   },
   "config": {
     "platform": {
@@ -24,6 +24,7 @@
     }
   },
   "scripts": {
-    "test": "phpunit ./t"
+    "test": "phpunit ./t",
+    "coverage": "phpunit t --coverage-html coverage --whitelist src"
   }
 }
index 6763bc86707bc77cc49c9d577f2ac99a0cb76cc3..725c9b418d9738eed2bb31add8955440eff0b2e2 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "2bdd3ecd1c14aff8b1e4786ee9bf123c",
+    "content-hash": "34b720d947e75b0bb44ede913e37a788",
     "packages": [
         {
             "name": "andy128k/pegp",
             ],
             "time": "2015-06-14T21:17:01+00:00"
         },
+        {
+            "name": "myclabs/deep-copy",
+            "version": "1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/myclabs/DeepCopy.git",
+                "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
+                "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "require-dev": {
+                "doctrine/collections": "^1.0",
+                "doctrine/common": "^2.6",
+                "phpunit/phpunit": "^4.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "DeepCopy\\": "src/DeepCopy/"
+                },
+                "files": [
+                    "src/DeepCopy/deep_copy.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Create deep copies (clones) of your objects",
+            "keywords": [
+                "clone",
+                "copy",
+                "duplicate",
+                "object",
+                "object graph"
+            ],
+            "time": "2017-10-19T19:58:43+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-common",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
+                "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
+                "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": "2017-09-11T18:02:19+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "3.3.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2",
+                "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0",
+                "phpdocumentor/reflection-common": "^1.0.0",
+                "phpdocumentor/type-resolver": "^0.4.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": "2017-11-10T14:09:06+00:00"
+        },
+        {
+            "name": "phpdocumentor/type-resolver",
+            "version": "0.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/TypeResolver.git",
+                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
+                "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5 || ^7.0",
+                "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": "2017-07-14T14:27:02+00:00"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "1.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
+                "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.2",
+                "php": "^5.3|^7.0",
+                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
+                "sebastian/comparator": "^1.1|^2.0|^3.0",
+                "sebastian/recursion-context": "^1.0|^2.0|^3.0"
+            },
+            "require-dev": {
+                "phpspec/phpspec": "^2.5|^3.2",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.8.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": "2018-08-05T17:53:17+00:00"
+        },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "2.2.4",
+            "version": "4.0.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
+                "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
-                "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
+                "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3",
-                "phpunit/php-file-iterator": "~1.3",
-                "phpunit/php-text-template": "~1.2",
-                "phpunit/php-token-stream": "~1.3",
-                "sebastian/environment": "^1.3.2",
-                "sebastian/version": "~1.0"
+                "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": "~4"
+                "ext-xdebug": "^2.1.4",
+                "phpunit/phpunit": "^5.7"
             },
             "suggest": {
-                "ext-dom": "*",
-                "ext-xdebug": ">=2.2.1",
-                "ext-xmlwriter": "*"
+                "ext-xdebug": "^2.5.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.2.x-dev"
+                    "dev-master": "4.0.x-dev"
                 }
             },
             "autoload": {
                 "testing",
                 "xunit"
             ],
-            "time": "2015-10-06T15:47:00+00:00"
+            "time": "2017-04-02T07:44:40+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
-            "version": "1.3.4",
+            "version": "1.4.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
-                "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb"
+                "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb",
-                "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
+                "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4.x-dev"
+                }
+            },
             "autoload": {
                 "classmap": [
-                    "File/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
                 "filesystem",
                 "iterator"
             ],
-            "time": "2013-10-10T15:34:57+00:00"
+            "time": "2017-11-27T13:52:08+00:00"
         },
         {
             "name": "phpunit/php-text-template",
         },
         {
             "name": "phpunit/phpunit",
-            "version": "4.4.5",
+            "version": "5.7.27",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "2e8580deebb7d1ac92ac878595e6bffe01069c2a"
+                "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2e8580deebb7d1ac92ac878595e6bffe01069c2a",
-                "reference": "2e8580deebb7d1ac92ac878595e6bffe01069c2a",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c",
+                "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-json": "*",
-                "ext-pcre": "*",
-                "ext-reflection": "*",
-                "ext-spl": "*",
-                "php": ">=5.3.3",
-                "phpunit/php-code-coverage": "~2.0",
-                "phpunit/php-file-iterator": "~1.3.2",
+                "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.2",
-                "phpunit/phpunit-mock-objects": "~2.3",
-                "sebastian/comparator": "~1.0",
-                "sebastian/diff": "~1.1",
-                "sebastian/environment": "~1.1",
-                "sebastian/exporter": "~1.1",
-                "sebastian/global-state": "~1.0",
-                "sebastian/recursion-context": "~1.0",
-                "sebastian/version": "~1.0",
-                "symfony/yaml": "~2.0"
+                "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.6|^2.0.1",
+                "symfony/yaml": "~2.1|~3.0|~4.0"
+            },
+            "conflict": {
+                "phpdocumentor/reflection-docblock": "3.0.2"
+            },
+            "require-dev": {
+                "ext-pdo": "*"
             },
             "suggest": {
+                "ext-xdebug": "*",
                 "phpunit/php-invoker": "~1.1"
             },
             "bin": [
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "4.4.x-dev"
+                    "dev-master": "5.7.x-dev"
                 }
             },
             "autoload": {
                 "testing",
                 "xunit"
             ],
-            "time": "2015-01-27T16:06:15+00:00"
+            "time": "2018-02-01T05:50:59+00:00"
         },
         {
             "name": "phpunit/phpunit-mock-objects",
-            "version": "2.3.8",
+            "version": "3.4.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
-                "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983"
+                "reference": "a23b761686d50a560cc56233b9ecf49597cc9118"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983",
-                "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118",
+                "reference": "a23b761686d50a560cc56233b9ecf49597cc9118",
                 "shasum": ""
             },
             "require": {
                 "doctrine/instantiator": "^1.0.2",
-                "php": ">=5.3.3",
-                "phpunit/php-text-template": "~1.2",
-                "sebastian/exporter": "~1.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": "~4.4"
+                "phpunit/phpunit": "^5.4"
             },
             "suggest": {
                 "ext-soap": "*"
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.3.x-dev"
+                    "dev-master": "3.2.x-dev"
                 }
             },
             "autoload": {
                 "xunit"
             ],
             "abandoned": true,
-            "time": "2015-10-02T06:51:40+00:00"
+            "time": "2017-06-30T09:13:00+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",
         },
         {
             "name": "sebastian/environment",
-            "version": "1.3.8",
+            "version": "2.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/environment.git",
-                "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea"
+                "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea",
-                "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
+                "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3.3 || ^7.0"
+                "php": "^5.6 || ^7.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8 || ^5.0"
+                "phpunit/phpunit": "^5.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.3.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "autoload": {
                 "environment",
                 "hhvm"
             ],
-            "time": "2016-08-18T05:49:44+00:00"
+            "time": "2016-11-26T07:53:53+00:00"
         },
         {
             "name": "sebastian/exporter",
-            "version": "1.2.2",
+            "version": "2.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/exporter.git",
-                "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
+                "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
-                "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
+                "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3",
-                "sebastian/recursion-context": "~1.0"
+                "sebastian/recursion-context": "~2.0"
             },
             "require-dev": {
                 "ext-mbstring": "*",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.3.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "autoload": {
                 "export",
                 "exporter"
             ],
-            "time": "2016-06-17T09:04:28+00:00"
+            "time": "2016-11-19T08:54:04+00:00"
         },
         {
             "name": "sebastian/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/recursion-context",
-            "version": "1.0.5",
+            "version": "2.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/recursion-context.git",
-                "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7"
+                "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
-                "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a",
+                "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Provides functionality to recursively process PHP variables",
             "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
-            "time": "2016-10-03T07:41:43+00:00"
+            "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": "1.0.6",
+            "version": "2.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/version.git",
-                "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
+                "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
-                "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
+                "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/"
             ],
             "description": "Library that helps with managing the version number of Git-hosted PHP projects",
             "homepage": "https://github.com/sebastianbergmann/version",
-            "time": "2015-06-21T13:59:46+00:00"
+            "time": "2016-10-03T07:35:21+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
         },
         {
             "name": "symfony/yaml",
-            "version": "v2.8.49",
+            "version": "v3.4.23",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "02c1859112aa779d9ab394ae4f3381911d84052b"
+                "reference": "57f1ce82c997f5a8701b89ef970e36bb657fd09c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b",
-                "reference": "02c1859112aa779d9ab394ae4f3381911d84052b",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/57f1ce82c997f5a8701b89ef970e36bb657fd09c",
+                "reference": "57f1ce82c997f5a8701b89ef970e36bb657fd09c",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.9",
+                "php": "^5.5.9|>=7.0.8",
                 "symfony/polyfill-ctype": "~1.8"
             },
+            "conflict": {
+                "symfony/console": "<3.4"
+            },
+            "require-dev": {
+                "symfony/console": "~3.4|~4.0"
+            },
+            "suggest": {
+                "symfony/console": "For validating YAML files using the lint command"
+            },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.8-dev"
+                    "dev-master": "3.4-dev"
                 }
             },
             "autoload": {
             ],
             "description": "Symfony Yaml Component",
             "homepage": "https://symfony.com",
-            "time": "2018-11-11T11:18:13+00:00"
+            "time": "2019-02-23T15:06:07+00:00"
+        },
+        {
+            "name": "webmozart/assert",
+            "version": "1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webmozart/assert.git",
+                "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
+                "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0",
+                "symfony/polyfill-ctype": "^1.8"
+            },
+            "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": "2018-12-25T11:19:39+00:00"
         }
     ],
     "aliases": [],
     "prefer-stable": false,
     "prefer-lowest": false,
     "platform": {
-        "php": ">=5.3"
+        "php": ">=5.6"
     },
     "platform-dev": [],
     "platform-overrides": {
diff --git a/src/algorithm.php b/src/algorithm.php
deleted file mode 100644 (file)
index 9873e67..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-namespace PFF;
-
-class TopSort
-{
-    private $following, $visited = array(), $result = array();
-
-    private function getFollowing($obj)
-    {
-        return call_user_func($this->following, $obj);
-    }
-
-    private function once($obj)
-    {
-        if (in_array($obj, $this->visited)) {
-            return false;
-        } else {
-            $this->visited[] = $obj;
-            return true;
-        }
-    }
-
-    private function visit($obj, $chain)
-    {
-        if (in_array($obj, $chain)) {
-            $chain[] = $obj;
-            return $chain;
-        }
-
-        if ($this->once($obj)) {
-            $chain[] = $obj;
-            foreach ($this->getFollowing($obj) as $following) {
-                $loop = $this->visit($following, $chain);
-                if ($loop !== false)
-                    return $loop;
-            }
-            $this->result[] = $obj;
-        }
-        return false;
-    }
-
-    public static function sort($initialNodes, $following)
-    {
-        $sort = new self;
-        $sort->following = $following;
-
-        foreach ($initialNodes as $start) {
-            $loop = $sort->visit($start, array());
-            if ($loop !== false)
-                return array(false, $loop);
-        }
-        return array(true, $sort->result);
-    }
-}
-
diff --git a/src/cli.php b/src/cli.php
deleted file mode 100644 (file)
index 05ec6f5..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-namespace PFF;
-
-abstract class CLI
-{
-    private function getArgs()
-    {
-        global $argv;
-        if (is_array($argv))
-            return $argv;
-        if (@is_array($_SERVER['argv']))
-            return $_SERVER['argv'];
-        throw new \Exception("Could not read command arguments (register_argc_argv=Off?)");
-    }
-
-    protected abstract function commands();
-
-    public function printUsage($name)
-    {
-        echo "Usage: $name <command> [parameter...]\n\n";
-        echo "Available commands:\n\n";
-
-        $cmds = $this->commands();
-
-        $max_len = 0;
-        foreach ($cmds as $cmd) {
-            list($key, $method, $description) = $cmd;
-            $max_len = max($max_len, strlen($key));
-        }
-
-        $pad = floor(($max_len + 11) / 8) * 8;
-        foreach ($cmds as $cmd) {
-            list($key, $method, $description) = $cmd;
-
-            $prefix = "    ".str_pad($key, $pad);
-            foreach (explode("\n", $description) as $dsc) {
-                echo $prefix.$dsc."\n";
-                $prefix = str_repeat(' ', $pad + 4);
-            }
-            
-        }
-        echo "\n";
-    }
-
-    public function execute()
-    {
-        $args = $this->getArgs();
-
-        if (count($args) <= 1) {
-            $this->printUsage($args[0]);
-            return 1;
-        }
-
-        $command = $args[1];
-        $command_args = array_slice($args, 2);
-
-        foreach ($this->commands() as $cmd) {
-            list($key, $method, $description) = $cmd;
-            if ($key === $command) {
-                $m = new \ReflectionMethod(get_class($this), $method);
-                if ($m->getNumberOfRequiredParameters() > count($command_args)) {
-                    $params = $m->getParameters();
-                    $names = array();
-                    for ($i = count($command_args); $i < $m->getNumberOfRequiredParameters(); ++$i) {
-                        $names[] = $params[$i]->name;
-                    }
-                    echo 'Missing required parameters: '.implode(', ', $names)."\n";
-                    return 1;
-                }
-
-                return $m->invokeArgs($this, $command_args);
-            }
-        }
-
-        echo "Unknown command.\n";
-        return 1;
-    }
-
-    public static function run()
-    {
-        if (php_sapi_name() === 'cli') {
-            $cli = new static;
-            $code = $cli->execute();
-            exit($code);
-        }
-    }
-}
index cbf69347bcd691549ff1c309ab318d56dab332e6..10c374b527a1e1cb245058d03046422bc7636a82 100644 (file)
@@ -59,6 +59,11 @@ final class Collection
         return $result;
     }
 
+    /**
+     * @param \Traversable $collection
+     * @param callable $key
+     * @return array
+     */
     public static function groupBy($collection, $key)
     {
         $groups = array();
@@ -67,39 +72,45 @@ final class Collection
         return $groups;
     }
 
+    /**
+     * @param \Traversable $collection
+     * @param string $propertyName
+     * @return array
+     * @throws \Exception
+     */
     public static function groupByProperty($collection, $propertyName)
     {
-        $groups = array();
-        foreach ($collection as $item) {
-            $key = $item->$propertyName;
-            Arr::pushToKey($groups, $key, $item);
-        }
-        return $groups;
+        return self::groupBy($collection, function ($item) use ($propertyName) {
+            return MapLike::get($item, $propertyName);
+        });
     }
 
-    public static function groupByMethod(/*$collection, $methodName, ...$args*/)
+    /**
+     * @param \Traversable $collection
+     * @param string $methodName
+     * @param mixed ...$methodArgs
+     * @return array
+     */
+    public static function groupByMethod($collection, $methodName, ...$methodArgs)
     {
-        $args = func_get_args();
-        $collection = $args[0];
-        $methodName = $args[1];
-        $methodArgs = array_slice($args, 2);
-
-        $groups = array();
-        foreach ($collection as $item) {
-            $key = call_user_func_array(array($item, $methodName), $methodArgs);
-            Arr::pushToKey($groups, $key, $item);
-        }
-        return $groups;
+        return self::groupBy($collection, function ($item) use ($methodArgs, $methodName) {
+            return call_user_func_array(array($item, $methodName), $methodArgs);
+        });
     }
 
     /**
      * indexByProperty($collection, $propertyName) === array_column($collection, null, $propertyName) for PHP >= 7
+     *
+     * @param \Traversable $collection
+     * @param string $propertyName
+     * @return array
+     * @throws \Exception
      */
     public static function indexByProperty($collection, $propertyName)
     {
         $groups = array();
         foreach ($collection as $item) {
-            $key = is_object($item) ? $item->$propertyName : $item[$propertyName];
+            $key = MapLike::get($item, $propertyName);
             $groups[$key] = $item;
         }
         return $groups;
@@ -107,18 +118,28 @@ final class Collection
 
     /**
      * columnByProperty($collection, $value, $key) === array_column($collection, $value, $key) for PHP >= 7
+     *
+     * @param \Traversable $collection
+     * @param string $valueProperty
+     * @param string $keyProperty
+     * @return array
+     * @throws \Exception
      */
     public static function columnByProperty($collection, $valueProperty, $keyProperty)
     {
         $groups = array();
         foreach ($collection as $item) {
-            $key = is_object($item) ? $item->$keyProperty : $item[$keyProperty];
-            $value = is_object($item) ? $item->$valueProperty : $item[$valueProperty];
+            $key = MapLike::get($item, $keyProperty);
+            $value = MapLike::get($item, $valueProperty);
             $groups[$key] = $value;
         }
         return $groups;
     }
 
+    /**
+     * @param \Traversable $collection
+     * @return array
+     */
     public static function inits($collection)
     {
         $result = array();
@@ -133,4 +154,3 @@ final class Collection
         return $result;
     }
 }
-
diff --git a/src/container.php b/src/container.php
deleted file mode 100644 (file)
index 9f1543b..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-
-namespace PFF;
-
-// Dependency injection container
-
-class Container
-{
-    private $values = array();
-
-    public function getWithArgs($id, $args)
-    {
-        if (!array_key_exists($id, $this->values))
-            throw new \InvalidArgumentException('Identifier "'.$id.'" is not defined.');
-
-        $v = $this->values[$id];
-        if ($v[0] == 'function') {
-            return call_user_func_array($v[1], $args);
-        } elseif ($v[0] == 'factory') {
-            $value = call_user_func_array($v[1], $args);
-            $this->values[$id] = array('value', $value);
-            return $value;
-        } else /*value*/ {
-            return $v[1];
-        }
-    }
-
-    public function get()
-    {
-        $args = func_get_args();
-        $id = array_shift($args);
-        return $this->getWithArgs($id, $args);
-    }
-
-    public function __call($name, $args)
-    {
-        switch ($name) {
-        case 'set':
-            $this->values[$args[0]] = array('value', $args[1]);
-            return $this;
-        case 'setFunction':
-            $this->values[$args[0]] = array('function', $args[1]);
-            return $this;
-        case 'setFactory':
-            $this->values[$args[0]] = array('factory', $args[1]);
-            return $this;
-        default:
-            return $this->getWithArgs($name, $args);
-        }
-    }
-
-    private static $instance = null;
-
-    public static function getInstance()
-    {
-        if (!self::$instance)
-            self::$instance = new self;
-        return self::$instance;
-    }
-
-    public static function __callStatic($name, $args)
-    {
-        return call_user_func_array(array(self::getInstance(), $name), $args);
-    }
-}
index 79a6d428a18308a8bd5b7b6554e6562921f4bfda..b39309d1bd66f8969c8a2d0c2489901db2bebb88 100644 (file)
@@ -141,7 +141,7 @@ final class DateFormat
             case self::DATE_MIDDLE_ENDIAN:
                 return $controls[1].$separator.$controls[2].$separator.$controls[0];
             default:
-                throw new Exception('Bad date format');
+                throw new \Exception('Bad date format');
         }
     }
 
diff --git a/src/function.php b/src/function.php
deleted file mode 100644 (file)
index fc53752..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-<?php
-
-namespace PFF;
-
-class Functions
-{
-    static function identity()
-    {
-        return new IdentityFunc;
-    }
-
-    static function func($func)
-    {
-        if ($func instanceof Func)
-            return $func;
-        else
-            return new Func($func);
-    }
-
-    static function bind()
-    {
-        $args = func_get_args();
-        $func = array_shift($args);
-        return new BoundFunc($func, $args);
-    }
-
-    static function curry($func, $arity)
-    {
-        return new BoundFunc($func, Placeholder::range($arity));
-    }
-
-    static function compose($f, $g)
-    {
-        return new \PFF\CompositionFunc($f, $g);
-    }
-
-    static function S($f, $g)
-    {
-        return new \PFF\SFunc($f, $g);
-    }
-}
-
-abstract class AbstractFunc
-{
-    abstract function apply($args);
-
-    function call()
-    {
-        return $this->apply(func_get_args());
-    }
-
-    function __invoke()
-    {
-        return $this->apply(func_get_args());
-    }
-
-    /* compose */
-
-    function then($then)
-    {
-        return new CompositionFunc($then, $this);
-    }
-
-    function thenBind()
-    {
-        $args = func_get_args();
-        $func = array_shift($args);
-        return $this->then(BoundFunc::createv($func, $args));
-    }
-}
-
-class Func extends AbstractFunc
-{
-    private $f;
-
-    function __construct($f)
-    {
-        $this->f = $f;
-    }
-
-    function apply($args)
-    {
-        return call_user_func_array($this->f, $args);
-    }
-}
-
-class IdentityFunc extends AbstractFunc
-{
-    function apply($args)
-    {
-        if (count($args) !== 1)
-            throw new \Exception('Wrong number of arguments. Only one is expected.');
-        return $args[0];
-    }
-}
-
-
-class Placeholder
-{
-    public $place;
-
-    function __construct($place)
-    {
-        $this->place = $place;
-    }
-
-    static function p($place=1)
-    {
-        return new self($place);
-    }
-
-    static function range($count)
-    {
-        $range = array();
-        for ($i = 1; $i <= $count; ++$i)
-            $range[] = self::p($i);
-        return $range;
-    }
-}
-
-class BoundFunc extends Func
-{
-    private $args, $arity;
-
-    function __construct($f, $args=null)
-    {
-        parent::__construct($f);
-        $this->args = $args;
-        $this->arity = 0;
-        foreach ($this->args as $p)
-            if ($p instanceof Placeholder)
-                $this->arity = max($this->arity, $p->place);
-    }
-
-    static function createv($func, $args)
-    {
-        return new BoundFunc($func, $args);
-    }
-
-    static function create()
-    {
-        $args = func_get_args();
-        $func = array_shift($args);
-        return self::createv($func, $args);
-    }
-
-    function apply($args)
-    {
-        if (count($args) < $this->arity) {
-            $px = Placeholder::range($this->arity - count($args));
-            return new self($this, array_merge($args, $px));
-        }
-
-        $a = array();
-        foreach ($this->args as $p) {
-            if ($p instanceof Placeholder) {
-                $a[] = $args[$p->place - 1];
-            } else {
-                $a[] = $p;
-            }
-        }
-        return parent::apply($a);
-    }
-}
-
-class CompositionFunc extends Func
-{
-    private $left;
-
-    function __construct($left, $right)
-    {
-        parent::__construct($right);
-        $this->left = Functions::func($left);
-    }
-
-    function apply($args)
-    {
-        return $this->left->call(parent::apply($args));
-    }
-}
-
-class SFunc extends Func
-{
-    private $left;
-
-    function __construct($left, $right)
-    {
-        parent::__construct($right);
-        $this->left = Functions::func($left);
-    }
-
-    function apply($args)
-    {
-        $r = parent::apply($args);
-        $args[] = $r;
-        return $this->left->apply($args);
-    }
-}
-
index 9e0a9ed44581daed875ec227b166235cda4066ad..48637b60ca8de08185b328bdbc5740b8e61c6624 100644 (file)
@@ -16,14 +16,14 @@ class Text
 
 class Tag
 {
-    private $name, $selfClose, $attributes, $inner=array();
+    private $name, $selfClose, $attributes, $inner = array();
 
     /**
      * @param string $name
      * @param array $attributes
      * @param mixed|null $inner
      */
-    public function __construct($name, $attributes=array(), $inner=null)
+    public function __construct($name, $attributes = array(), $inner = null)
     {
         $this->name = $name;
         $this->attributes = (array)$attributes;
@@ -36,14 +36,14 @@ class Tag
     }
 
     /**
+     * @param string $name
+     * @param array $attributes
+     * @param array $inner
      * @return Tag
      */
-    public static function create(/*name[, attributes, [... inner]]*/)
+    public static function create($name, $attributes = array(), ...$inner)
     {
-        $args = func_get_args();
-        $name = array_shift($args);
-        $attributes = array_shift($args);
-        return new Tag($name, $attributes, $args);
+        return new Tag($name, $attributes, $inner);
     }
 
     /**
@@ -175,7 +175,7 @@ class Tag
     }
 
     /**
-     * @param string $raw
+     * @param string|array $raw
      * @return Tag
      */
     public function raw($raw)
@@ -193,18 +193,18 @@ class Tag
      */
     public function html()
     {
-        $s = '<'.$this->name;
+        $s = '<' . $this->name;
         foreach ($this->attributes as $k => $v) {
             if (is_array($v))
                 $v = implode(' ', $v);
-            $s .= ' '.$k.'="'.Text::escape($v).'"';
+            $s .= ' ' . $k . '="' . Text::escape($v) . '"';
         }
         $s .= '>';
 
         if (!$this->selfClose) {
             foreach ($this->inner as $item)
                 $s .= (string)$item;
-            $s .= '</'.$this->name.'>';
+            $s .= '</' . $this->name . '>';
         }
 
         return $s;
diff --git a/src/io/pushback.php b/src/io/pushback.php
deleted file mode 100644 (file)
index e2dfc8b..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-namespace PFF\IO;
-
-interface PushBackInputStream
-{
-    public function close();
-    public function getc();
-    public function ungetc($char);
-}
-
-class FileInputStream implements PushBackInputStream
-{
-    private $fd, $buffer = array();
-
-    public function __construct($path, $mode)
-    {
-        $this->fd = fopen($path, $mode);
-    }
-
-    public function __destruct()
-    {
-        $this->close();
-    }
-
-    public function close()
-    {
-        if ($this->fd) {
-            fclose($this->fd);
-            $this->fd = null;
-        }
-    }
-
-    public function getc()
-    {
-        if (count($this->buffer)) {
-            return array_shift($this->buffer);
-        } else {
-            return fgetc($this->fd);
-        }
-    }
-
-    public function ungetc($char)
-    {
-        array_unshift($this->buffer, $char);
-    }
-}
-
-class StringInputStream implements PushBackInputStream
-{
-    private $string, $position;
-
-    public function __construct($string)
-    {
-        $this->string = $string;
-        $this->position = 0;
-    }
-
-    public function close()
-    {
-        if ($this->string !== null)
-            $this->string = null;
-    }
-
-    public function getc()
-    {
-        if ($this->position >= strlen($this->string))
-            return false;
-        else
-            return $this->string[$this->position++];
-    }
-
-    public function ungetc($char)
-    {
-        --$this->position;
-    }
-}
-
diff --git a/src/map.php b/src/map.php
new file mode 100644 (file)
index 0000000..6af1f37
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+namespace PFF;
+
+class Map
+{
+    private $data = array();
+
+    function count()
+    {
+        return count($this->data);
+    }
+
+    function contains($key)
+    {
+        foreach ($this->data as &$entry) {
+            if ($this->keysAreEqual($key, $entry[0])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    function get($key)
+    {
+        foreach ($this->data as &$entry) {
+            if ($this->keysAreEqual($key, $entry[0])) {
+                return $entry[1];
+            }
+        }
+        return null;
+    }
+
+    function set($key, $value)
+    {
+        foreach ($this->data as &$entry) {
+            if ($this->keysAreEqual($key, $entry[0])) {
+                $entry[1] = $value;
+                return;
+            }
+        }
+        $this->data[] = [$key, $value];
+    }
+
+    function remove($key)
+    {
+        $this->data = array_filter($this->data, function ($entry) use ($key) {
+            return !$this->keysAreEqual($key, $entry[0]);
+        });
+    }
+
+    function __debugInfo()
+    {
+        $i = [];
+        foreach ($this->data as &$entry) {
+            $i[print_r($entry[0], true)] = print_r($entry[1], true);
+        }
+        return $i;
+    }
+
+    private function keysAreEqual($key, $entry)
+    {
+        return $entry === $key;
+    }
+}
diff --git a/src/maplike.php b/src/maplike.php
new file mode 100644 (file)
index 0000000..c092a00
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+namespace PFF;
+
+final class MapLike
+{
+    public static function get($mapLike, $key, $default = null)
+    {
+        if (is_array($mapLike)) {
+            if (array_key_exists($key, $mapLike)) {
+                return $mapLike[$key];
+            } else {
+                return $default;
+            }
+        } elseif (is_object($mapLike)) {
+            try {
+                return $mapLike->$key;
+            } catch (\Exception $e) {
+                return $default;
+            }
+        } else {
+            throw new \Exception("Not a map");
+        }
+    }
+}
diff --git a/src/mixins.php b/src/mixins.php
deleted file mode 100644 (file)
index 3026b13..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-namespace PFF;
-
-class Mixins
-{
-    private $methods = array();
-
-    public function __construct($object, $mixins)
-    {
-        foreach ($mixins as $class) {
-            if (!class_exists($class))
-                throw new \Exception('Tried to inherit non-existant class \''.$class.'\'.');
-
-            $mixin = new $class($object);
-            $methods = get_class_methods($mixin);
-            if (is_array($methods))
-                foreach ($methods as $method)
-                    $this->methods[strtolower($method)][] = $mixin;
-        }
-    }
-
-    private $current_call_method = null;
-    private $current_call_mixins = null;
-
-    public function __call($method, $args = array())
-    {
-        $m = strtolower($method);
-        if (!isset($this->methods[$m]))
-            throw new \Exception('Call to undefined method ' . get_class($this) . "::$method()");
-
-        $this->current_call_method = $method;
-        $this->current_call_mixins = $this->methods[$m];
-        $result = call_user_func_array(array($this, 'call_next_method'), $args);
-        $this->current_call_method = null;
-        $this->current_call_mixins = null;
-        return $result;
-    }
-
-    public function call_next_method()
-    {
-        if ($this->current_call_mixins === null)
-            throw new \Exception('call_next_method is invoked outside of mixin method.');
-
-        $mixin = array_pop($this->current_call_mixins);
-        if ($mixin === null)
-            throw new \Exception('No next method for ' . get_class($this) . "::$method()");
-
-        return call_user_func_array(array($mixin, $this->current_call_method), func_get_args());
-    }
-}
-
-abstract class Mixable
-{
-    protected abstract function __mixins();
-
-    protected $mixins;
-
-    public function __construct()
-    {
-        $this->mixins = new Mixins($this, $this->__mixins());
-    }
-
-    public function __call($method, $args = array())
-    {
-        return call_user_func_array(array($this->mixins, $method), $args);
-    }
-
-    public function call_next_method()
-    {
-        return call_user_func_array(array($this->mixins, 'call_next_method'), func_get_args());
-    }
-
-    public function __clone()
-    {
-    }
-}
-
-class Mixin
-{
-    protected $self;
-
-    public function __construct($self)
-    {
-        $this->self = $self;
-    }
-
-    protected function call_next_method()
-    {
-        return $this->self->call_next_method(func_get_args());
-    }
-}
-
diff --git a/src/multidimensionalarray.php b/src/multidimensionalarray.php
deleted file mode 100644 (file)
index 44f79fd..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-namespace PFF;
-
-class MultidimensionalArray implements \ArrayAccess
-{
-    private $keys = array();
-    private $storage = array();
-
-    function count()
-    {
-        return count($this->keys);
-    }
-
-    function contains()
-    {
-        return $this->offsetExists(func_get_args());
-    }
-
-    function offsetExists($key)
-    {
-        $key = array_values($key);
-        return false !== array_search($key, $this->keys);
-    }
-
-    function get()
-    {
-        return $this->offsetGet(func_get_args());
-    }
-
-    function offsetGet($key)
-    {
-        $key = array_values($key);
-        $index = array_search($key, $this->keys);
-        if ($index !== false)
-            return $this->storage[$index];
-        else
-            return null;
-    }
-
-    function set()
-    {
-        $key = func_get_args();
-        $value = array_pop($key);
-        $this->offsetSet($key, $value);
-        return $this;
-    }
-
-    function offsetSet($key, $value)
-    {
-        $key = array_values($key);
-        $index = array_search($key, $this->keys);
-        if ($index === false) {
-            $this->keys[] = $key;
-            end($this->keys);
-            $index = key($this->keys);
-        }
-        $this->storage[$index] = $value;
-    }
-
-    function reset()
-    {
-        $this->offsetUnset(func_get_args());
-        return $this;
-    }
-
-    function offsetUnset($key)
-    {
-        $key = array_values($key);
-        $index = array_search($key, $this->keys);
-        if ($index !== false) {
-            unset($this->keys[$index]);
-            unset($this->storage[$index]);
-        }
-    }
-
-    function __debugInfo()
-    {
-        $i = array();
-        foreach ($this->keys as $index => $key) {
-            $i[implode(', ', $key)] = $this->storage[$index];
-        }
-        return $i;
-    }
-
-    public function ensureKeyExists($key, $default=null)
-    {
-        $key = array_values($key);
-        $index = array_search($key, $this->keys);
-        if ($index === false) {
-            $this->keys[] = $key;
-            end($this->keys);
-            $index = key($this->keys);
-            $this->storage[$index] = $default;
-        }
-        return $this;
-    }
-
-    public function pushToKey($key, $value)
-    {
-        $key = array_values($key);
-        $index = array_search($key, $this->keys);
-        if ($index === false) {
-            $this->keys[] = $key;
-            end($this->keys);
-            $index = key($this->keys);
-
-            $this->storage[$index] = array($value);
-        } else {
-            $this->storage[$index][] = $value;
-        }
-        return $this;
-    }
-}
-
index 286712b94b26774f17608d10ca5161edf02e41bf..36dec1c184d78d0b1bab903d4745fa0a4e75c7b1 100644 (file)
@@ -36,27 +36,26 @@ class NativeTemplate
 
     private function renderFile($file)
     {
-        $previous = set_error_handler(array($this, '_errorHandler'));
+        $filename = $this->findFile($file);
+
+        set_error_handler(function ($errno, $errstr, $errfile, $errline) {
+            if (error_reporting() & $errno)
+                throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
+        });
         ob_start();
         try {
-            include $this->findFile($file);
+            include $filename;
         } catch (\Exception $e) {
             ob_end_clean();
-            set_error_handler($previous);
+            restore_error_handler();
             throw $e;
         }
         $output = ob_get_contents();
         ob_end_clean();
-        set_error_handler($previous);
+        restore_error_handler();
         return $output;
     }
 
-    public function _errorHandler($errno, $errstr, $errfile, $errline)
-    {
-        if (error_reporting() & $errno)
-            throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
-    }
-
     private function findFile($file)
     {
         foreach ($this->_dirs as $dir) {
@@ -87,4 +86,3 @@ class NativeTemplate
         return $this->_content;
     }
 }
-
index 57411a38cba1cade19f0983ae4590e0ee68b913e..640387c74d468b9b17b35ea1d364c60b9a4d3929 100644 (file)
@@ -28,4 +28,3 @@ class Regexp
         return '!^(http|https|ftp|gopher)\://('.$ip.'|'.$dom.')!i';
     }
 }
-
diff --git a/src/shell.php b/src/shell.php
deleted file mode 100644 (file)
index 6aa5f0d..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-namespace PFF;
-
-class Shell
-{
-    public static function call()
-    {
-        self::callv(func_get_args());
-    }
-
-    public static function callv(array $command)
-    {
-        $str = '';
-        foreach ($command as $part) {
-            $str .= escapeshellarg($part) . ' ';
-        }
-        $descriptorspec = array(
-            0 => STDIN,
-            1 => STDOUT,
-            2 => STDERR
-        );
-        $process = proc_open($str, $descriptorspec, $pipes);
-        proc_close($process);
-    }
-}
index 31785846ba855767bf442703ea36a6d3c196d961..f063a936d9ae2b78f8e0854eb821b330e9d3f8ef 100644 (file)
@@ -15,4 +15,3 @@ final class Str
         return !$length || substr($haystack, -$length) === $needle;
     }
 }
-
diff --git a/src/symbol.php b/src/symbol.php
deleted file mode 100644 (file)
index 8c140b0..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-namespace PFF;
-
-final class Symbol
-{
-    private static $symbols = array();
-    private $name;
-
-    private function __construct()
-    {
-    }
-
-    function __clone()
-    {
-        throw new \Exception('Cloning of symbols is not allowed.');
-    }
-
-    function __wakeup()
-    {
-        throw new \Exception('Deserialization of symbols is not allowed.');
-    }
-
-    public function name()
-    {
-        return $this->name;
-    }
-
-    public static function intern($name)
-    {
-        if (array_key_exists($name, self::$symbols))
-            return self::$symbols[$name];
-
-        $symbol = new self;
-        $symbol->name = $name;
-        self::$symbols[$name] = $symbol;
-        return $symbol;
-    }
-
-    public static function __callStatic($name, $args)
-    {
-        return self::intern($name);
-    }
-}
-
diff --git a/src/topsort.php b/src/topsort.php
new file mode 100644 (file)
index 0000000..e75a287
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+
+namespace PFF;
+
+class TopSort
+{
+    private $following, $visited = array(), $result = array();
+
+    private function getFollowing($obj)
+    {
+        return call_user_func($this->following, $obj);
+    }
+
+    private function once($obj)
+    {
+        if (in_array($obj, $this->visited)) {
+            return false;
+        } else {
+            $this->visited[] = $obj;
+            return true;
+        }
+    }
+
+    private function visit($obj, $chain)
+    {
+        if (in_array($obj, $chain)) {
+            $chain[] = $obj;
+            return $chain;
+        }
+
+        if ($this->once($obj)) {
+            $chain[] = $obj;
+            foreach ($this->getFollowing($obj) as $following) {
+                $loop = $this->visit($following, $chain);
+                if ($loop !== false)
+                    return $loop;
+            }
+            $this->result[] = $obj;
+        }
+        return false;
+    }
+
+    public static function sort($initialNodes, $following)
+    {
+        $sort = new self;
+        $sort->following = $following;
+
+        foreach ($initialNodes as $start) {
+            $loop = $sort->visit($start, array());
+            if ($loop !== false)
+                return array(false, $loop);
+        }
+        return array(true, $sort->result);
+    }
+}
index e0def0c10bfac7900d3916d8caa67353dd9aeeb9..bb5d4b060ea79d0a369cf6c81aafbfdc0d4af55d 100644 (file)
@@ -65,5 +65,17 @@ class ArrTest extends PHPUnit_Framework_TestCase
         $flat = \PFF\Arr::flatten($arr);
         $this->assertEquals(array('a', 'b', 'x', 'y', 'z', 'p'), $flat);
     }
-}
 
+    public function testPluck()
+    {
+        $arr = array(
+            (object)array('name' => 'apple',      'color' => 'green'),
+            (object)array('name' => 'carrot',     'color' => 'red'),
+            (object)array('name' => 'tomato',     'color' => 'red'),
+            (object)array('name' => 'grapefruit', 'color' => 'green'),
+        );
+        $colors = \PFF\Arr::pluck($arr, 'color');
+        $this->assertEquals(4, count($colors));
+        $this->assertEquals(['green', 'red', 'red', 'green'], $colors);
+    }
+}
index 62a0ed851281aa08821a347c064e8ab3a4b53a38..6f95f81e99a74788f8d6c655645eac1eb53e9281 100644 (file)
@@ -1,5 +1,27 @@
 <?php
 
+class Fruit
+{
+    private $name;
+    private $color;
+
+    function __construct($name, $color)
+    {
+        $this->name = $name;
+        $this->color = $color;
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function getColor()
+    {
+        return $this->color;
+    }
+}
+
 class CollectionTest extends PHPUnit_Framework_TestCase
 {
     public function testColumns()
@@ -59,6 +81,20 @@ class CollectionTest extends PHPUnit_Framework_TestCase
         $this->assertEquals(2, count($groups['green']));
     }
 
+    public function testGroupByMethod()
+    {
+        $arr = [
+            new Fruit('apple', 'green'),
+            new Fruit('carrot', 'red'),
+            new Fruit('tomato', 'red'),
+            new Fruit('grapefruit', 'green'),
+        ];
+        $groups = \PFF\Collection::groupByMethod($arr, 'getColor');
+        $this->assertEquals(2, count($groups));
+        $this->assertEquals(2, count($groups['red']));
+        $this->assertEquals(2, count($groups['green']));
+    }
+
     public function testInits()
     {
         $this->assertEquals(array(array()),
diff --git a/t/ContainerTest.php b/t/ContainerTest.php
deleted file mode 100644 (file)
index 1948e68..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-class ContainerTest extends PHPUnit_Framework_TestCase
-{
-    public function testValue()
-    {
-        \PFF\Container::set('text', 'Hello, World');
-        $this->assertEquals('Hello, World', \PFF\Container::text());
-    }
-
-    public function testFunction()
-    {
-        \PFF\Container::setFunction('wrap', function($str) { return '('.$str.')'; });
-        $this->assertEquals('(i love lisp)', \PFF\Container::wrap('i love lisp'));
-    }
-
-    public function testFactory()
-    {
-        \PFF\Container::setFactory('wrapOnce', function($str) { return '('.$str.')'; });
-        $this->assertEquals('(i love lisp)', \PFF\Container::wrapOnce('i love lisp'));
-        $this->assertEquals('(i love lisp)', \PFF\Container::wrapOnce('i hate lisp'));
-    }
-}
-
index a600516fad0f96cd8d6f07f3ec67b20c4ded216d..2bab90eb6099c030cce3c74b6b84e1525abffb85 100644 (file)
@@ -32,5 +32,72 @@ class FormatTest extends PHPUnit_Framework_TestCase
         $this->assertEquals(\PFF\DateFormat::DATE_BIG_ENDIAN | \PFF\DateFormat::DATE_SPACES | \PFF\DateFormat::TIME_12HOURS_LOWER | \PFF\DateFormat::TIME_SECONDS,
                                                                \PFF\DateFormat::datetimeFlagsFromString("big-endian spaces \n 12-lower Seconds"));
     }
-}
 
+    public function testMakeFormat()
+    {
+        $flags = \PFF\DateFormat::DATETIME_DEFAULT;
+        $this->assertEquals('%Y-%m-%d %H:%M', \PFF\DateFormat::makeFormat($flags, \PFF\DateFormat::$strftimeControls));
+        $this->assertEquals('Y-m-d H:i', \PFF\DateFormat::makeFormat($flags, \PFF\DateFormat::$dateControls));
+        $this->assertEquals('yy-mm-dd HH:mm', \PFF\DateFormat::makeFormat($flags, \PFF\DateFormat::$pickerControls));
+
+        $flags2 = \PFF\DateFormat::DATETIME_US;
+        $this->assertEquals('%m/%d/%Y %l:%M %p', \PFF\DateFormat::makeFormat($flags2, \PFF\DateFormat::$strftimeControls));
+        $this->assertEquals('m/d/Y h:i A', \PFF\DateFormat::makeFormat($flags2, \PFF\DateFormat::$dateControls));
+        $this->assertEquals('mm/dd/yy hh:mm TT', \PFF\DateFormat::makeFormat($flags2, \PFF\DateFormat::$pickerControls));
+
+        $flags3 = \PFF\DateFormat::DATETIME_EUROPEAN;
+        $this->assertEquals('%d.%m.%Y %H:%M', \PFF\DateFormat::makeFormat($flags3, \PFF\DateFormat::$strftimeControls));
+        $this->assertEquals('d.m.Y H:i', \PFF\DateFormat::makeFormat($flags3, \PFF\DateFormat::$dateControls));
+        $this->assertEquals('dd.mm.yy HH:mm', \PFF\DateFormat::makeFormat($flags3, \PFF\DateFormat::$pickerControls));
+
+        $flags4 = \PFF\DateFormat::DATE_US | \PFF\DateFormat::TIME_12HOURS_LOWER;
+        $this->assertEquals('%m/%d/%Y %l:%M %P', \PFF\DateFormat::makeFormat($flags4, \PFF\DateFormat::$strftimeControls));
+        $this->assertEquals('m/d/Y h:i a', \PFF\DateFormat::makeFormat($flags4, \PFF\DateFormat::$dateControls));
+
+        $flags5 = \PFF\DateFormat::DATE_BIG_ENDIAN | \PFF\DateFormat::TIME_24HOURS | \PFF\DateFormat::TIME_SECONDS | \PFF\DateFormat::DATE_SPACES;
+        $this->assertEquals('Y m d H:i:s', \PFF\DateFormat::makeFormat($flags5, \PFF\DateFormat::$dateControls));
+
+        $flags6 = \PFF\DateFormat::DATE_BIG_ENDIAN | \PFF\DateFormat::TIME_12HOURS_LOWER | \PFF\DateFormat::TIME_SECONDS | \PFF\DateFormat::DATE_SLASHES;
+        $this->assertEquals('Y/m/d h:i:s a', \PFF\DateFormat::makeFormat($flags6, \PFF\DateFormat::$dateControls));
+
+        $flags7 = \PFF\DateFormat::DATE_BIG_ENDIAN | \PFF\DateFormat::TIME_12HOURS_UPPER | \PFF\DateFormat::TIME_SECONDS | \PFF\DateFormat::DATE_SLASHES;
+        $this->assertEquals('Y/m/d h:i:s A', \PFF\DateFormat::makeFormat($flags7, \PFF\DateFormat::$dateControls));
+    }
+
+    public function testMakeFormatWOTime()
+    {
+        $flags = \PFF\DateFormat::DATE_DEFAULT;
+        $this->assertEquals(
+            \PFF\DateFormat::makeFormat($flags, \PFF\DateFormat::$strftimeControls),
+            \PFF\DateFormat::makeDateFormat($flags, \PFF\DateFormat::$strftimeControls)
+        );
+    }
+
+    public function testImpossibleDate()
+    {
+        $this->expectException(Exception::class);
+        $this->expectExceptionMessageRegExp('/Bad/');
+        \PFF\DateFormat::makeFormat(\PFF\DateFormat::DATE_LITTLE_ENDIAN | \PFF\DateFormat::DATE_MIDDLE_ENDIAN, \PFF\DateFormat::$dateControls);
+    }
+
+    public function testImpossibleTime()
+    {
+        $this->expectException(Exception::class);
+        $this->expectExceptionMessageRegExp('/Bad/');
+        \PFF\DateFormat::makeTimeFormat(\PFF\DateFormat::TIME_SECONDS, \PFF\DateFormat::$dateControls);
+    }
+
+    public function testParse()
+    {
+        $date = \PFF\DateFormat::parseDate(\PFF\DateFormat::DATE_US, '05/01/2018');
+        $this->assertEquals(118, $date['tm_year']);
+        $this->assertEquals(4, $date['tm_mon']);
+        $this->assertEquals(1, $date['tm_mday']);
+    }
+
+    public function testFormat()
+    {
+        $str = \PFF\DateFormat::formatDate(\PFF\DateFormat::DATE_DEFAULT, 1525132800);
+        $this->assertEquals('2018-05-01', $str);
+    }
+}
diff --git a/t/FunctionTest.php b/t/FunctionTest.php
deleted file mode 100644 (file)
index dd98477..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-
-use \PFF\Functions as F;
-use \PFF\Placeholder as P;
-
-function second()
-{
-    return func_get_arg(1);
-}
-
-class FunctionTest extends PHPUnit_Framework_TestCase
-{
-    public function testBind()
-    {
-        $f = \PFF\BoundFunc::create('str_replace', ' ', '_', P::p());
-        $this->assertEquals('no_space', $f('no space'));
-
-        $f = \PFF\BoundFunc::create('sprintf', '[%s:%s:%s]', '[', P::p(), ']');
-        $this->assertEquals('[[:Abc:]]', $f('Abc'));
-    }
-
-    public function testCompose()
-    {
-        $f = F::func('abs')
-            ->then('sqrt');
-
-        $this->assertEquals(2, $f(4));
-        $this->assertEquals(2, $f(-4));
-        $this->assertEquals(9, $f(81));
-    }
-
-    public function testChain()
-    {
-        $before = ' text  with_spaces  underscores  and-hypens  ';
-        $after = '_text_with_spaces_underscores_and_hypens_';
-
-        $f = F::bind('str_replace', ' ', '_', P::p())
-            ->then(F::bind('str_replace', '-', '_', P::p()))
-            ->then(F::bind('str_replace', '__', '_', P::p()));
-
-        $this->assertEquals($after, $f($before));
-
-
-        $f = F::identity()
-            ->thenBind('str_replace', ' ', '_', P::p())
-            ->thenBind('str_replace', '-', '_', P::p())
-            ->thenBind('str_replace', '__', '_', P::p());
-
-        $this->assertEquals($after, $f($before));
-    }
-
-    public function testArrayBindDefault()
-    {
-        $f = F::bind(array('PFF\Arr', 'get'), P::p(1), P::p(2), 'DEFAULT');
-
-        $arr = array(
-            'apple' => 100,
-            'grapefruit' => 400,
-            'carrot' => 50,
-        );
-
-        $this->assertEquals(400, $f($arr, 'grapefruit'));
-        $this->assertEquals('DEFAULT', $f($arr, 'orange'));
-        $this->assertEquals('DEFAULT', $f($arr, 'orange', 'no-oranges'));
-    }
-
-    public function testGetter()
-    {
-        $arr = array('name' => 'apple', 'weight' => 100);
-
-        $getter = F::bind(array('PFF\Arr', 'get'), P::p(2), P::p(1));
-        $getName = $getter('name');
-        $getWeight = $getter->call('weight');
-
-        $this->assertEquals('apple', $getName($arr));
-        $this->assertEquals(100, $getWeight($arr));
-    }
-
-    public function testPluck0()
-    {
-        $getName = F::bind(array('PFF\Arr', 'get'), P::p(), 'name');
-        $f = F::bind('array_map', $getName, P::p());
-
-        $arr = array(
-            array('name' => 'apple', 'weight' => 100),
-            array('name' => 'grapefruit', 'weight' => 400),
-            array('name' => 'carrot', 'weight' => 50),
-        );
-
-        $this->assertEquals(array('apple', 'grapefruit', 'carrot'), $f($arr));
-    }
-
-    public function testPluck1()
-    {
-        $f = F::compose(
-            F::curry('array_map', 2),
-            F::bind(array('PFF\Arr', 'get'), P::p(2), P::p(1)));
-
-        $arr = array(
-            array('name' => 'apple', 'weight' => 100),
-            array('name' => 'grapefruit', 'weight' => 400),
-            array('name' => 'carrot', 'weight' => 50),
-        );
-
-        $this->assertEquals(array('apple', 'grapefruit', 'carrot'), $f->call('name')->call($arr));
-    }
-
-    public function testPluck2()
-    {
-        $second = F::func('second');
-        $getter = F::bind(array('PFF\Arr', 'get'), P::p(2), P::p(1));
-        $pluck = F::S(
-            F::bind('array_map', P::p(3), P::p(1)),
-            F::compose($getter, $second));
-
-        $arr = array(
-            array('name' => 'apple', 'weight' => 100),
-            array('name' => 'grapefruit', 'weight' => 400),
-            array('name' => 'carrot', 'weight' => 50),
-        );
-
-        $this->assertEquals(array('apple', 'grapefruit', 'carrot'), $pluck($arr, 'name'));
-    }
-}
-
index bdef45dc4677dc21493eb1bc3f1a10c49106ec75..cf24739ad6f384a792718c246f7f04538387e106 100644 (file)
@@ -10,5 +10,24 @@ class HtmlBuilderTest extends PHPUnit_Framework_TestCase
 
         $this->assertEquals('<div class="container">Hello, &quot;World&quot;<a>click <b>me</b></a></div>', (string)$tag);
     }
-}
 
+    public function testAttrs()
+    {
+        $tag = \PFF\HtmlBuilder\Tag::div(array('class' => 'container'))
+            ->attrs(['data-somedata' => '', 'class' => 'red', 'rel' => 'what?'])
+            ->append(['Hello', ', "World"'])
+            ->append(\PFF\HtmlBuilder\Tag::create('a', ['href' => '#', 'onClick' => 'clicked()'], 'cli', 'ck', ' ')->raw('<b>me</b>'))
+            ->raw(['&nbsp;', '&mdash;'])
+            ->append((new \PFF\HtmlBuilder\Tag('span', null, 'Thanks!'))->unsetAttr('font'));
+
+        $tag->addClass('button')
+            ->removeClass('container');
+        $tag->toggleAttr("data-somedata", "somedatavalue", true);
+        $tag->toggleClass('red', false);
+        $tag->toggleClass('red', true);
+        $tag->toggleClass('red', false);
+        $tag->unsetAttr('rel');
+
+        $this->assertEquals('<div class="button" data-somedata="somedatavalue">Hello, &quot;World&quot;<a href="#" onClick="clicked()">click <b>me</b></a>&nbsp;&mdash;<span>Thanks!</span></div>', (string)$tag);
+    }
+}
diff --git a/t/MapLikeTest.php b/t/MapLikeTest.php
new file mode 100644 (file)
index 0000000..1320d35
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+class MapLikeTest extends PHPUnit_Framework_TestCase
+{
+    function testGetArray()
+    {
+        $map = ['a' => '1', 'b' => 2];
+
+        $this->assertEquals('1', \PFF\MapLike::get($map, 'a'));
+        $this->assertEquals(33, \PFF\MapLike::get($map, 'c', 33));
+    }
+
+    function testGetObject()
+    {
+        $map = (object)['a' => '1', 'b' => 2];
+
+        $this->assertEquals('1', \PFF\MapLike::get($map, 'a'));
+        $this->assertEquals(33, \PFF\MapLike::get($map, 'c', 33));
+    }
+
+    function testGetUnsupported()
+    {
+        $this->expectException(Exception::class);
+        $this->expectExceptionMessage('Not a map');
+        \PFF\MapLike::get('map', 'a');
+    }
+}
diff --git a/t/MapTest.php b/t/MapTest.php
new file mode 100644 (file)
index 0000000..6b11b27
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+class MapTest extends PHPUnit_Framework_TestCase
+{
+    public function testGet()
+    {
+        $arr = new \PFF\Map;
+        $arr->set(['fruit', 'apple'], 100);
+        $arr->set(['fruit', 'grapefruit'], 400);
+        $arr->set(['vegetable', 'carrot'], 50);
+
+        $this->assertEquals(3, $arr->count());
+        $this->assertEquals(true, $arr->contains(['fruit', 'grapefruit']));
+        $this->assertEquals(400, $arr->get(['fruit', 'grapefruit']));
+        $this->assertEquals(null, $arr->get(['fruit', 'orange']));
+        $this->assertEquals(false, $arr->contains(['apple', 'fruit']));
+        $this->assertEquals(null, $arr->get(['apple', 'fruit']));
+
+        $this->assertEquals(50, $arr->get(['vegetable', 'carrot']));
+        $arr->set(['vegetable', 'carrot'], 150);
+        $this->assertEquals(150, $arr->get(['vegetable', 'carrot']));
+        $arr->remove(['vegetable', 'carrot']);
+        $this->assertEquals(false, $arr->contains(['vegetable', 'carrot']));
+    }
+
+    public function testDebugInfo()
+    {
+        $arr = new \PFF\Map;
+        $arr->set(['fruit', 'apple'], 100);
+        $output = $this->varDumpToString($arr);
+        $this->assertContains('fruit', $output);
+        $this->assertContains('apple', $output);
+        $this->assertContains('100', $output);
+    }
+
+    private function varDumpToString($anything)
+    {
+        ob_start();
+        var_dump($anything);
+        $output = ob_get_contents();
+        ob_end_clean();
+        return $output;
+    }
+}
diff --git a/t/MixinsTest.php b/t/MixinsTest.php
deleted file mode 100644 (file)
index fb0970c..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-class SomeMixin extends \PFF\Mixin
-{
-    public function getSmthUseful()
-    {
-        return 'a';
-    }
-
-    public function setB($newValue)
-    {
-        $this->self->b = $newValue;
-    }
-}
-
-class WrapMixin extends \PFF\Mixin
-{
-    public function getSmthUseful()
-    {
-        return '(' . $this->call_next_method() . ')';
-    }
-
-    public function doSmthElse($str)
-    {
-        return strrev($str);
-    }
-}
-
-class Mixture extends \PFF\Mixable
-{
-    protected function __mixins() { return array('SomeMixin', 'WrapMixin'); }
-
-    public $b = 'b';
-
-    public function doSmthElse($str)
-    {
-        return strtoupper($this->mixins->doSmthElse($str));
-    }
-}
-
-class MixinsTest extends PHPUnit_Framework_TestCase
-{
-    public function testSetter()
-    {
-        $mixture = new Mixture;
-        $mixture->setB('B');
-        $this->assertEquals('B', $mixture->b);
-    }
-
-    public function testWrapping()
-    {
-        $mixture = new Mixture;
-        $this->assertEquals('(a)', $mixture->getSmthUseful());
-    }
-
-    public function testOverriding()
-    {
-        $mixture = new Mixture;
-        $this->assertEquals('LARCH', $mixture->doSmthElse('hCraL'));
-    }
-}
-
diff --git a/t/MultidimensionalArrayTest.php b/t/MultidimensionalArrayTest.php
deleted file mode 100644 (file)
index 4496eb0..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-class MultidimensionalArrayTest extends PHPUnit_Framework_TestCase
-{
-    public function testGet()
-    {
-        $arr = new \PFF\MultidimensionalArray;
-        $arr->set('fruit', 'apple', 100);
-        $arr->set('fruit', 'grapefruit', 400);
-        $arr->set('vegetable', 'carrot', 50);
-
-        $this->assertEquals(400, $arr->get('fruit', 'grapefruit'));
-        $this->assertEquals(null, $arr->get('fruit', 'orange'));
-        $this->assertEquals(null, $arr->get('apple', 'fruit'));
-    }
-
-    public function testEnsureKeyExists()
-    {
-        $arr = new \PFF\MultidimensionalArray;
-        $arr->set('fruit', 'apple', 100);
-        $arr->set('fruit', 'grapefruit', 400);
-        $arr->set('vegetable', 'carrot', 50);
-
-        $this->assertEquals(false, $arr->contains('orange'));
-        $arr->ensureKeyExists(array('orange'));
-        $this->assertEquals(true, $arr->contains('orange'));
-    }
-
-    public function testPushToKey()
-    {
-        $arr = new \PFF\MultidimensionalArray;
-        $arr->set('vegetable', 'carrot', array('orange'));
-
-        $arr->pushToKey(array('fruit', 'apple'), 'green')
-            ->pushToKey(array('vegetable', 'carrot'), 'long')
-            ->pushToKey(array('fruit', 'apple'), 'sweet');
-
-        $this->assertEquals(array('green', 'sweet'), $arr->get('fruit', 'apple'));
-        $this->assertEquals(array('orange', 'long'), $arr->get('vegetable', 'carrot'));
-    }
-
-    public function testPop()
-    {
-        $arr = array(
-            'apple' => 100,
-            'grapefruit' => 400,
-            'carrot' => 50,
-        );
-
-        $apple = \PFF\Arr::pop($arr, 'apple');
-        $this->assertEquals(false, array_key_exists('apple', $arr));
-        $this->assertEquals(100, $apple);
-
-        $apple = \PFF\Arr::pop($arr, 'apple');
-        $this->assertEquals(null, $apple);
-
-        $apple = \PFF\Arr::pop($arr, 'apple', 'what?');
-        $this->assertEquals('what?', $apple);
-    }
-}
-
diff --git a/t/RegexpTest.php b/t/RegexpTest.php
new file mode 100644 (file)
index 0000000..ea5cdbf
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+class RegexpTest extends PHPUnit_Framework_TestCase
+{
+    function testEmail()
+    {
+        $this->assertTrue(preg_match(\PFF\Regexp::email(), "a@b.com") !== 0);
+        $this->assertTrue(preg_match(\PFF\Regexp::email(), " a @ b . com ") === 0);
+    }
+
+    function testUrl()
+    {
+        $this->assertTrue(preg_match(\PFF\Regexp::url(), "mailto:a@b.com") === 0);
+        $this->assertTrue(preg_match(\PFF\Regexp::url(), "https://ab.com") !== 0);
+    }
+}
diff --git a/t/TopSortTest.php b/t/TopSortTest.php
new file mode 100644 (file)
index 0000000..ed200eb
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+use PFF\Arr;
+
+class TopSortTest extends PHPUnit_Framework_TestCase
+{
+    function testAlchemy()
+    {
+        $nodes = [
+            'time' => ['sand', 'glass'],
+            'fruit' => ['sun', 'tree'],
+            'stone' => ['air', 'lava'],
+            'steam' => ['water', 'fire'],
+            'lava' => ['earth', 'fire'],
+            'sand' => ['air', 'stone'],
+            'sun' => ['fire', 'sky'],
+            'glass' => ['fire', 'sand'],
+            'sky' => ['air', 'cloud'],
+            'cloud' => ['air', 'steam'],
+            'alcohol' => ['fruit', 'time'],
+        ];
+
+        list($success, $sorted) = \PFF\TopSort::sort(['alcohol'], function ($node) use ($nodes) {
+            return Arr::get($nodes, $node, []);
+        });
+
+        $this->assertTrue($success, 'Sorted successfully');
+        $this->assertEquals([
+            'fire',
+            'air',
+            'water',
+            'steam',
+            'cloud',
+            'sky',
+            'sun',
+            'tree',
+            'fruit',
+            'earth',
+            'lava',
+            'stone',
+            'sand',
+            'glass',
+            'time',
+            'alcohol',
+        ], $sorted);
+    }
+
+    function testLoop()
+    {
+        $nodes = [
+            'a' => ['b', 'c'],
+            'b' => ['c', 'a'],
+            'c' => ['a', 'b'],
+        ];
+
+        list($success, $loop) = \PFF\TopSort::sort(['a'], function ($node) use ($nodes) {
+            return Arr::get($nodes, $node, []);
+        });
+
+        $this->assertFalse($success, 'Sort impossible');
+        $this->assertEquals(['a', 'b', 'c', 'a'], $loop);
+    }
+}
index ba2cf9ad4628bc0ead3b589c20beace9f3fb23eb..73ec47d24ab4ec2ddfac12ed41194c856f225612 100644 (file)
@@ -4,19 +4,57 @@ class NativeTemplateTest extends PHPUnit_Framework_TestCase
 {
     public function testEscaping()
     {
+        $this->expectOutputString('');
+
         $nt = new \PFF\NativeTemplate(array(dirname(__FILE__)));
         $this->assertEquals(
-            '<b>\'test\', &quot;test&quot; &amp; test.</b>',
+            '<b>\'test\', &quot;test&quot; &amp; test.</b>' . "\n" . '<em>1</em>' . "\n" . '<em>2</em>',
             trim($nt->render('simple.html', array('text' => '\'test\', "test" & test.'))));
+
     }
 
     public function testErrorCatching()
     {
+        $this->expectException(ErrorException::class);
+        $this->expectExceptionMessageRegExp('/undefined_variable/');
+        $this->expectOutputString('');
+
         $nt = new \PFF\NativeTemplate(array(dirname(__FILE__)));
+        $nt->render('syntax_error.html');
+    }
 
-        $this->setExpectedExceptionRegExp('ErrorException', '/undefined_variable/');
+    public function testBadFile()
+    {
+        $this->expectException(Exception::class);
+        $this->expectExceptionMessageRegExp('/not found/');
         $this->expectOutputString('');
-        $nt->render('syntax_error.html');
+
+        $nt = new \PFF\NativeTemplate(array(dirname(__FILE__)));
+        $nt->render('unknown.html');
     }
-}
 
+    public function testLayout()
+    {
+        $this->expectOutputString('');
+
+        $nt = new \PFF\NativeTemplate(array(dirname(__FILE__)));
+        $rendered = $nt->render('page.html', array('title' => 'Page Title', 'content' => 'Page Content'));
+
+        $this->assertEquals(<<<'HTML'
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Page Title</title>
+</head>
+<body>
+    <div class="page">
+    Page Content</div>
+</body>
+</html>
+
+HTML
+            ,
+            $rendered);
+    }
+}
diff --git a/t/native_template/layout.html b/t/native_template/layout.html
new file mode 100644 (file)
index 0000000..f0f46d4
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title><?= $this->title ?></title>
+</head>
+<body>
+    <?= $this->content() ?>
+</body>
+</html>
diff --git a/t/native_template/page.html b/t/native_template/page.html
new file mode 100644 (file)
index 0000000..bdfeccc
--- /dev/null
@@ -0,0 +1,4 @@
+<?php $this->layout("layout.html"); ?>
+<div class="page">
+    <?= $this->content ?>
+</div>
index 1101b3f9d5d7b75d2d98d2d6108fdcd491f4fc96..ea13da910e58b9d12f4ef06a0d15454670a9d8fa 100644 (file)
@@ -1 +1,5 @@
 <b><?php $this->e($this->text) ?></b>
+<?php $this->em = 1; ?>
+<em><?php echo $this->em ?></em>
+<?php $this->em++; ?>
+<em><?php echo $this->em ?></em>