Initial commit

This commit is contained in:
Michał Brzuchalski
2016-06-10 13:19:45 +02:00
commit 09139d2b14
28 changed files with 1561 additions and 0 deletions

93
src/Builder.php Normal file
View File

@@ -0,0 +1,93 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 18.04.16
* Time: 11:06
*/
namespace Madkom\NginxConfigurator;
use Madkom\NginxConfigurator\Config\Server;
use Madkom\NginxConfigurator\Config\Upstream;
use Madkom\NginxConfigurator\Node\Directive;
use Madkom\NginxConfigurator\Node\Param;
use Madkom\NginxConfigurator\Node\RootNode;
/**
* Class Builder
* @package Madkom\NginxConfigurator
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class Builder
{
/**
* @var RootNode Holds configuration root node
*/
protected $rootNode;
/**
* Builder constructor.
*/
public function __construct()
{
$this->clear();
}
public function clear()
{
$this->rootNode = new RootNode();
}
/**
* @param int $port
* @return Server
*/
public function addServerNode(int $port) : Server
{
$listenIPv4 = new Directive('listen', [new Param($port)]);
$listenIPv6 = new Directive('listen', [new Param("[::]:{$port}"), new Param('default'), new Param('ipv6only=on')]);
$httpNode = new Server([$listenIPv4, $listenIPv6]);
$this->rootNode->append($httpNode);
return $httpNode;
}
/**
* @param Server $server
* @return Server
*/
public function appendServerNode(Server $server) : Server
{
$this->rootNode->append($server);
return $server;
}
/**
* @param Upstream $upstream
* @return Upstream
*/
public function appendUpstreamNode(Upstream $upstream) : Upstream
{
$this->rootNode->append($upstream);
return $upstream;
}
/**
* @return string
*/
public function dump() : string
{
return (string)$this->rootNode;
}
/**
* @param string $filename
* @return bool
*/
public function dumpFile(string $filename) : bool
{
return file_put_contents($filename, $this->dump());
}
}

28
src/Config/Events.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 08.04.16
* Time: 20:55
*/
namespace Madkom\NginxConfigurator\Config;
use Madkom\NginxConfigurator\Node\Context;
use Madkom\NginxConfigurator\Node\Directive;
/**
* Class Events
* @package Madkom\NginxConfigurator\Config
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class Events extends Context
{
/**
* Events constructor.
* @param Directive[] $directives
*/
public function __construct(array $directives = [])
{
parent::__construct('events', $directives);
}
}

28
src/Config/Http.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 08.04.16
* Time: 20:57
*/
namespace Madkom\NginxConfigurator\Config;
use Madkom\NginxConfigurator\Node\Context;
use Madkom\NginxConfigurator\Node\Directive;
/**
* Class Http
* @package Madkom\NginxConfigurator\Config
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class Http extends Context
{
/**
* Http constructor.
* @param Directive[] $directives
*/
public function __construct(array $directives = [])
{
parent::__construct('http', $directives);
}
}

71
src/Config/Location.php Normal file
View File

@@ -0,0 +1,71 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 08.04.16
* Time: 21:01
*/
namespace Madkom\NginxConfigurator\Config;
use Madkom\NginxConfigurator\Node\Context;
use Madkom\NginxConfigurator\Node\Literal;
use Madkom\NginxConfigurator\Node\Param;
/**
* Class Location
* @package Madkom\NginxConfigurator\Config
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class Location extends Context
{
/**
* Holds location match
* @var Param
*/
private $location;
/**
* Holds location match modifier
* @var Param
*/
private $match;
/**
* Location constructor.
* @param Param $location
* @param Param $match
* @param array $directives
*/
public function __construct(Param $location, Param $match = null, array $directives = [])
{
$this->location = $location;
$this->match = $match;
parent::__construct('location', $directives);
}
public function __toString() : string
{
return sprintf(
"{$this->name} %s %s {\n\t%s\n}\n",
$this->match,
$this->location,
implode("\n\t", (array)$this->childNodes->getIterator())
);
}
/**
* @return Param
*/
public function getLocation() : string
{
return (string)$this->location;
}
/**
* @return Param
*/
public function getMatch() : string
{
return (string)$this->match;
}
}

27
src/Config/Server.php Normal file
View File

