Skip to content

Commit

Permalink
Redesigned container Setup
Browse files Browse the repository at this point in the history
- Added method to add sub-containers with setup entry
- Added Setup integrity validation (develop environment)
- Created single setting point for validation and circular
  reference detection enabled by Setup connstructor subtype
  • Loading branch information
shudd3r committed Nov 11, 2019
2 parents dda24e8 + 51db7e0 commit 2419bc3
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 98 deletions.
14 changes: 6 additions & 8 deletions src/CompositeContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

class CompositeContainer implements ContainerInterface
{
protected const SEPARATOR = '.';
public const SEPARATOR = '.';

private $records;
private $containers;
Expand Down Expand Up @@ -44,25 +44,23 @@ public function has($id)
private function fromContainers($id)
{
[$containerId, $itemId] = $this->splitId($id);
if (!$containerId || !$itemId || !isset($this->containers[$containerId])) {
if (!isset($this->containers[$containerId])) {
throw new Exception\RecordNotFoundException(sprintf('Record `%s` not defined', $id));
}

return $this->containers[$containerId]->get($itemId);
return $itemId ? $this->containers[$containerId]->get($itemId) : $this->containers[$containerId];
}

private function inContainers(string $id): bool
{
[$containerId, $itemId] = $this->splitId($id);
if (!$containerId || !$itemId) { return false; }
if (!isset($this->containers[$containerId])) { return false; }

return isset($this->containers[$containerId]) ? $this->containers[$containerId]->has($itemId) : false;
return $itemId ? $this->containers[$containerId]->has($itemId) : true;
}

private function splitId(string $id): array
{
return $id[0] === static::SEPARATOR
? [static::SEPARATOR, ltrim($id, static::SEPARATOR)]
: explode(static::SEPARATOR, $id, 2) + [false, null];
return explode(static::SEPARATOR, $id, 2) + [false, null];
}
}
34 changes: 20 additions & 14 deletions src/Setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,30 @@
class Setup
{
private $collection;
private $containers;
private $container;

public function __construct(Setup\Collection $collection = null)
{
$this->collection = $collection ?: new Setup\Collection();
}

public static function secure(): self
{
return new self(new Setup\ValidatedCollection());
}

/**
* @param Records\Record[] $records
* @param ContainerInterface[] $containers
* @param bool $validate
*
* @return self
*/
public function __construct(array $records = [], array $containers = [])
public static function withData(array $records = [], array $containers = [], bool $validate = false): self
{
$this->collection = new Setup\Collection($records);
$this->containers = $containers;
$collection = $validate
? new Setup\ValidatedCollection($records, $containers)
: new Setup\Collection($records, $containers);
return new self($collection);
}

/**
Expand All @@ -40,18 +53,11 @@ public function __construct(array $records = [], array $containers = [])
* encapsulated and not passed to uncontrolled parts of application
* (including container itself).
*
* @param bool $tracking Enables call stack tracking and detects
* circular references
*
* @return ContainerInterface
*/
public function container(bool $tracking = false): ContainerInterface
public function container(): ContainerInterface
{
if ($this->container) { return $this->container; }

return $this->container = $this->containers
? new CompositeContainer($this->collection->records($tracking), $this->containers)
: new RecordContainer($this->collection->records($tracking));
return $this->collection->container();
}

/**
Expand Down
31 changes: 25 additions & 6 deletions src/Setup/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,34 @@
namespace Polymorphine\Container\Setup;

use Polymorphine\Container\Records;
use Polymorphine\Container\RecordContainer;
use Polymorphine\Container\CompositeContainer;
use Polymorphine\Container\Exception;
use Psr\Container\ContainerInterface;


class Collection
{
private $records;
protected const SEPARATOR = CompositeContainer::SEPARATOR;

protected $records;
protected $containers;

/**
* @param Records\Record[] $records
* @param Records\Record[] $records
* @param ContainerInterface[] $containers
*/
public function __construct(array $records)
public function __construct(array $records = [], array $containers = [])
{
$this->records = $records;
$this->records = $records;
$this->containers = $containers;
}

public function records(bool $tracking = false): Records
public function container(): ContainerInterface
{
return $tracking ? new Records\TrackedRecords($this->records) : new Records($this->records);
return $this->containers
? new CompositeContainer(new Records($this->records), $this->containers)
: new RecordContainer(new Records($this->records));
}

public function add(string $id, Records\Record $record): void
Expand All @@ -41,6 +51,15 @@ public function add(string $id, Records\Record $record): void
$this->records[$id] = $record;
}

public function addContainer(string $id, ContainerInterface $container): void
{
if (isset($this->containers[$id])) {
throw new Exception\InvalidIdException(sprintf('Cannot overwrite defined `%s` container', $id));
}

$this->containers[$id] = $container;
}

public function moveRecord(string $id): string
{
if (!isset($this->records[$id])) {
Expand Down
6 changes: 6 additions & 0 deletions src/Setup/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Polymorphine\Container\Records\Record;
use Polymorphine\Container\Exception;
use Psr\Container\ContainerInterface;


/**
Expand Down Expand Up @@ -117,6 +118,11 @@ public function create(string $factoryId, string $method, string ...$arguments):
$this->useRecord(new Record\CreateMethodRecord($factoryId, $method, ...$arguments));
}

public function container(ContainerInterface $container)
{
$this->records->addContainer($this->name, $container);
}

private function decoratedRecordAlias(): string
{
try {
Expand Down
98 changes: 98 additions & 0 deletions src/Setup/ValidatedCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

/*
* This file is part of Polymorphine/Container package.
*
* (c) Shudd3r <q3.shudder@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Polymorphine\Container\Setup;

use Polymorphine\Container\Records;
use Polymorphine\Container\RecordContainer;
use Polymorphine\Container\CompositeContainer;
use Polymorphine\Container\Exception;
use Psr\Container\ContainerInterface;


class ValidatedCollection extends Collection
{
private $reservedIds = [];

public function __construct(array $records = [], array $containers = [])
{
parent::__construct($records, $containers);
$this->validateState();
}

public function container(): ContainerInterface
{
return $this->containers
? new CompositeContainer(new Records\TrackedRecords($this->records), $this->containers)
: new RecordContainer(new Records\TrackedRecords($this->records));
}

public function add(string $id, Records\Record $record): void
{
$this->checkRecordId($id);
parent::add($id, $record);
}

public function addContainer(string $id, ContainerInterface $container): void
{
$this->checkContainerId($id);
parent::addContainer($id, $container);
}

private function validateState()
{
array_map([$this, 'checkRecord'], array_keys($this->records), $this->records);
array_map([$this, 'checkContainer'], array_keys($this->containers), $this->containers);
}

private function checkRecord(string $id, $value): void
{
if (!$value instanceof Records\Record) {
$message = 'Setup record expected instance of Record in `%s` field';
throw new Exception\InvalidArgumentException(sprintf($message, $id));
}
$this->checkRecordId($id);
}

private function checkRecordId(string $id): void
{
$separator = strpos($id, self::SEPARATOR);
$prefix = $separator === false ? $id : substr($id, 0, $separator);
if (isset($this->containers[$prefix])) {
$message = 'Record id or prefix `%s` already used by stored Container';
throw new Exception\InvalidIdException(sprintf($message, $prefix));
}

$this->reservedIds[$prefix] = true;
}

private function checkContainer(string $id, $value): void
{
if (!$value instanceof ContainerInterface) {
$message = 'Setup config expected instance of ContainerInterface in `%s` field';
throw new Exception\InvalidArgumentException(sprintf($message, $id));
}
$this->checkContainerId($id);
}

private function checkContainerId(string $id): void
{
if (strpos($id, self::SEPARATOR) !== false) {
$message = 'Container id cannot contain `%s` separator - `%s` id given';
throw new Exception\InvalidIdException(sprintf($message, self::SEPARATOR, $id));
}

if (isset($this->reservedIds[$id])) {
$message = 'Container id `%s` already used by some record (possibly as prefix)';
throw new Exception\InvalidIdException(sprintf($message, $id));
}
}
}
Loading

0 comments on commit 2419bc3

Please sign in to comment.