diff --git a/README.md b/README.md
index f2934e0..aef2a0a 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
NGINX Configurator
==================
-PHP Library for NGINX configuration parser/generator
+PHP Library for NGINX configuration generator

@@ -27,60 +27,6 @@ This library requires *PHP* in `>=8.1` version.
## Usage
-Parsing configuration string:
-
-```php
-use Envoyr\NginxConfigurator\Builder;
-use Envoyr\NginxConfigurator\Config\Server;
-use Envoyr\NginxConfigurator\Parser;
-
-require 'vendor/autoload.php';
-
-$config = <<
+ * The string representation of the object.
+ *
+ * The return protocol is cast to an integer. + * @since 5.1.0 + */ + public function count() + { + return count($this->elements); + } + + /** + * Retrieve an external iterator + * @link http://php.net/manual/en/iteratoraggregate.getiterator.php + * @return Traversable An instance of an object implementing Iterator or + * Traversable + * @since 5.0.0 + */ + public function getIterator() + { + return new ArrayIterator($this->elements); + } +} diff --git a/src/Collection/ComparableDistinctTypedCollection.php b/src/Collection/ComparableDistinctTypedCollection.php new file mode 100644 index 0000000..c2265b2 --- /dev/null +++ b/src/Collection/ComparableDistinctTypedCollection.php @@ -0,0 +1,53 @@ + + */ +abstract class ComparableDistinctTypedCollection extends CustomTypedCollection +{ + /** + * Checks two elements are equal + * @param $left + * @param $right + * @return bool + */ + abstract public function compareTo($left, $right) : bool; + + /** + * @inheritDoc + */ + public function add($element) : bool + { + if ($this->contains($element)) { + throw new RuntimeException("Given element already exists in collection"); + } + + return parent::add($element); + } + + + /** + * @inheritDoc + */ + public function contains($element) : bool + { + if (!$this->isElementValid($element)) { + throw new UnexpectedValueException( + "Unexpected element type, expecting: {$this->getType()}, given: " . get_class($element) + ); + } + foreach ($this->elements as $current) { + if ($this->compareTo($current, $element)) { + return true; + } + } + + return false; + } +} diff --git a/src/Collection/CustomDistinctCollection.php b/src/Collection/CustomDistinctCollection.php new file mode 100644 index 0000000..7bcf324 --- /dev/null +++ b/src/Collection/CustomDistinctCollection.php @@ -0,0 +1,51 @@ + + */ +abstract class CustomDistinctCollection extends CustomTypedCollection +{ + /** + * @return string + */ + abstract protected function getMethod() : string; + + /** + * @inheritDoc + */ + public function add($element) : bool + { + if ($this->contains($element)) { + throw new RuntimeException("Given element already exists in collection"); + } + + return parent::add($element); + } + + + /** + * @inheritDoc + */ + public function contains($element) : bool + { + if (!$this->isElementValid($element)) { + throw new UnexpectedValueException( + "Unexpected element type, expecting: {$this->getType()}, given: " . get_class($element) + ); + } + $distinct = $element->{$this->getMethod()}(); + foreach ($this->elements as $current) { + if ($current->{$this->getMethod()}() == $distinct) { + return true; + } + } + + return false; + } +} diff --git a/src/Collection/CustomTypedCollection.php b/src/Collection/CustomTypedCollection.php new file mode 100644 index 0000000..0fcba07 --- /dev/null +++ b/src/Collection/CustomTypedCollection.php @@ -0,0 +1,69 @@ + + */ +abstract class CustomTypedCollection extends Collection +{ + /** + * Retrieves collection type + * @return string + */ + abstract protected function getType() : string; + + /** + * AbstractTypedCollection constructor. + * @param array $elements + */ + public function __construct(array $elements = []) + { + if (!class_exists($this->getType())) { + throw new InvalidArgumentException("Expected type should be accessible class name, given: {$this->getType()}"); + } + parent::__construct($elements); + } + + /** + * @inheritDoc + */ + public function add($element) : bool + { + if (!$this->isElementValid($element)) { + throw new UnexpectedValueException( + "Unexpected element type, expecting: {$this->getType()}, given: " . get_class($element) + ); + } + + return parent::add($element); + } + + /** + * @inheritDoc + */ + public function remove($element) : bool + { + if (!$this->isElementValid($element)) { + throw new UnexpectedValueException( + "Unexpected element type, expecting: {$this->getType()}, given: " . get_class($element) + ); + } + + return parent::remove($element); + } + + /** + * Check is element valid object type + * @param $element + * @return bool + */ + protected function isElementValid($element) : bool + { + return is_a($element, $this->getType()) || is_subclass_of($element, $this->getType()); + } +} diff --git a/src/Collection/DistinctCollection.php b/src/Collection/DistinctCollection.php new file mode 100644 index 0000000..3ee3336 --- /dev/null +++ b/src/Collection/DistinctCollection.php @@ -0,0 +1,57 @@ + + */ +class DistinctCollection extends CustomDistinctCollection +{ + /** + * @var string Collection generic type + */ + protected $type; + /** + * @var string Generic type distinguish method name + */ + private $method; + + /** + * UniqueGenericCollection constructor. + * @param string $type + * @param string $method + * @param array $elements + */ + public function __construct(string $type, string $method, array $elements = []) + { + if (!method_exists($type, $method) && is_callable($type, $method)) { + throw new InvalidArgumentException( + "Non-existent distinct method name in class {$type}, given: {$method}" + ); + } + $this->type = $type; + $this->method = $method; + parent::__construct($elements); + } + + /** + * @return string + */ + protected function getType() : string + { + return $this->type; + } + + /** + * @inheritDoc + */ + protected function getMethod() : string + { + return $this->method; + } +} diff --git a/src/Collection/TypedCollection.php b/src/Collection/TypedCollection.php new file mode 100644 index 0000000..28c5e4b --- /dev/null +++ b/src/Collection/TypedCollection.php @@ -0,0 +1,34 @@ + + */ +class TypedCollection extends CustomTypedCollection +{ + /** + * @var string Collection generic type + */ + protected $type; + + /** + * GenericCollection constructor. + * @param string $type + * @param array $elements + */ + public function __construct(string $type, array $elements = []) + { + $this->type = $type; + parent::__construct($elements); + } + + /** + * @return string + */ + protected function getType() : string + { + return $this->type; + } +} diff --git a/src/Node/Directive.php b/src/Node/Directive.php index bbe5228..39adc88 100644 --- a/src/Node/Directive.php +++ b/src/Node/Directive.php @@ -7,7 +7,7 @@ */ namespace Envoyr\NginxConfigurator\Node; -use Envoyr\Collection\CustomTypedCollection; +use Envoyr\NginxConfigurator\Collection\CustomTypedCollection; use Traversable; /** diff --git a/src/Node/Node.php b/src/Node/Node.php index ad1a9d4..6cd7729 100644 --- a/src/Node/Node.php +++ b/src/Node/Node.php @@ -9,7 +9,7 @@ namespace Envoyr\NginxConfigurator\Node; use Countable; use IteratorAggregate; -use Envoyr\Collection\CustomTypedCollection; +use Envoyr\NginxConfigurator\Collection\CustomTypedCollection; use Traversable; /** diff --git a/src/Parser.php b/src/Parser.php deleted file mode 100644 index 972ec09..0000000 --- a/src/Parser.php +++ /dev/null @@ -1,220 +0,0 @@ - - */ -class Parser extends Grammar -{ - /** - * Holds parsed filename - * @var string - */ - protected $filename; - /** - * Holds parsed string - * @var string - */ - protected $content; - - /** - * Parser constructor. - */ - public function __construct() - { - parent::__construct('syntax', [ - 'syntax' => new GreedyStarParser(new LazyAltParser(['directive', 'section'])), - 'sections' => new GreedyMultiParser('section', 0, 2), - 'section' => new ConcParser( - [ - 'section-name', - new LazyAltParser(['space', 'opt-space']), - new LazyAltParser(['params', new LazyAltParser(['space', 'opt-space'])]), - new StringParser('{'), - new LazyAltParser(['space', 'opt-space']), - new GreedyMultiParser(new LazyAltParser(['directive', 'section']), 0, null), - new LazyAltParser(['space', 'opt-space']), - new StringParser('}'), - new LazyAltParser(['space', 'opt-space']), - ], - [$this, 'parseSection'] - ), - 'section-name' => new RegexParser('/^[a-z0-9\_]+/i'), - - 'directives' => new GreedyMultiParser('directive', 0, null), - 'directive' => new LazyAltParser([ - new ConcParser([ - 'directive-name', - 'semicolon', - new LazyAltParser(['space', 'opt-space']), - ], [$this, 'parseDirective']), - new ConcParser([ - 'directive-name', - 'space', - 'params', - 'semicolon', - new LazyAltParser(['space', 'opt-space']), - ], [$this, 'parseDirective']) - ]), - 'directive-name' => new RegexParser('/^[a-z0-9\_]+/i'), - - 'params' => new GreedyMultiParser(new ConcParser(['param', 'opt-space'], function ($param, $space) { - return $param; - }), 0, null), - 'param' => new LazyAltParser(['literal', 'param-name']), - 'param-name' => new RegexParser('/^[^\s\r\n\{\}\;\"\']+/i', function ($match) { - return new Param($match); - }), - 'literal' => new LazyAltParser([ - new RegexParser('/^"([^"]*)"/', function ($match0, $match1) { - return new Literal($match1); - }), - new RegexParser("/^'([^']*)'/", function ($match0, $match1) { - return new Literal($match1); - }) - ]), - - 'semicolon' => new StringParser(';', function () { - return null; - }), - 'space' => new GreedyStarParser('whitespace/comment', function () { - return null; - }), - 'whitespace/comment' => new LazyAltParser(['whitespace', 'comment'], function () { - return null; - }), - 'comment' => new RegexParser("/^#+([^\r\n]*)/", function () { - return null; - }), - 'whitespace' => new RegexParser("/^[ \t\r\n]+/"), - 'opt-space' => new RegexParser("/^[ \t\r\n]?/"), - 'eol' => new LazyAltParser([new StringParser("\r"), new StringParser("\n")], function () { - return null; - }) - ], function (array $nodes = []) { - return new RootNode($nodes); - }); - } - - /** - * Parses config file - * @param string $filename - * @return mixed - * @throws ParseFailureException - */ - public function parseFile(string $filename) : RootNode - { - $this->content = null; - $this->filename = $filename; - - return $this->parse(file_get_contents($filename)); - } - - /** - * Parses string - * @param string $string - * @return mixed - * @throws ParseFailureException - */ - public function parse($string) : RootNode - { - $this->content = $string; - $this->filename = null; - - return parent::parse($string); - } - - /** - * Parses section entries - * @param string $section Section name - * @param null $space0 Ignored - * @param Param[] $params Params collection - * @param null $open Ignored - * @param null $space1 Ignored - * @param Directive[] $directives Directives collection - * @return Context - * @throws GrammarException - * @throws UnrecognizedContextException - */ - protected function parseSection($section, $space0, $params, $open, $space1, $directives) : Context - { - switch ($section) { - case 'server': - return new Server($directives); - - case 'http': - return new Http($directives); - - case 'location': - $modifier = null; - if (sizeof($params) == 2) { - list($modifier, $location) = $params; - } elseif (sizeof($params) == 1) { - $location = $params[0]; - } else { - throw new GrammarException( - sprintf( - "Location context missing in %s", - $this->filename ? var_export($this->filename, true) : var_export($this->content, true) - ) - ); - } - return new Location($location, $modifier, $directives); - - case 'events': - return new Events($directives); - - case 'upstream': - list($upstream) = $params; - return new Upstream($upstream, $directives); - } - - throw new UnrecognizedContextException( - sprintf( - "Unrecognized context: {$section} found in %s", - $this->filename ? var_export($this->filename, true) : var_export($this->content, true) - ) - ); - } - - /** - * Parses directive - * @param string $name - * @param null $space - * @param array $params - * @return Directive - */ - protected function parseDirective(string $name, $space = null, $params = []) : Directive - { - return new Directive($name, is_null($params) ? [] : $params); - } -}