From ae7df64e5686bf3461f7f9d158eb0f468f9d4bab Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 12 Mar 2019 00:25:53 +0100 Subject: [PATCH 01/13] composer: removed conflicts --- composer.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/composer.json b/composer.json index e4057ef..1daaad3 100644 --- a/composer.json +++ b/composer.json @@ -22,10 +22,6 @@ "nette/tester": "^2.0", "tracy/tracy": "^2.3" }, - "conflict": { - "nette/nette": "<2.2", - "nette/application": "<2.4" - }, "autoload": { "classmap": ["src/"] }, From 6e67ed90689b666949036e1f15f5991795b5d9cf Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 7 Jan 2020 11:43:16 +0100 Subject: [PATCH 02/13] composer: license clarification --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1daaad3..9b5b0dc 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "⚛ Nette Component Model", "keywords": ["nette", "components"], "homepage": "https://nette.org", - "license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"], + "license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"], "authors": [ { "name": "David Grudl", From 7ae1a9f57acb4028e3d556c0eb5defc2d7e0fe66 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 22 Mar 2019 01:57:58 +0100 Subject: [PATCH 03/13] travis: added PHP 7.4 --- .travis.yml | 1 + readme.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d2ec511..abff51c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ php: - 7.1 - 7.2 - 7.3 + - 7.4 before_install: # turn off XDebug diff --git a/readme.md b/readme.md index e57f184..478b6c2 100644 --- a/readme.md +++ b/readme.md @@ -28,4 +28,4 @@ The recommended way to install is via Composer: composer require nette/component-model ``` -It requires PHP version 5.6 and supports PHP up to 7.3. The dev-master version requires PHP 7.1. +It requires PHP version 7.1 and supports PHP up to 7.4. From c48e89c5ddaa0ad6b87a776463ae871c79767228 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 10 Dec 2019 17:06:12 +0100 Subject: [PATCH 04/13] composer: added PHPStan --- .travis.yml | 6 +----- composer.json | 7 ++++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index abff51c..b1ecaee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,12 +41,8 @@ jobs: - stage: Static Analysis (informative) - install: - # Install PHPStan - - travis_retry composer create-project phpstan/phpstan-shim temp/phpstan --no-progress - - travis_retry composer install --no-progress --prefer-dist script: - - php temp/phpstan/phpstan.phar analyse --autoload-file vendor/autoload.php --level 5 src + - composer run-script phpstan - stage: Code Coverage diff --git a/composer.json b/composer.json index 9b5b0dc..586f7b7 100644 --- a/composer.json +++ b/composer.json @@ -20,12 +20,17 @@ }, "require-dev": { "nette/tester": "^2.0", - "tracy/tracy": "^2.3" + "tracy/tracy": "^2.3", + "phpstan/phpstan": "^0.12" }, "autoload": { "classmap": ["src/"] }, "minimum-stability": "dev", + "scripts": { + "phpstan": "phpstan analyse --level 8 src", + "tester": "tester tests -s" + }, "extra": { "branch-alias": { "dev-master": "3.0-dev" From c9f859af3c1256cf3d4925d90778f5352778c1d2 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 10 Dec 2019 23:07:58 +0100 Subject: [PATCH 05/13] fixed bugs reported by PHPStan --- src/ComponentModel/Component.php | 2 +- src/ComponentModel/Container.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ComponentModel/Component.php b/src/ComponentModel/Component.php index 87a1ab7..3f55ff6 100644 --- a/src/ComponentModel/Component.php +++ b/src/ComponentModel/Component.php @@ -241,7 +241,7 @@ private function refreshMonitors(int $depth, array &$missing = null, array &$lis $this->monitors[$type] = [null, null, null, $rec[3]]; } else { - $this->monitors[$type] = null; // forces re-lookup + unset($this->monitors[$type]); // forces re-lookup if ($obj = $this->lookup($type, false)) { foreach ($rec[3] as $pair) { $listeners[] = [$pair[0], $obj]; diff --git a/src/ComponentModel/Container.php b/src/ComponentModel/Container.php index 77f8ad0..4cc9c8e 100644 --- a/src/ComponentModel/Container.php +++ b/src/ComponentModel/Container.php @@ -199,6 +199,7 @@ public function __clone() { if ($this->components) { $oldMyself = reset($this->components)->getParent(); + assert($oldMyself instanceof self); $oldMyself->cloning = $this; foreach ($this->components as $name => $component) { $this->components[$name] = clone $component; From 593d95c8e0467f861ef48d78d5c231a204875a48 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 23 Feb 2020 19:03:44 +0100 Subject: [PATCH 06/13] travis: uses PHP 7.4 for coding checks --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index b1ecaee..3a1d7bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,7 @@ jobs: - name: Nette Code Checker + php: 7.4 install: - travis_retry composer create-project nette/code-checker temp/code-checker ^3 --no-progress script: @@ -34,6 +35,7 @@ jobs: - name: Nette Coding Standard + php: 7.4 install: - travis_retry composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress script: @@ -41,11 +43,13 @@ jobs: - stage: Static Analysis (informative) + php: 7.4 script: - composer run-script phpstan - stage: Code Coverage + php: 7.4 script: - vendor/bin/tester -p phpdbg tests -s --coverage ./coverage.xml --coverage-src ./src after_script: From 71bbc0fb19d0bebfe716ca90bdaf87b4b508546a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 23 Jan 2020 16:20:36 +0100 Subject: [PATCH 07/13] added GitHub Actions workflow --- .github/workflows/coding-style.yml | 31 ++++++++++++++ .github/workflows/static-analysis.yml | 21 ++++++++++ .github/workflows/tests.yml | 58 +++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 .github/workflows/coding-style.yml create mode 100644 .github/workflows/static-analysis.yml create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml new file mode 100644 index 0000000..4a708c5 --- /dev/null +++ b/.github/workflows/coding-style.yml @@ -0,0 +1,31 @@ +name: Coding Style + +on: [push, pull_request] + +jobs: + nette_cc: + name: Nette Code Checker + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.4 + coverage: none + + - run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress + - run: php temp/code-checker/code-checker --strict-types --no-progress + + + nette_cs: + name: Nette Coding Standard + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.4 + coverage: none + + - run: composer create-project nette/coding-standard temp/coding-standard ^2 --no-progress + - run: php temp/coding-standard/ecs check src tests --config temp/coding-standard/coding-standard-php71.yml diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..e129028 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,21 @@ +name: Static Analysis (only informative) + +on: + push: + branches: + - master + +jobs: + phpstan: + name: PHPStan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.4 + coverage: none + + - run: composer install --no-progress --prefer-dist + - run: composer phpstan + continue-on-error: true # is only informative diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..2e44f68 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,58 @@ +name: Tests + +on: [push, pull_request] + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + matrix: + php: ['7.1', '7.2', '7.3', '7.4'] + + fail-fast: false + + name: PHP ${{ matrix.php }} tests + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: ${{ matrix.php }} + coverage: none + + - run: composer install --no-progress --prefer-dist + - run: vendor/bin/tester tests -s -C + - if: failure() + uses: actions/upload-artifact@v1 + with: + name: output + path: tests/ComponentModel/output + + + lowest_dependencies: + name: Lowest Dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.1 + coverage: none + + - run: composer update --no-progress --prefer-dist --prefer-lowest --prefer-stable + - run: vendor/bin/tester tests -s -C + + + code_coverage: + name: Code Coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: shivammathur/setup-php@v1 + with: + php-version: 7.4 + coverage: none + + - run: composer install --no-progress --prefer-dist + - run: wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar + - run: vendor/bin/tester -p phpdbg tests -s -C --coverage ./coverage.xml --coverage-src ./src + - run: php coveralls.phar --verbose --config tests/.coveralls.yml From c0d2bdab3b8daf06ff5fb1134a1643a732ef8a27 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 11 Jul 2019 20:00:33 +0200 Subject: [PATCH 08/13] regexp: \z replaced with D modifier --- src/ComponentModel/Container.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ComponentModel/Container.php b/src/ComponentModel/Container.php index 4cc9c8e..a9784c2 100644 --- a/src/ComponentModel/Container.php +++ b/src/ComponentModel/Container.php @@ -40,7 +40,7 @@ public function addComponent(IComponent $component, ?string $name, string $inser $name = $component->getName(); } - if (!preg_match('#^[a-zA-Z0-9_]+\z#', $name)) { + if (!preg_match('#^[a-zA-Z0-9_]+$#D', $name)) { throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given."); } @@ -107,7 +107,7 @@ final public function getComponent(string $name, bool $throw = true): ?IComponen [$name] = $parts = explode(self::NAME_SEPARATOR, $name, 2); if (!isset($this->components[$name])) { - if (!preg_match('#^[a-zA-Z0-9_]+\z#', $name)) { + if (!preg_match('#^[a-zA-Z0-9_]+$#D', $name)) { if ($throw) { throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given."); } From 7f173baae75e6e4d5b816b2936e6886127584f00 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 1 Oct 2019 17:02:47 +0200 Subject: [PATCH 09/13] added .phpstorm.meta.php --- .phpstorm.meta.php | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .phpstorm.meta.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php new file mode 100644 index 0000000..9804e5a --- /dev/null +++ b/.phpstorm.meta.php @@ -0,0 +1,7 @@ + '@'])); From 15b56f3e694f8dc647933a83bd3d8e2bfbc0045b Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 1 Nov 2019 12:44:03 +0100 Subject: [PATCH 10/13] tests: improvements --- tests/ComponentModel/Container.attached.phpt | 10 +-- tests/ComponentModel/Container.clone-new.phpt | 4 +- tests/ComponentModel/Container.clone.phpt | 4 +- tests/ComponentModel/Container.lookup.phpt | 78 +++++++++++++++++++ tests/ComponentModel/Container.monitor.phpt | 21 ++--- 5 files changed, 92 insertions(+), 25 deletions(-) create mode 100644 tests/ComponentModel/Container.lookup.phpt diff --git a/tests/ComponentModel/Container.attached.phpt b/tests/ComponentModel/Container.attached.phpt index 133e1d3..cef4f1a 100644 --- a/tests/ComponentModel/Container.attached.phpt +++ b/tests/ComponentModel/Container.attached.phpt @@ -74,12 +74,12 @@ Assert::same([ // 'a' becoming 'b' parent $a['b'] = $b; -Assert::same('b-c-d-e', $d['e']->lookupPath('A')); -Assert::same($a, $d['e']->lookup('A')); -Assert::same('b-c-d-e', $d['e']->lookupPath()); +Assert::same('b-c-d-e', $d['e']->lookupPath(A::class)); +Assert::same($a, $d['e']->lookup(A::class)); +Assert::same('b-c-d-e', $d['e']->lookupPath(null)); Assert::same($a, $d['e']->lookup(null)); -Assert::same('c-d-e', $d['e']->lookupPath('B')); -Assert::same($b, $d['e']->lookup('B')); +Assert::same('c-d-e', $d['e']->lookupPath(B::class)); +Assert::same($b, $d['e']->lookup(B::class)); Assert::same($a['b-c'], $b['c']); Notes::fetch(); // clear diff --git a/tests/ComponentModel/Container.clone-new.phpt b/tests/ComponentModel/Container.clone-new.phpt index f2a5bec..288d256 100644 --- a/tests/ComponentModel/Container.clone-new.phpt +++ b/tests/ComponentModel/Container.clone-new.phpt @@ -91,7 +91,7 @@ Assert::same([ 'ATTACHED(A, C)', ], Notes::fetch()); -Assert::same('b-c-d-e', $a['b']['c']['d']['e']->lookupPath('A', false)); +Assert::same('b-c-d-e', $a['b']['c']['d']['e']->lookupPath(A::class)); // ==> clone 'c' @@ -103,7 +103,7 @@ Assert::same([ Assert::null($dolly['d']['e']->lookupPath('A', false)); -Assert::same('d-e', $dolly['d']['e']->lookupPath('C', false)); +Assert::same('d-e', $dolly['d']['e']->lookupPath(C::class)); // ==> clone 'b' diff --git a/tests/ComponentModel/Container.clone.phpt b/tests/ComponentModel/Container.clone.phpt index 952979e..109d559 100644 --- a/tests/ComponentModel/Container.clone.phpt +++ b/tests/ComponentModel/Container.clone.phpt @@ -75,7 +75,7 @@ Assert::same([ 'C::ATTACHED(A)', ], Notes::fetch()); -Assert::same('b-c-d-e', $a['b']['c']['d']['e']->lookupPath('A', false)); +Assert::same('b-c-d-e', $a['b']['c']['d']['e']->lookupPath(A::class)); // ==> clone 'c' @@ -87,7 +87,7 @@ Assert::same([ Assert::null($dolly['d']['e']->lookupPath('A', false)); -Assert::same('d-e', $dolly['d']['e']->lookupPath('C', false)); +Assert::same('d-e', $dolly['d']['e']->lookupPath(C::class)); // ==> clone 'b' diff --git a/tests/ComponentModel/Container.lookup.phpt b/tests/ComponentModel/Container.lookup.phpt new file mode 100644 index 0000000..5bc28ca --- /dev/null +++ b/tests/ComponentModel/Container.lookup.phpt @@ -0,0 +1,78 @@ +lookup(null)); +Assert::same('b-c-c2-d', $d->lookupPath(null)); + +// specified top +Assert::same($a, $d->lookup(A::class)); +Assert::same('b-c-c2-d', $d->lookupPath(A::class)); + +// other +Assert::same($b, $d->lookup(B::class)); +Assert::same('c-c2-d', $d->lookupPath(B::class)); + +// from top to bottom +Assert::same($c2, $d->lookup(C::class)); +Assert::same('d', $d->lookupPath(C::class)); + + +// self +Assert::exception(function () use ($d) { + $d->lookup(D::class); +}, Nette\InvalidStateException::class, "Component 'd' is not attached to 'D'."); + +Assert::exception(function () use ($d) { + $d->lookupPath(D::class); +}, Nette\InvalidStateException::class, "Component 'd' is not attached to 'D'."); + + +// not exists +Assert::exception(function () use ($d) { + $d->lookup('Unknown'); +}, Nette\InvalidStateException::class, "Component 'd' is not attached to 'Unknown'."); + +Assert::exception(function () use ($d) { + $d->lookupPath('Unknown'); +}, Nette\InvalidStateException::class, "Component 'd' is not attached to 'Unknown'."); diff --git a/tests/ComponentModel/Container.monitor.phpt b/tests/ComponentModel/Container.monitor.phpt index c3ee61c..9c54f36 100644 --- a/tests/ComponentModel/Container.monitor.phpt +++ b/tests/ComponentModel/Container.monitor.phpt @@ -17,17 +17,6 @@ require __DIR__ . '/../bootstrap.php'; class TestClass extends Container implements ArrayAccess { use Nette\ComponentModel\ArrayAccess; - - public function attached(IComponent $obj): void - { - Notes::add(get_class($this) . '::ATTACHED(' . get_class($obj) . ')'); - } - - - public function detached(IComponent $obj): void - { - Notes::add(get_class($this) . '::detached(' . get_class($obj) . ')'); - } } @@ -91,12 +80,12 @@ Assert::same([ // 'a' becoming 'b' parent $a['b'] = $b; -Assert::same('b-c-d-e', $d['e']->lookupPath('A')); -Assert::same($a, $d['e']->lookup('A')); -Assert::same('b-c-d-e', $d['e']->lookupPath()); +Assert::same('b-c-d-e', $d['e']->lookupPath(A::class)); +Assert::same($a, $d['e']->lookup(A::class)); +Assert::same('b-c-d-e', $d['e']->lookupPath(null)); Assert::same($a, $d['e']->lookup(null)); -Assert::same('c-d-e', $d['e']->lookupPath('B')); -Assert::same($b, $d['e']->lookup('B')); +Assert::same('c-d-e', $d['e']->lookupPath(B::class)); +Assert::same($b, $d['e']->lookup(B::class)); Assert::same($a['b-c'], $b['c']); Notes::fetch(); // clear From 154add915bb20030b2a7913ec9ed4b27ade6c022 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 1 Nov 2019 13:47:58 +0100 Subject: [PATCH 11/13] Container: added const NAME_REGEXP --- src/ComponentModel/Container.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ComponentModel/Container.php b/src/ComponentModel/Container.php index a9784c2..847da2d 100644 --- a/src/ComponentModel/Container.php +++ b/src/ComponentModel/Container.php @@ -19,6 +19,8 @@ */ class Container extends Component implements IContainer { + private const NAME_REGEXP = '#^[a-zA-Z0-9_]+$#D'; + /** @var IComponent[] */ private $components = []; @@ -40,7 +42,7 @@ public function addComponent(IComponent $component, ?string $name, string $inser $name = $component->getName(); } - if (!preg_match('#^[a-zA-Z0-9_]+$#D', $name)) { + if (!preg_match(self::NAME_REGEXP, $name)) { throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given."); } @@ -107,7 +109,7 @@ final public function getComponent(string $name, bool $throw = true): ?IComponen [$name] = $parts = explode(self::NAME_SEPARATOR, $name, 2); if (!isset($this->components[$name])) { - if (!preg_match('#^[a-zA-Z0-9_]+$#D', $name)) { + if (!preg_match(self::NAME_REGEXP, $name)) { if ($throw) { throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given."); } From d91eb52ca278defa9885d1564c84ff11d1871a71 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 23 Jan 2020 15:40:59 +0100 Subject: [PATCH 12/13] improved phpDoc --- src/ComponentModel/Component.php | 5 +++-- src/ComponentModel/Container.php | 1 + src/ComponentModel/IContainer.php | 3 ++- src/ComponentModel/RecursiveComponentIterator.php | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ComponentModel/Component.php b/src/ComponentModel/Component.php index 3f55ff6..a14bff8 100644 --- a/src/ComponentModel/Component.php +++ b/src/ComponentModel/Component.php @@ -30,7 +30,7 @@ abstract class Component implements IComponent /** @var string|null */ private $name; - /** @var array of [type => [obj, depth, path, array of [attached, detached]]] */ + /** @var array}> means [type => [obj, depth, path, [attached, detached]]] */ private $monitors = []; @@ -203,7 +203,8 @@ protected function validateParent(IContainer $parent): void /** * Refreshes monitors. - * @param array|null $missing (array = attaching, null = detaching) + * @param array|null $missing (array = attaching, null = detaching) + * @param array $listeners */ private function refreshMonitors(int $depth, array &$missing = null, array &$listeners = []): void { diff --git a/src/ComponentModel/Container.php b/src/ComponentModel/Container.php index 847da2d..f3a6de6 100644 --- a/src/ComponentModel/Container.php +++ b/src/ComponentModel/Container.php @@ -166,6 +166,7 @@ protected function createComponent(string $name): ?IComponent /** * Iterates over descendants components. + * @return \Iterator */ final public function getComponents(bool $deep = false, string $filterType = null): \Iterator { diff --git a/src/ComponentModel/IContainer.php b/src/ComponentModel/IContainer.php index e33e610..ea6925e 100644 --- a/src/ComponentModel/IContainer.php +++ b/src/ComponentModel/IContainer.php @@ -28,12 +28,13 @@ function removeComponent(IComponent $component): void; /** * Returns component specified by name or path. - * @throws Nette\InvalidArgumentException if component doesn't exist + * @throws \Nette\InvalidArgumentException if component doesn't exist */ function getComponent(string $name): ?IComponent; /** * Iterates over descendants components. + * @return \Iterator */ function getComponents(): \Iterator; } diff --git a/src/ComponentModel/RecursiveComponentIterator.php b/src/ComponentModel/RecursiveComponentIterator.php index b400e9f..6a43a78 100644 --- a/src/ComponentModel/RecursiveComponentIterator.php +++ b/src/ComponentModel/RecursiveComponentIterator.php @@ -27,6 +27,7 @@ public function hasChildren(): bool /** * The sub-iterator for the current element. + * @return \RecursiveIterator */ public function getChildren(): \RecursiveIterator { From 66409cf5507c77edb46ffa88cf6a92ff58395601 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 23 Jan 2020 14:49:36 +0100 Subject: [PATCH 13/13] Container: component name may be number or null --- src/ComponentModel/Container.php | 7 +++++-- tests/ComponentModel/Container.suggestions.phpt | 1 + tests/ComponentModel/Container.zeroname.phpt | 9 ++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/ComponentModel/Container.php b/src/ComponentModel/Container.php index f3a6de6..c6272f9 100644 --- a/src/ComponentModel/Container.php +++ b/src/ComponentModel/Container.php @@ -40,6 +40,9 @@ public function addComponent(IComponent $component, ?string $name, string $inser { if ($name === null) { $name = $component->getName(); + if ($name === null) { + throw new Nette\InvalidStateException("Missing component's name."); + } } if (!preg_match(self::NAME_REGEXP, $name)) { @@ -65,7 +68,7 @@ public function addComponent(IComponent $component, ?string $name, string $inser if (isset($this->components[$insertBefore])) { $tmp = []; foreach ($this->components as $k => $v) { - if ($k === $insertBefore) { + if ((string) $k === $insertBefore) { $tmp[$name] = $component; } $tmp[$k] = $v; @@ -136,7 +139,7 @@ final public function getComponent(string $name, bool $throw = true): ?IComponen } elseif ($throw) { $hint = Nette\Utils\ObjectHelpers::getSuggestion(array_merge( - array_keys($this->components), + array_map('strval', array_keys($this->components)), array_map('lcfirst', preg_filter('#^createComponent([A-Z0-9].*)#', '$1', get_class_methods($this))) ), $name); throw new Nette\InvalidArgumentException("Component with name '$name' does not exist" . ($hint ? ", did you mean '$hint'?" : '.')); diff --git a/tests/ComponentModel/Container.suggestions.phpt b/tests/ComponentModel/Container.suggestions.phpt index 09f2a9b..838d616 100644 --- a/tests/ComponentModel/Container.suggestions.phpt +++ b/tests/ComponentModel/Container.suggestions.phpt @@ -38,6 +38,7 @@ class TestContainer extends Container $cont = new TestContainer; $cont->addComponent(new TestContainer, 'form'); +$cont->addComponent(new TestContainer, '0'); Assert::exception(function () use ($cont) { $comp = $cont->getComponent('from'); diff --git a/tests/ComponentModel/Container.zeroname.phpt b/tests/ComponentModel/Container.zeroname.phpt index a5396e0..0364386 100644 --- a/tests/ComponentModel/Container.zeroname.phpt +++ b/tests/ComponentModel/Container.zeroname.phpt @@ -14,5 +14,12 @@ require __DIR__ . '/../bootstrap.php'; $container = new Container; -$container->addComponent(new Container, '0'); +$container->addComponent($c0 = new Container, '0'); +Assert::same($c0, $container->getComponent('0')); Assert::same('0', $container->getComponent('0')->getName()); + +$container->addComponent($c1 = new Container, '1', '0'); +Assert::same( + [1 => $c1, 0 => $c0], + (array) $container->getComponents() +);