Skip to content
Permalink
Browse files

Component: added lookupIfExists() & lookupPathIfExists()

  • Loading branch information...
dg committed Nov 1, 2019
1 parent ca036af commit 4c303c1375fd268c73943a08e975e494209e7a61
Showing with 125 additions and 12 deletions.
  1. +1 −0 .phpstorm.meta.php
  2. +30 −12 src/ComponentModel/Component.php
  3. +94 −0 tests/ComponentModel/Container.lookup.phpt
@@ -5,3 +5,4 @@
namespace PHPSTORM_META;
override(\Nette\ComponentModel\Component::lookup(0), map(['' => '@']));
override(\Nette\ComponentModel\Component::lookupIfExists(0), map(['' => '@']));
@@ -39,6 +39,21 @@ abstract class Component implements IComponent
* @param bool $throw throw exception if component doesn't exist?
*/
final public function lookup(?string $type, bool $throw = true): ?IComponent
{
if (($res = $this->lookupIfExists($type)) || !$throw) {
return $res;
}
$message = $this->name !== null
? "Component '$this->name' is not attached to '$type'."
: "Component of type '" . static::class . "' is not attached to '$type'.";
throw new Nette\InvalidStateException($message);
}
/**
* Finds the closest ancestor specified by class or interface name.
*/
final public function lookupIfExists(?string $type): ?IComponent
{
if (!isset($this->monitors[$type])) { // not monitored or not processed yet
$obj = $this->parent;
@@ -64,25 +79,28 @@ abstract class Component implements IComponent
$this->monitors[$type] = [null, null, null, []]; // not found
}
}
if ($throw && $this->monitors[$type][0] === null) {
$message = $this->name !== null
? "Component '$this->name' is not attached to '$type'."
: "Component of type '" . static::class . "' is not attached to '$type'.";
throw new Nette\InvalidStateException($message);
}
return $this->monitors[$type][0];
}
/**
* Finds the closest ancestor specified by class or interface name and returns backtrace path.
* A path is the concatenation of component names separated by self::NAME_SEPARATOR.
* A path is the concatenation of component names separated by dash.
*/
final public function lookupPath(string $type = null, bool $throw = true): ?string
{
$this->lookup($type, $throw);
$throw ? $this->lookup($type) : $this->lookupIfExists($type);
return $this->monitors[$type][2];
}
/**
* Finds the closest ancestor specified by class or interface name and returns backtrace path.
* A path is the concatenation of component names separated by dash.
*/
final public function lookupPathIfExists(?string $type): ?string
{
$this->lookupIfExists($type);
return $this->monitors[$type][2];
}
@@ -96,7 +114,7 @@ abstract class Component implements IComponent
$attached = [$this, 'attached'];
$detached = [$this, 'detached'];
}
if (($obj = $this->lookup($type, false)) && $attached && !in_array([$attached, $detached], $this->monitors[$type][3], true)) {
if (($obj = $this->lookupIfExists($type)) && $attached && !in_array([$attached, $detached], $this->monitors[$type][3], true)) {
$attached($obj);
}
$this->monitors[$type][3][] = [$attached, $detached]; // mark as monitored
@@ -242,7 +260,7 @@ private function refreshMonitors(int $depth, array &$missing = null, array &$lis
} else {
$this->monitors[$type] = null; // forces re-lookup
if ($obj = $this->lookup($type, false)) {
if ($obj = $this->lookupIfExists($type)) {
foreach ($rec[3] as $pair) {
$listeners[] = [$pair[0], $obj];
}
@@ -0,0 +1,94 @@
<?php
/**
* Test: Nette\ComponentModel\Container lookup.
*/
declare(strict_types=1);
use Nette\ComponentModel\Container;
use Tester\Assert;
require __DIR__ . '/../bootstrap.php';
class TestClass extends Container implements ArrayAccess
{
use Nette\ComponentModel\ArrayAccess;
}
class A extends TestClass
{
}
class B extends TestClass
{
}
class C extends TestClass
{
}
class D extends TestClass
{
}
$a = new A;
$a['b'] = $b = new B;
$a['b']['c'] = $c = new C;
$a['b']['c']['c2'] = $c2 = new C;
$a['b']['c']['c2']['d'] = $d = new D;
// top
Assert::same($a, $d->lookup(null));
Assert::same($a, $d->lookupIfExists(null));
Assert::same('b-c-c2-d', $d->lookupPath());
Assert::same('b-c-c2-d', $d->lookupPathIfExists(null));
// specified top
Assert::same($a, $d->lookup(A::class));
Assert::same($a, $d->lookupIfExists(A::class));
Assert::same('b-c-c2-d', $d->lookupPath(A::class));
Assert::same('b-c-c2-d', $d->lookupPathIfExists(A::class));
// other
Assert::same($b, $d->lookup(B::class));
Assert::same($b, $d->lookupIfExists(B::class));
Assert::same('c-c2-d', $d->lookupPath(B::class));
Assert::same('c-c2-d', $d->lookupPathIfExists(B::class));
// from top to bottom
Assert::same($c2, $d->lookup(C::class));
Assert::same($c2, $d->lookupIfExists(C::class));
Assert::same('d', $d->lookupPath(C::class));
Assert::same('d', $d->lookupPathIfExists(C::class));
// self
Assert::exception(function () use ($d) {
$d->lookup(D::class);
}, Nette\InvalidStateException::class, "Component 'd' is not attached to 'D'.");
Assert::null($d->lookupIfExists(D::class));
Assert::exception(function () use ($d) {
$d->lookupPath(D::class);
}, Nette\InvalidStateException::class, "Component 'd' is not attached to 'D'.");
Assert::null($d->lookupPathIfExists(D::class));
// not exists
Assert::exception(function () use ($d) {
$d->lookup('Unknown');
}, Nette\InvalidStateException::class, "Component 'd' is not attached to 'Unknown'.");
Assert::null($d->lookupIfExists('Unknown'));
Assert::exception(function () use ($d) {
$d->lookupPath('Unknown');
}, Nette\InvalidStateException::class, "Component 'd' is not attached to 'Unknown'.");
Assert::null($d->lookupPathIfExists('Unknown'));

0 comments on commit 4c303c1

Please sign in to comment.
You can’t perform that action at this time.