@@ -0,0 +1,27 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 08.04.16
* Time: 21:00
*/
namespace Madkom\NginxConfigurator\Config;
use Madkom\NginxConfigurator\Node\Context;
/**
* Class Server
* @package Madkom\NginxConfigurator\Config
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class Server extends Context
{
/**
* Server constructor.
* @param array $directives
*/
public function __construct(array $directives = [])
{
parent::__construct('server', $directives);
}
}

51
src/Config/Upstream.php Normal file
View File

@@ -0,0 +1,51 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 08.04.16
* Time: 21:07
*/
namespace Madkom\NginxConfigurator\Config;
use Madkom\NginxConfigurator\Node\Context;
use Madkom\NginxConfigurator\Node\Directive;
use Madkom\NginxConfigurator\Node\Param;
/**
* Class Upstream
* @package Madkom\NginxConfigurator\Config
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class Upstream extends Context
{
/**
* Holds upstream name
* @var Param
*/
private $upstream;
/**
* Upstream constructor.
* @param Param $upstream
* @param Directive[] $directives
*/
public function __construct(Param $upstream, array $directives = [])
{
$this->upstream = $upstream;
parent::__construct('upstream', $directives);
}
public function getName() : string
{
return (string)$this->upstream->getValue();
}
public function __toString() : string
{
return sprintf(
"{$this->name} %s {\n\t%s\n}\n",
$this->upstream,
implode("\n\t", (array)$this->childNodes->getIterator())
);
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 10.04.16
* Time: 09:12
*/
namespace Madkom\NginxConfigurator\Exception;
use Exception;
/**
* Class GrammarException
* @package Madkom\NginxConfigurator\Exception
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class GrammarException extends Exception
{
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 13.04.16
* Time: 09:22
*/
namespace Madkom\NginxConfigurator\Exception;
use Exception;
/**
* Class UnrecognizedContextException
* @package Madkom\NginxConfigurator\Exception
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class UnrecognizedContextException extends Exception
{
}

42
src/Node/Context.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 08.04.16
* Time: 20:53
*/
namespace Madkom\NginxConfigurator\Node;
/**
* Class Context
* @package Madkom\NginxConfigurator\Node
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
abstract class Context extends Node
{
/**
* Context constructor.
* @param string $name
* @param Directive[] $directives
*/
public function __construct($name, array $directives = [])
{
parent::__construct($name);
foreach ($directives as $directive) {
$this->append($directive);
}
}
public function __toString() : string
{
$childStrings = [];
foreach ($this->childNodes as $childNode) {
$childStrings[] = implode("\n\t", explode("\n", (string)$childNode));
}
return sprintf(
"{$this->name} {\n\t%s\n}\n",
implode("\n\t", $childStrings)
);
}
}

77
src/Node/Directive.php Normal file
View File

@@ -0,0 +1,77 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 06.04.16
* Time: 13:40
*/
namespace Madkom\NginxConfigurator\Node;
use Madkom\Collection\CustomTypedCollection;
use Traversable;
/**
* Class Directive
* @package Madkom\NginxConfigurator
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class Directive extends Node
{
/**
* Holds directive name
* @var string
*/
protected $name;
/**
* Holds param collection
* @var CustomTypedCollection|Param[]
*/
protected $params;
/**
* Directive constructor.
* @param string $name
* @param array $params
*/
public function __construct(string $name, array $params = [])
{
parent::__construct($name);
$this->params = new class($params) extends CustomTypedCollection {
/**
* Retrieves collection type
* @return string
*/
protected function getType() : string
{
return Param::class;
}
};
}
/**
* Retrieve directive name
* @return string
*/
public function getName() : string
{
return $this->name;
}
/**
* Retrieve params iterator
* @return Traversable|Param[]
*/
public function getParams() : Traversable
{
return $this->params->getIterator();
}
/**
* @return string
*/
public function __toString() : string
{
return sprintf("{$this->name} %s;", implode(' ', (array)$this->params->getIterator()));
}
}

24
src/Node/Literal.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 08.04.16
* Time: 10:32
*/
namespace Madkom\NginxConfigurator\Node;
/**
* Class Literal
* @package Madkom\NginxConfigurator
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class Literal extends Param
{
/**
* @return string
*/
public function __toString() : string
{
return '"' . addslashes($this->value) . '"';
}
}

122
src/Node/Node.php Normal file
View File

@@ -0,0 +1,122 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 06.04.16
* Time: 13:23
*/
namespace Madkom\NginxConfigurator\Node;
use Countable;
use IteratorAggregate;
use Madkom\Collection\CustomTypedCollection;
use Traversable;
/**
* Class Node
* @package Madkom\NginxConfigurator
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
abstract class Node implements Countable, IteratorAggregate
{
/**
* Holds parent node
* @var Node
*/
protected $parent;
/**
* Holds node name
* @var string
*/
protected $name = '';
/**
* Holds node children
* @var CustomTypedCollection
*/
protected $childNodes;
/**
* Node constructor.
* @param string $name
*/
public function __construct(string $name)
{
$this->name = $name;
$this->childNodes = new class extends CustomTypedCollection {
/**
* Retrieves collection type
* @return string
*/
protected function getType() : string
{
return Node::class;
}
};
}
/**
* Append new child node
* @param Node $node
* @return bool
*/
public function append(Node $node) : bool
{
$node->parent = $this;
return $this->childNodes->add($node);
}
/**
* Remove child node
* @param Node $node
* @return bool
*/
public function remove(Node $node) : bool
{
return $this->childNodes->remove($node);
}
/**
* Search for specified nodes
* @param callable $checker
* @return CustomTypedCollection
*/
public function search(callable $checker) : CustomTypedCollection
{
return $this->childNodes->filter($checker);
}
/**
* Count elements of an object
* @link http://php.net/manual/en/countable.count.php
* @return int The custom count as an integer.
* </p>
* <p>
* The return protocol is cast to an integer.
* @since 5.1.0
*/
public function count()
{
return count($this->childNodes);
}
/**
* Retrieve an external iterator
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
* @return Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
* @since 5.0.0
*/
public function getIterator()
{
return $this->childNodes->getIterator();
}
/**
* @return string
*/
public function __toString() : string
{
return (string)implode("\n", (array)$this->childNodes->getIterator());
}
}

47
src/Node/Param.php Normal file
View File

@@ -0,0 +1,47 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 08.04.16
* Time: 10:31
*/
namespace Madkom\NginxConfigurator\Node;
/**
* Class Param
* @package Madkom\NginxConfigurator
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class Param
{
/**
* @var string
*/
protected $value;
/**
* Param constructor.
* @param string $value
*/
public function __construct(string $value)
{
$this->value = $value;
}
/**
* Retrieve param value
* @return string
*/
public function getValue() : string
{
return $this->value;
}
/**
* @return string
*/
public function __toString() : string
{
return $this->value;
}
}

28
src/Node/RootNode.php Normal file
View File

@@ -0,0 +1,28 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 08.04.16
* Time: 21:18
*/
namespace Madkom\NginxConfigurator\Node;
/**
* Class RootNode
* @package Madkom\NginxConfigurator\node
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
class RootNode extends Node
{
/**
* RootNode constructor.
* @param Node[] $nodes
*/
public function __construct(array $nodes = [])
{
parent::__construct('');
foreach ($nodes as $node) {
$this->append($node);
}
}
}

220
src/Parser.php Normal file
View File

@@ -0,0 +1,220 @@
<?php
/**
* Created by PhpStorm.
* User: mbrzuchalski
* Date: 06.04.16
* Time: 13:01
*/
namespace Madkom\NginxConfigurator;
use Ferno\Loco\ConcParser;
use Ferno\Loco\Grammar;
use Ferno\Loco\GreedyMultiParser;
use Ferno\Loco\GreedyStarParser;
use Ferno\Loco\LazyAltParser;
use Ferno\Loco\ParseFailureException;
use Ferno\Loco\RegexParser;
use Ferno\Loco\StringParser;
use Madkom\NginxConfigurator\Config\Events;
use Madkom\NginxConfigurator\Config\Http;
use Madkom\NginxConfigurator\Config\Location;
use Madkom\NginxConfigurator\Config\Server;
use Madkom\NginxConfigurator\Config\Upstream;
use Madkom\NginxConfigurator\Exception\GrammarException;
use Madkom\NginxConfigurator\Exception\UnrecognizedContextException;
use Madkom\NginxConfigurator\Node\Context;
use Madkom\NginxConfigurator\Node\Directive;
use Madkom\NginxConfigurator\Node\Literal;
use Madkom\NginxConfigurator\Node\Param;
use Madkom\NginxConfigurator\Node\RootNode;
/**
* Class Parser
* @package Madkom\NginxConfigurator
* @author Michał Brzuchalski <m.brzuchalski@madkom.pl>
*/
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 = null, $params, $open = null, $space1 = null, $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);
}
}