├── tests
├── coverage
│ └── .gitkeep
└── spec
│ ├── FactorySpec.php
│ ├── BuilderSpec.php
│ └── ParserSpec.php
├── .gitignore
├── .coveralls.yml
├── test.conf
├── phpspec.yml
├── phpcs.xml
├── src
├── Exception
│ ├── GrammarException.php
│ └── UnrecognizedContextException.php
├── Node
│ ├── Literal.php
│ ├── RootNode.php
│ ├── Param.php
│ ├── Context.php
│ ├── Directive.php
│ └── Node.php
├── Command
│ ├── AddUpstreamCommand.php
│ ├── RemoveUpstream.php
│ ├── RemoveLocationCommand.php
│ ├── AddUpstreamServerCommand.php
│ ├── RemoveUpstreamServerCommand.php
│ ├── BaseCommand.php
│ ├── RemoveServerCommand.php
│ ├── AddServerCommand.php
│ └── AddLocationCommand.php
├── Config
│ ├── Server.php
│ ├── Http.php
│ ├── Events.php
│ ├── Upstream.php
│ └── Location.php
├── Factory.php
├── Builder.php
└── Parser.php
├── .travis.yml
├── examples
├── default.conf
├── parse.php
└── build.php
├── phpunit.xml
├── LICENSE
├── composer.json
├── bin
└── ngxconf
└── README.md
/tests/coverage/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /.sonar/
3 | /.idea/
4 | /bin/*
5 | !/bin/ngxconf
6 | /tests/coverage/*
7 | !tests/coverage/.gitkeep
8 |
--------------------------------------------------------------------------------
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | # for php-coveralls
2 | coverage_clover: tests/coverage/clover.xml
3 | json_path: tests/coverage/coveralls-upload.json
--------------------------------------------------------------------------------
/test.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 90;
3 | listen [::]:90 default ipv6only=on;
4 | server_name test-ms;
5 | }
6 |
7 | server {
8 | listen 90;
9 | listen [::]:90 default ipv6only=on;
10 | server_name test-ms;
11 | }
12 |
--------------------------------------------------------------------------------
/phpspec.yml:
--------------------------------------------------------------------------------
1 | extensions:
2 | - PhpSpec\Extension\CodeCoverageExtension
3 | - Knp\PhpSpec\WellDone\Extension
4 |
5 | suites:
6 | types:
7 | namespace: Madkom\NginxConfigurator
8 | psr4_prefix: Madkom\NginxConfigurator
9 | spec_path: tests
10 |
11 | code_coverage:
12 | output: tests/coverage/phpspec.cov
13 | format: php
14 |
15 | formatter.name: pretty
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Project Coding Standard
4 | ./src
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/Exception/GrammarException.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class GrammarException extends Exception
18 | {
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/Exception/UnrecognizedContextException.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class UnrecognizedContextException extends Exception
18 | {
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.0
5 | - nightly
6 |
7 | sudo: false
8 |
9 | matrix:
10 | allow_failures:
11 | - php: hhvm
12 | - php: nightly
13 |
14 | before_install:
15 | - composer self-update
16 |
17 | install:
18 | - composer install --prefer-dist --no-interaction
19 |
20 | script:
21 | - bin/phpspec run --format=pretty --no-code-generation
22 | - bin/phpcov merge --clover tests/coverage/clover.xml tests/coverage
23 |
24 | after_success:
25 | - travis_retry php bin/coveralls -v
--------------------------------------------------------------------------------
/src/Node/Literal.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Literal extends Param
16 | {
17 | /**
18 | * @return string
19 | */
20 | public function __toString() : string
21 | {
22 | return '"' . addslashes($this->value) . '"';
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Command/AddUpstreamCommand.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class AddUpstreamCommand extends BaseCommand
16 | {
17 | protected function configure()
18 | {
19 | parent::configure();
20 | $this->setName('upstream:add');
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Command/RemoveUpstream.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class RemoveUpstreamCommand extends BaseCommand
16 | {
17 | protected function configure()
18 | {
19 | parent::configure();
20 | $this->setName('upstream:remove');
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Command/RemoveLocationCommand.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class RemoveLocationCommand extends BaseCommand
16 | {
17 | protected function configure()
18 | {
19 | parent::configure();
20 | $this->setName('location:remove');
21 | $this->setDescription("Remove location context and it's configuration");
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Config/Server.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class Server extends Context
18 | {
19 | /**
20 | * Server constructor.
21 | * @param array $directives
22 | */
23 | public function __construct(array $directives = [])
24 | {
25 | parent::__construct('server', $directives);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Node/RootNode.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class RootNode extends Node
16 | {
17 | /**
18 | * RootNode constructor.
19 | * @param Node[] $nodes
20 | */
21 | public function __construct(array $nodes = [])
22 | {
23 | parent::__construct('');
24 | foreach ($nodes as $node) {
25 | $this->append($node);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Command/AddUpstreamServerCommand.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class AddUpstreamServerCommand extends BaseCommand
16 | {
17 | protected function configure()
18 | {
19 | parent::configure();
20 | $this->setName('upstream:server:add');
21 | $this->setDescription("Adds server directive to upstream context and configuration");
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Command/RemoveUpstreamServerCommand.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class RemoveUpstreamServerCommand extends BaseCommand
16 | {
17 | protected function configure()
18 | {
19 | parent::configure();
20 | $this->setName('upstream:server:remove');
21 | $this->setDescription("Removes server directive from upstream context and configuration");
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Config/Http.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class Http extends Context
19 | {
20 | /**
21 | * Http constructor.
22 | * @param Directive[] $directives
23 | */
24 | public function __construct(array $directives = [])
25 | {
26 | parent::__construct('http', $directives);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Config/Events.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class Events extends Context
19 | {
20 | /**
21 | * Events constructor.
22 | * @param Directive[] $directives
23 | */
24 | public function __construct(array $directives = [])
25 | {
26 | parent::__construct('events', $directives);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 8080;
3 | root /data/www/web;
4 | index index.php index.html index.htm;
5 |
6 | location / {
7 | try_files $uri $uri/ /index.php;
8 | }
9 |
10 | error_page 404 /404.html;
11 |
12 | error_page 500 502 503 504 /50x.html;
13 | location = /50x.html {
14 | root /usr/share/nginx/www;
15 | }
16 |
17 | # pass the PHP scripts to FastCGI server listening on the php-fpm socket
18 | location ~ \.php$ {
19 | try_files $uri =404;
20 | fastcgi_pass unix:/var/run/php5-fpm.sock;
21 | fastcgi_index index.php;
22 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
23 | include fastcgi_params;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/spec/FactorySpec.php:
--------------------------------------------------------------------------------
1 |
14 | * @mixin Factory
15 | */
16 | class FactorySpec extends ObjectBehavior
17 | {
18 | function it_is_initializable()
19 | {
20 | $this->shouldHaveType(Factory::class);
21 | }
22 |
23 | function it_can_create_Server_node()
24 | {
25 | $this->createServer(80)->shouldReturnAnInstanceOf(Server::class);
26 | }
27 |
28 | function it_can_create_Location_node()
29 | {
30 | $this->createLocation('/test', '~')->shouldReturnAnInstanceOf(Location::class);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Node/Param.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Param
16 | {
17 | /**
18 | * @var string
19 | */
20 | protected $value;
21 |
22 | /**
23 | * Param constructor.
24 | * @param string $value
25 | */
26 | public function __construct(string $value)
27 | {
28 | $this->value = $value;
29 | }
30 |
31 | /**
32 | * Retrieve param value
33 | * @return string
34 | */
35 | public function getValue() : string
36 | {
37 | return $this->value;
38 | }
39 |
40 | /**
41 | * @return string
42 | */
43 | public function __toString() : string
44 | {
45 | return $this->value;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | ./tests/phpunit/
10 | ./vendor
11 | ./app
12 |
13 |
14 |
15 |
16 | ./vendor
17 | ./app
18 | ./bin
19 | ./tests
20 | ./var/bootstrap.php.cache
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/tests/spec/BuilderSpec.php:
--------------------------------------------------------------------------------
1 |
14 | * @mixin Builder
15 | */
16 | class BuilderSpec extends ObjectBehavior
17 | {
18 | function it_is_initializable()
19 | {
20 | $this->shouldHaveType(Builder::class);
21 | }
22 |
23 | function it_can_build_with_Server_node(Server $server)
24 | {
25 | $server->__toString()->willReturn("server {
26 | }");
27 | $this->append($server);
28 | $this->dump()->shouldBeString();
29 | }
30 |
31 | function it_can_build_with_Upstream_node(Upstream $upstream)
32 | {
33 | $upstream->__toString()->willReturn("upstream name {
34 | }");
35 | $this->append($upstream);
36 | $this->dump()->shouldBeString();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Madkom S.A.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/src/Node/Context.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | abstract class Context extends Node
16 | {
17 | /**
18 | * Context constructor.
19 | * @param string $name
20 | * @param Directive[] $directives
21 | */
22 | public function __construct($name, array $directives = [])
23 | {
24 | parent::__construct($name);
25 | foreach ($directives as $directive) {
26 | $this->append($directive);
27 | }
28 | }
29 |
30 | public function __toString() : string
31 | {
32 | $childStrings = [];
33 | foreach ($this->childNodes as $childNode) {
34 | $childStrings[] = implode("\n\t", explode("\n", (string)$childNode));
35 | }
36 |
37 | return sprintf(
38 | "{$this->name} {\n\t%s\n}\n",
39 | implode("\n\t", $childStrings)
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "madkom/nginx-configurator",
3 | "license": "MIT",
4 | "homepage": "http://madkom.pl/",
5 | "minimum-stability": "dev",
6 | "require": {
7 | "madkom/collection": "1.*",
8 | "ferno/loco": "@dev",
9 | "madkom/uri": "1.*"
10 | },
11 | "require-dev": {
12 | "henrikbjorn/phpspec-code-coverage": "^2.0.2",
13 | "phpspec/phpspec": "^2.5",
14 | "phpunit/phpunit": "~4",
15 | "knplabs/phpspec-welldone-extension": "dev-master",
16 | "squizlabs/php_codesniffer": "^2.3",
17 | "phpunit/phpcov": "*",
18 | "jakub-onderka/php-parallel-lint": "0.*",
19 | "jakub-onderka/php-console-highlighter": "0.*",
20 | "satooshi/php-coveralls": "dev-master",
21 | "clover/dump": "dev-master",
22 | "symfony/var-dumper": "^3.1"
23 | },
24 | "repositories": [
25 | {
26 | "type": "vcs",
27 | "url": "git@github.com:madkom/loco.git"
28 | }
29 | ],
30 | "autoload": {
31 | "psr-4": {
32 | "Madkom\\NginxConfigurator\\": "src/"
33 | }
34 | },
35 | "authors": [
36 | {
37 | "name": "Michał Brzuchalski",
38 | "email": "m.brzuchalski@madkom.pl"
39 | }
40 | ],
41 | "config": {
42 | "bin-dir": "bin/"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Config/Upstream.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | class Upstream extends Context
20 | {
21 | /**
22 | * Holds upstream name
23 | * @var Param
24 | */
25 | private $upstream;
26 |
27 | /**
28 | * Upstream constructor.
29 | * @param Param $upstream
30 | * @param Directive[] $directives
31 | */
32 | public function __construct(Param $upstream, array $directives = [])
33 | {
34 | $this->upstream = $upstream;
35 | parent::__construct('upstream', $directives);
36 | }
37 |
38 | public function getName() : string
39 | {
40 | return (string)$this->upstream->getValue();
41 | }
42 |
43 | public function __toString() : string
44 | {
45 | return sprintf(
46 | "{$this->name} %s {\n\t%s\n}\n",
47 | $this->upstream,
48 | implode("\n\t", (array)$this->childNodes->getIterator())
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Factory.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | class Factory
21 | {
22 | /**
23 | * Creates Server node
24 | * @param int $port
25 | * @return Server
26 | */
27 | public function createServer(int $port = 80) : Server
28 | {
29 | $listenIPv4 = new Directive('listen', [new Param($port)]);
30 | $listenIPv6 = new Directive('listen', [new Param("[::]:{$port}"), new Param('default'), new Param('ipv6only=on')]);
31 |
32 | return new Server([$listenIPv4, $listenIPv6]);
33 | }
34 |
35 | /**
36 | * Creates Location node
37 | * @param string $location
38 | * @param string|null $match
39 | * @return Location
40 | */
41 | public function createLocation(string $location, string $match = null) : Location
42 | {
43 | return new Location(new Param($location), is_null($match) ? null : new Param($match));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/bin/ngxconf:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | add(new AddServerCommand());
30 | $app->add(new RemoveServerCommand());
31 | $app->add(new AddLocationCommand());
32 | $app->add(new RemoveLocationCommand());
33 | $app->add(new AddUpstreamServerCommand());
34 | $app->add(new RemoveUpstreamServerCommand());
35 | $app->run();
36 |
--------------------------------------------------------------------------------
/examples/parse.php:
--------------------------------------------------------------------------------
1 | parse($config);
40 |
41 | /** @var Server $defaultServers[] */
42 | $defaultServers = $defaultConfig->search(function (Node $node) {
43 | return $node instanceof Server;
44 | });
45 |
46 |
47 |
48 | $builder = new Builder();
49 | if (count($defaultServers) > 0) {
50 | /** @var Server $defaultServer */
51 | foreach ($defaultServers as $defaultServer) {
52 | $builder->appendServerNode($defaultServer);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Config/Location.php:
--------------------------------------------------------------------------------
1 |
18 | */
19 | class Location extends Context
20 | {
21 | /**
22 | * Holds location match
23 | * @var Param
24 | */
25 | private $location;
26 |
27 | /**
28 | * Holds location match modifier
29 | * @var Param
30 | */
31 | private $match;
32 |
33 | /**
34 | * Location constructor.
35 | * @param Param $location
36 | * @param Param $match
37 | * @param array $directives
38 | */
39 | public function __construct(Param $location, Param $match = null, array $directives = [])
40 | {
41 | $this->location = $location;
42 | $this->match = $match;
43 | parent::__construct('location', $directives);
44 | }
45 |
46 | public function __toString() : string
47 | {
48 | return sprintf(
49 | "{$this->name} %s %s {\n\t%s\n}\n",
50 | $this->match,
51 | $this->location,
52 | implode("\n\t", (array)$this->childNodes->getIterator())
53 | );
54 | }
55 |
56 | /**
57 | * @return Param
58 | */
59 | public function getLocation() : string
60 | {
61 | return (string)$this->location;
62 | }
63 |
64 | /**
65 | * @return Param
66 | */
67 | public function getMatch() : string
68 | {
69 | return (string)$this->match;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Command/BaseCommand.php:
--------------------------------------------------------------------------------
1 |
24 | */
25 | abstract class BaseCommand extends Command
26 | {
27 | protected function configure()
28 | {
29 | $this->addOption('file', 'f', InputOption::VALUE_OPTIONAL, 'Output filename', 'php://stdout');
30 | }
31 |
32 | /**
33 | * @param InputInterface $input
34 | * @return RootNode
35 | * @throws Exception
36 | */
37 | protected function getConfig(InputInterface $input) : RootNode
38 | {
39 | $filename = $input->getOption('file');
40 | if ($filename != 'php://stdout' && !file_exists($filename)) {
41 | @touch($filename);
42 | }
43 | if ($filename != 'php://stdout' && file_exists($filename)) {
44 | if (!is_writable($filename)) {
45 | throw new Exception('Given filename is not writable!');
46 | }
47 | }
48 | if ($filename != 'php://stdout' && file_exists($filename)) {
49 | $parser = new Parser();
50 | return $parser->parseFile($filename);
51 | }
52 |
53 | return new RootNode();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Node/Directive.php:
--------------------------------------------------------------------------------
1 |
17 | */
18 | class Directive extends Node
19 | {
20 | /**
21 | * Holds directive name
22 | * @var string
23 | */
24 | protected $name;
25 | /**
26 | * Holds param collection
27 | * @var CustomTypedCollection|Param[]
28 | */
29 | protected $params;
30 |
31 | /**
32 | * Directive constructor.
33 | * @param string $name
34 | * @param array $params
35 | */
36 | public function __construct(string $name, array $params = [])
37 | {
38 | parent::__construct($name);
39 | $this->params = new class($params) extends CustomTypedCollection {
40 |
41 | /**
42 | * Retrieves collection type
43 | * @return string
44 | */
45 | protected function getType() : string
46 | {
47 | return Param::class;
48 | }
49 | };
50 | }
51 |
52 | /**
53 | * Retrieve directive name
54 | * @return string
55 | */
56 | public function getName() : string
57 | {
58 | return $this->name;
59 | }
60 |
61 | /**
62 | * Retrieve params iterator
63 | * @return Traversable|Param[]
64 | */
65 | public function getParams() : Traversable
66 | {
67 | return $this->params->getIterator();
68 | }
69 |
70 | /**
71 | * @return string
72 | */
73 | public function __toString() : string
74 | {
75 | return sprintf("{$this->name} %s;", implode(' ', (array)$this->params->getIterator()));
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Command/RemoveServerCommand.php:
--------------------------------------------------------------------------------
1 |
23 | */
24 | class RemoveServerCommand extends BaseCommand
25 | {
26 | protected function configure()
27 | {
28 | parent::configure();
29 | $this->setName('server:remove');
30 | $this->setDescription("Removes server context and it's configuration");
31 | $this->addArgument('name', InputArgument::OPTIONAL, 'Server hostname:port', 'localhost:80');
32 | }
33 |
34 | protected function execute(InputInterface $input, OutputInterface $output)
35 | {
36 | $filename = $input->getOption('file');
37 | $builder = $this->getConfig($input);
38 |
39 | list($name, $port) = explode(':', $input->getArgument('name') . ':80');
40 |
41 | $listenIPv4 = new Directive('listen', [new Param($port)]);
42 | $listenIPv6 = new Directive('listen', [new Param("[::]:{$port}"), new Param('default'), new Param('ipv6only=on')]);
43 |
44 | $server = new Server([$listenIPv4, $listenIPv6]);
45 | if ($name != 'localhost' && !empty($name)) {
46 | $server->append(new Directive('server_name', [new Param($name)]));
47 | }
48 |
49 | $builder->appendServerNode($server);
50 | $builder->dumpFile($filename);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/examples/build.php:
--------------------------------------------------------------------------------
1 | addServerNode(80);
14 | $server->append(new Directive('error_log', [new Param('/var/log/nginx/error.log'), new Param('debug')]));
15 | $server->append(new Location(new Param('/test'), null, [
16 | new Directive('error_page', [new Param('401'), new Param('@unauthorized')]),
17 | new Directive('set', [new Param('$auth_user'), new Literal('none')]),
18 | new Directive('auth_request', [new Param('/auth')]),
19 | new Directive('proxy_pass', [new Param('http://test-service')]),
20 | ]));
21 | $server->append(new Location(new Param('/auth'), null, [
22 | new Directive('proxy_pass', [new Param('http://auth-service:9999')]),
23 | new Directive('proxy_bind', [new Param('$server_addr')]),
24 | new Directive('proxy_redirect', [new Param('http://$host'), new Param('https://$host')]),
25 | new Directive('proxy_set_header', [new Param('Content-Length'), new Literal("")]),
26 | new Directive('proxy_pass_request_body', [new Param('off')]),
27 | ]));
28 | $server->append(new Location(new Param('@unauthorized'), null, [
29 | new Directive('return', [new Param('302'), new Param('/login?backurl=$request_uri')]),
30 | ]));
31 | $server->append(new Location(new Param('/login'), null, [
32 | new Directive('expires', [new Param('-1')]),
33 | new Directive('proxy_pass', [new Param('http://identity-provider-service')]),
34 | new Directive('proxy_bind', [new Param('$server_addr')]),
35 | new Directive('proxy_redirect', [new Param('http://$host'), new Param('https://$host')]),
36 | new Directive('proxy_set_header', [new Param('Content-Length'), new Literal("")]),
37 | new Directive('proxy_pass_request_body', [new Param('off')]),
38 | ]));
39 |
40 | print($builder->dump());
--------------------------------------------------------------------------------
/src/Command/AddServerCommand.php:
--------------------------------------------------------------------------------
1 |
23 | */
24 | class AddServerCommand extends BaseCommand
25 | {
26 | protected function configure()
27 | {
28 | parent::configure();
29 | $this->setName('server:add');
30 | $this->setDescription("Adds server context and configuration of port and name");
31 | $this->addArgument('name', InputArgument::OPTIONAL, 'Server hostname:port', 'localhost:80');
32 | }
33 |
34 | protected function execute(InputInterface $input, OutputInterface $output)
35 | {
36 | $filename = $input->getOption('file');
37 | $config = $this->getConfig($input);
38 |
39 | list($name, $port) = explode(':', $input->getArgument('name') . ':80');
40 |
41 | $listenIPv4 = new Directive('listen', [new Param($port)]);
42 | $listenIPv6 = new Directive('listen', [new Param("[::]:{$port}"), new Param('default'), new Param('ipv6only=on')]);
43 |
44 | // TODO: Find server by name
45 | $server = new Server([$listenIPv4, $listenIPv6]);
46 | if ($name != 'localhost' && !empty($name)) {
47 | $server->append(new Directive('server_name', [new Param($name)]));
48 | }
49 | $config->append($server);
50 |
51 | $builder = new Builder();
52 | $builder->append($server);
53 | $builder->dumpFile($filename);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Builder.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | class Builder implements Countable
22 | {
23 | /**
24 | * @var RootNode Holds configuration root node
25 | */
26 | protected $rootNode;
27 |
28 | /**
29 | * Builder constructor.
30 | */
31 | public function __construct()
32 | {
33 | $this->clear();
34 | }
35 |
36 | /**
37 | * Clears builder root node
38 | */
39 | public function clear()
40 | {
41 | $this->rootNode = new RootNode();
42 | }
43 |
44 | /**
45 | * Append child node
46 | * @param Node $node
47 | * @return Node
48 | */
49 | public function append(Node $node) : Node
50 | {
51 | $this->rootNode->append($node);
52 |
53 | return $node;
54 | }
55 |
56 | /**
57 | * Remove child node
58 | * @param Node $node
59 | * @return bool
60 | */
61 | public function remove(Node $node) : bool
62 | {
63 | return $this->rootNode->remove($node);
64 | }
65 |
66 | /**
67 | * Search for specified nodes
68 | * @param callable $checker
69 | * @return CustomTypedCollection
70 | */
71 | public function search(callable $checker) : CustomTypedCollection
72 | {
73 | return $this->rootNode->filter($checker);
74 | }
75 |
76 | /**
77 | * Count elements of an object
78 | * @link http://php.net/manual/en/countable.count.php
79 | * @return int The custom count as an integer.
80 | */
81 | public function count()
82 | {
83 | return count($this->rootNode);
84 | }
85 |
86 | /**
87 | * Retrieve an external iterator
88 | * @link http://php.net/manual/en/iteratoraggregate.getiterator.php
89 | * @return Traversable An instance of an object implementing Iterator or
90 | */
91 | public function getIterator()
92 | {
93 | return $this->rootNode->getIterator();
94 | }
95 |
96 | /**
97 | * @return string
98 | */
99 | public function dump() : string
100 | {
101 | return (string)$this->rootNode;
102 | }
103 |
104 | /**
105 | * @param string $filename
106 | * @return bool
107 | */
108 | public function dumpFile(string $filename) : bool
109 | {
110 | return file_put_contents($filename, $this->dump());
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/Command/AddLocationCommand.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | class AddLocationCommand extends BaseCommand
21 | {
22 | protected function configure()
23 | {
24 | parent::configure();
25 | $this->setName('location:add');
26 | $this->setDescription("Adds location context and configuration");
27 | $this->addArgument('name', InputArgument::OPTIONAL, 'Server hostname:port', 'localhost:80');
28 | $this->addOption('internal', null, InputOption::VALUE_NONE, 'Adds internal directive');
29 | $this->addOption(
30 | 'proxy_pass',
31 | null,
32 | InputOption::VALUE_OPTIONAL,
33 | 'Adds proxy_pass url (eg. http://proxy/)'
34 | );
35 | $this->addOption(
36 | 'proxy_bind',
37 | null,
38 | InputOption::VALUE_OPTIONAL,
39 | 'Adds proxy_bind directive url or variable (eg. $server_addr)'
40 | );
41 | $this->addOption(
42 | 'proxy_redirect',
43 | null,
44 | InputOption::VALUE_OPTIONAL ^ InputOption::VALUE_IS_ARRAY,
45 | 'Adds proxy_redirect directive (eg. http://$host or https://$host)'
46 | );
47 | $this->addOption(
48 | 'proxy_set_header',
49 | null,
50 | InputOption::VALUE_OPTIONAL ^ InputOption::VALUE_IS_ARRAY,
51 | 'Adds proxy_set_header directive (eg. "Content-Type: text/html"'
52 | );
53 | $this->addOption(
54 | 'proxy_pass_request_body',
55 | null,
56 | InputOption::VALUE_OPTIONAL,
57 | 'Adds proxy_pass_requeest_body directive (on|off)',
58 | 'on'
59 | );
60 |
61 | // // new Directive('internal'),
62 | // new Directive('expires', [new Param('-1')]),
63 | // new Directive('proxy_pass', [new Param('http://172.17.0.1:7777')]),
64 | // new Directive('proxy_bind', [new Param('$server_addr')]),
65 | // new Directive('proxy_redirect', [new Param('http://$host'), new Param('https://$host')]),
66 | // new Directive('proxy_set_header', [new Param('Content-Length'), new Literal("")]),
67 | // new Directive('proxy_pass_request_body', [new Param('off')]),
68 | }
69 |
70 | protected function execute(InputInterface $input, OutputInterface $output)
71 | {
72 |
73 | $proxy_pass = $input->getOption('proxy_pass');
74 |
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Node/Node.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | abstract class Node implements Countable, IteratorAggregate
21 | {
22 | /**
23 | * Holds parent node
24 | * @var Node
25 | */
26 | protected $parent;
27 | /**
28 | * Holds node name
29 | * @var string
30 | */
31 | protected $name = '';
32 | /**
33 | * Holds node children
34 | * @var CustomTypedCollection
35 | */
36 | protected $childNodes;
37 |
38 | /**
39 | * Node constructor.
40 | * @param string $name
41 | */
42 | public function __construct(string $name)
43 | {
44 | $this->name = $name;
45 | $this->childNodes = new class extends CustomTypedCollection {
46 | /**
47 | * Retrieves collection type
48 | * @return string
49 | */
50 | protected function getType() : string
51 | {
52 | return Node::class;
53 | }
54 | };
55 | }
56 |
57 | /**
58 | * Append new child node
59 | * @param Node $node
60 | * @return bool
61 | */
62 | public function append(Node $node) : bool
63 | {
64 | $node->parent = $this;
65 |
66 | return $this->childNodes->add($node);
67 | }
68 |
69 | /**
70 | * Remove child node
71 | * @param Node $node
72 | * @return bool
73 | */
74 | public function remove(Node $node) : bool
75 | {
76 | return $this->childNodes->remove($node);
77 | }
78 |
79 | /**
80 | * Search for specified nodes
81 | * @param callable $checker
82 | * @return CustomTypedCollection
83 | */
84 | public function search(callable $checker) : CustomTypedCollection
85 | {
86 | return $this->childNodes->filter($checker);
87 | }
88 |
89 | /**
90 | * Count elements of an object
91 | * @link http://php.net/manual/en/countable.count.php
92 | * @return int The custom count as an integer.
93 | *
94 | *
95 | * The return protocol is cast to an integer.
96 | * @since 5.1.0
97 | */
98 | public function count()
99 | {
100 | return count($this->childNodes);
101 | }
102 |
103 | /**
104 | * Retrieve an external iterator
105 | * @link http://php.net/manual/en/iteratoraggregate.getiterator.php
106 | * @return Traversable An instance of an object implementing Iterator or
107 | * Traversable
108 | * @since 5.0.0
109 | */
110 | public function getIterator()
111 | {
112 | return $this->childNodes->getIterator();
113 | }
114 |
115 | /**
116 | * @return string
117 | */
118 | public function __toString() : string
119 | {
120 | return (string)implode("\n", (array)$this->childNodes->getIterator());
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/Parser.php:
--------------------------------------------------------------------------------
1 |
35 | */
36 | class Parser extends Grammar
37 | {
38 | /**
39 | * Holds parsed filename
40 | * @var string
41 | */
42 | protected $filename;
43 | /**
44 | * Holds parsed string
45 | * @var string
46 | */
47 | protected $content;
48 |
49 | /**
50 | * Parser constructor.
51 | */
52 | public function __construct()
53 | {
54 | parent::__construct('syntax', [
55 | 'syntax' => new GreedyStarParser(new LazyAltParser(['directive', 'section'])),
56 | 'sections' => new GreedyMultiParser('section', 0, 2),
57 | 'section' => new ConcParser(
58 | [
59 | 'section-name',
60 | new LazyAltParser(['space', 'opt-space']),
61 | new LazyAltParser(['params', new LazyAltParser(['space', 'opt-space'])]),
62 | new StringParser('{'),
63 | new LazyAltParser(['space', 'opt-space']),
64 | new GreedyMultiParser(new LazyAltParser(['directive', 'section']), 0, null),
65 | new LazyAltParser(['space', 'opt-space']),
66 | new StringParser('}'),
67 | new LazyAltParser(['space', 'opt-space']),
68 | ],
69 | [$this, 'parseSection']
70 | ),
71 | 'section-name' => new RegexParser('/^[a-z0-9\_]+/i'),
72 |
73 | 'directives' => new GreedyMultiParser('directive', 0, null),
74 | 'directive' => new LazyAltParser([
75 | new ConcParser([
76 | 'directive-name',
77 | 'semicolon',
78 | new LazyAltParser(['space', 'opt-space']),
79 | ], [$this, 'parseDirective']),
80 | new ConcParser([
81 | 'directive-name',
82 | 'space',
83 | 'params',
84 | 'semicolon',
85 | new LazyAltParser(['space', 'opt-space']),
86 | ], [$this, 'parseDirective'])
87 | ]),
88 | 'directive-name' => new RegexParser('/^[a-z0-9\_]+/i'),
89 |
90 | 'params' => new GreedyMultiParser(new ConcParser(['param', 'opt-space'], function ($param, $space) {
91 | return $param;
92 | }), 0, null),
93 | 'param' => new LazyAltParser(['literal', 'param-name']),
94 | 'param-name' => new RegexParser('/^[^\s\r\n\{\}\;\"\']+/i', function ($match) {
95 | return new Param($match);
96 | }),
97 | 'literal' => new LazyAltParser([
98 | new RegexParser('/^"([^"]*)"/', function ($match0, $match1) {
99 | return new Literal($match1);
100 | }),
101 | new RegexParser("/^'([^']*)'/", function ($match0, $match1) {
102 | return new Literal($match1);
103 | })
104 | ]),
105 |
106 | 'semicolon' => new StringParser(';', function () {
107 | return null;
108 | }),
109 | 'space' => new GreedyStarParser('whitespace/comment', function () {
110 | return null;
111 | }),
112 | 'whitespace/comment' => new LazyAltParser(['whitespace', 'comment'], function () {
113 | return null;
114 | }),
115 | 'comment' => new RegexParser("/^#+([^\r\n]*)/", function () {
116 | return null;
117 | }),
118 | 'whitespace' => new RegexParser("/^[ \t\r\n]+/"),
119 | 'opt-space' => new RegexParser("/^[ \t\r\n]?/"),
120 | 'eol' => new LazyAltParser([new StringParser("\r"), new StringParser("\n")], function () {
121 | return null;
122 | })
123 | ], function (array $nodes = []) {
124 | return new RootNode($nodes);
125 | });
126 | }
127 |
128 | /**
129 | * Parses config file
130 | * @param string $filename
131 | * @return mixed
132 | * @throws ParseFailureException
133 | */
134 | public function parseFile(string $filename) : RootNode
135 | {
136 | $this->content = null;
137 | $this->filename = $filename;
138 |
139 | return $this->parse(file_get_contents($filename));
140 | }
141 |
142 | /**
143 | * Parses string
144 | * @param string $string
145 | * @return mixed
146 | * @throws ParseFailureException
147 | */
148 | public function parse($string) : RootNode
149 | {
150 | $this->content = $string;
151 | $this->filename = null;
152 |
153 | return parent::parse($string);
154 | }
155 |
156 | /**
157 | * Parses section entries
158 | * @param string $section Section name
159 | * @param null $space0 Ignored
160 | * @param Param[] $params Params collection
161 | * @param null $open Ignored
162 | * @param null $space1 Ignored
163 | * @param Directive[] $directives Directives collection
164 | * @return Context
165 | * @throws GrammarException
166 | * @throws UnrecognizedContextException
167 | */
168 | protected function parseSection($section, $space0, $params, $open, $space1, $directives) : Context
169 | {
170 | switch ($section) {
171 | case 'server':
172 | return new Server($directives);
173 |
174 | case 'http':
175 | return new Http($directives);
176 |
177 | case 'location':
178 | $modifier = null;
179 | if (sizeof($params) == 2) {
180 | list($modifier, $location) = $params;
181 | } elseif (sizeof($params) == 1) {
182 | $location = $params[0];
183 | } else {
184 | throw new GrammarException(
185 | sprintf(
186 | "Location context missing in %s",
187 | $this->filename ? var_export($this->filename, true) : var_export($this->content, true)
188 | )
189 | );
190 | }
191 | return new Location($location, $modifier, $directives);
192 |
193 | case 'events':
194 | return new Events($directives);
195 |
196 | case 'upstream':
197 | list($upstream) = $params;
198 | return new Upstream($upstream, $directives);
199 | }
200 |
201 | throw new UnrecognizedContextException(
202 | sprintf(
203 | "Unrecognized context: {$section} found in %s",
204 | $this->filename ? var_export($this->filename, true) : var_export($this->content, true)
205 | )
206 | );
207 | }
208 |
209 | /**
210 | * Parses directive
211 | * @param string $name
212 | * @param null $space
213 | * @param array $params
214 | * @return Directive
215 | */
216 | protected function parseDirective(string $name, $space = null, $params = []) : Directive
217 | {
218 | return new Directive($name, is_null($params) ? [] : $params);
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | NGINX Configurator
2 | ==================
3 |
4 | PHP Library for NGINX configuration parser/generator
5 |
6 | 
7 | [](https://travis-ci.org/madkom/nginx-configurator)
8 | [](https://packagist.org/packages/madkom/nginx-configurator)
9 | [](https://packagist.org/packages/madkom/nginx-configurator)
10 | [](https://packagist.org/packages/madkom/nginx-configurator)
11 | [](https://coveralls.io/github/madkom/nginx-configurator?branch=master)
12 | [](https://codeclimate.com/github/madkom/nginx-configurator)
13 | [](https://codeclimate.com/github/madkom/nginx-configurator)
14 |
15 | ---
16 |
17 | ## Features
18 |
19 | This library can parse and generate NGINX configuration files.
20 | In near future will provide CLI commands for NGINX configuration.
21 |
22 |
23 | ## Installation
24 |
25 | Install with Composer
26 |
27 | ```
28 | composer require madkom/nginx-configurator
29 | ```
30 |
31 | ## Requirements
32 |
33 | This library requires *PHP* in `~7` version.
34 |
35 | ## Usage
36 |
37 | Parsing configuration string:
38 |
39 | ```php
40 | use Madkom\NginxConfigurator\Builder;
41 | use Madkom\NginxConfigurator\Config\Server;
42 | use Madkom\NginxConfigurator\Parser;
43 |
44 | require 'vendor/autoload.php';
45 |
46 | $config = <<parse($config);
76 | /** @var Server $defaultServers[] */
77 | $defaultServers = $defaultConfig->search(function (Node $node) {
78 | return $node instanceof Server;
79 | });
80 |
81 |
82 | $builder = new Builder();
83 | if (count($defaultServers) > 0) {
84 | /** @var Server $defaultServer */
85 | foreach ($defaultServers as $defaultServer) {
86 | $builder->append($defaultServer);
87 | }
88 | }
89 | ```
90 |
91 | Generating configuration string:
92 |
93 | ```php
94 | use Madkom\NginxConfigurator\Factory;
95 | use Madkom\NginxConfigurator\Builder;
96 | use Madkom\NginxConfigurator\Config\Location;
97 | use Madkom\NginxConfigurator\Node\Directive;
98 | use Madkom\NginxConfigurator\Node\Literal;
99 | use Madkom\NginxConfigurator\Node\Param;
100 |
101 | require __DIR__ . '/../vendor/autoload.php';
102 |
103 | $factory = new Factory()
104 | $builder = new Builder();
105 |
106 | $server = $builder->append($factory->createServer(80));
107 | $server->append(new Directive('error_log', [
108 | new Param('/var/log/nginx/error.log'),
109 | new Param('debug'),
110 | ]));
111 | $server->append(new Location(new Param('/test'), null, [
112 | new Directive('error_page', [new Param('401'), new Param('@unauthorized')]),
113 | new Directive('set', [new Param('$auth_user'), new Literal('none')]),
114 | new Directive('auth_request', [new Param('/auth')]),
115 | new Directive('proxy_pass', [new Param('http://test-service')]),
116 | ]));
117 | $server->append(new Location(new Param('/auth'), null, [
118 | new Directive('proxy_pass', [new Param('http://auth-service:9999')]),
119 | new Directive('proxy_bind', [new Param('$server_addr')]),
120 | new Directive('proxy_redirect', [new Param('http://$host'), new Param('https://$host')]),
121 | new Directive('proxy_set_header', [new Param('Content-Length'), new Literal("")]),
122 | new Directive('proxy_pass_request_body', [new Param('off')]),
123 | ]));
124 | $server->append(new Location(new Param('@unauthorized'), null, [
125 | new Directive('return', [new Param('302'), new Param('/login?backurl=$request_uri')]),
126 | ]));
127 | $server->append(new Location(new Param('/login'), null, [
128 | new Directive('expires', [new Param('-1')]),
129 | new Directive('proxy_pass', [new Param('http://identity-provider-service')]),
130 | new Directive('proxy_bind', [new Param('$server_addr')]),
131 | new Directive('proxy_redirect', [new Param('http://$host'), new Param('https://$host')]),
132 | new Directive('proxy_set_header', [new Param('Content-Length'), new Literal("")]),
133 | new Directive('proxy_pass_request_body', [new Param('off')]),
134 | ]));
135 |
136 | print($builder->dump());
137 | ```
138 |
139 | Generated configuration output:
140 |
141 | ```
142 | server {
143 | listen 80;
144 | listen [::]:80 default ipv6only=on;
145 | error_log /var/log/nginx/error.log debug;
146 | location /test {
147 | error_page 401 @unauthorized;
148 | set $auth_user "none";
149 | auth_request /auth;
150 | proxy_pass http://test-service;
151 | }
152 |
153 | location /auth {
154 | proxy_pass http://auth-service:9999;
155 | proxy_bind $server_addr;
156 | proxy_redirect http://$host https://$host;
157 | proxy_set_header Content-Length "";
158 | proxy_pass_request_body off;
159 | }
160 |
161 | location @unauthorized {
162 | return 302 /login?backurl=$request_uri;
163 | }
164 |
165 | location /login {
166 | expires -1;
167 | proxy_pass http://identity-provider-service;
168 | proxy_bind $server_addr;
169 | proxy_redirect http://$host https://$host;
170 | proxy_set_header Content-Length "";
171 | proxy_pass_request_body off;
172 | }
173 |
174 | }
175 | ```
176 |
177 | There are also methods to read and dump file:
178 |
179 | ```php
180 | use Madkom\NginxConfigurator\Builder;
181 | use Madkom\NginxConfigurator\Config\Location;
182 | use Madkom\NginxConfigurator\Config\Server;
183 | use Madkom\NginxConfigurator\Node\Directive;
184 | use Madkom\NginxConfigurator\Node\Literal;
185 | use Madkom\NginxConfigurator\Parser;
186 |
187 | require __DIR__ . '/../vendor/autoload.php';
188 |
189 | $parser = new Parser();
190 | $builder = new Builder();
191 |
192 | $configuration = $parser->parseFile('default.conf');
193 |
194 | /** @var Server $servers[] */
195 | $servers = $configuration->search(function (Node $node) {
196 | return $node instanceof Server;
197 | });
198 | if (count($servers) > 0) {
199 | /** @var Server $server */
200 | foreach ($servers as $server) {
201 | $builder->append($server);
202 | }
203 | }
204 |
205 | $builder->dumpFile('generated.conf');
206 | ```
207 |
208 | ## TODO
209 |
210 | * [ ] Implement comments parsing
211 | * [ ] Implement commands for config manipulation from CLI
212 |
213 | ## License
214 |
215 | The MIT License (MIT)
216 |
217 | Copyright (c) 2016 Madkom S.A.
218 |
219 | Permission is hereby granted, free of charge, to any person obtaining a copy
220 | of this software and associated documentation files (the "Software"), to deal
221 | in the Software without restriction, including without limitation the rights
222 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
223 | copies of the Software, and to permit persons to whom the Software is
224 | furnished to do so, subject to the following conditions:
225 |
226 | The above copyright notice and this permission notice shall be included in
227 | all copies or substantial portions of the Software.
228 |
229 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
230 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
231 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
232 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
233 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
234 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
235 | THE SOFTWARE.
--------------------------------------------------------------------------------
/tests/spec/ParserSpec.php:
--------------------------------------------------------------------------------
1 |
26 | * @mixin Parser
27 | */
28 | class ParserSpec extends ObjectBehavior
29 | {
30 | function it_is_initializable()
31 | {
32 | $this->shouldHaveType(Parser::class);
33 | }
34 |
35 | /**
36 | * @throws ParseFailureException
37 | */
38 | function it_can_parse_directive()
39 | {
40 | /** @var RootNode $root */
41 | $root = $this->parse(<<shouldReturnAnInstanceOf(RootNode::class);
46 |
47 | $directives = $root->search(function (Node $node) {
48 | return $node;
49 | })->getWrappedObject();
50 | /** @var Directive $directive */
51 | foreach ($directives as $directive) {
52 | break;
53 | }
54 | Assert::assertEquals($directive->getName(), 'internal');
55 | Assert::assertInstanceOf(Traversable::class, $directive->getParams());
56 | }
57 |
58 | function it_can_parse_multiple_directives_with_params()
59 | {
60 | $root = $this->parse(<<shouldReturnAnInstanceOf(RootNode::class);
69 |
70 | $directives = $root->search(function (Node $node) {
71 | return $node;
72 | })->getWrappedObject();
73 | /** @var Directive $directive */
74 | foreach ($directives as $index => $directive) {
75 | switch ($index) {
76 | case 0:
77 | Assert::assertEquals($directive->getName(), 'internal');
78 | Assert::assertInstanceOf(Traversable::class, $directive->getParams());
79 | break;
80 | case 1:
81 | Assert::assertEquals($directive->getName(), 'sendfile');
82 | Assert::assertInstanceOf(Traversable::class, $directive->getParams());
83 | break;
84 | case 2:
85 | Assert::assertEquals($directive->getName(), 'set');
86 | Assert::assertInstanceOf(Traversable::class, $directive->getParams());
87 | break;
88 | }
89 | }
90 | }
91 |
92 | function it_can_parse_multiple_directives_with_params_in_context()
93 | {
94 | $root = $this->parse(<<shouldReturnAnInstanceOf(RootNode::class);
105 |
106 | $contexts = $root->search(function (Node $node) {
107 | return $node;
108 | })->getWrappedObject();
109 | /** @var Context $context */
110 | foreach ($contexts as $index => $context) {
111 | switch ($index) {
112 | case 0:
113 | Assert::assertInstanceOf(Server::class, $context);
114 | /** @var Directive $directive */
115 | foreach ($context as $index => $directive) {
116 | switch ($index) {
117 | case 0:
118 | Assert::assertEquals('internal', $directive->getName());
119 | break;
120 | case 1:
121 | Assert::assertEquals('sendfile', $directive->getName());
122 | break;
123 | case 2:
124 | Assert::assertEquals('set', $directive->getName());
125 | }
126 | }
127 | break;
128 | }
129 | }
130 | }
131 |
132 | function it_can_parse_multiple_directives_with_params_in_multiple_contexts()
133 | {
134 | $root = $this->parse(<<shouldReturnAnInstanceOf(RootNode::class);
155 |
156 | $contexts = $root->search(function (Node $node) {
157 | return $node;
158 | })->getWrappedObject();
159 | /** @var Context $context */
160 | foreach ($contexts as $index => $context) {
161 | switch ($index) {
162 | case 0:
163 | Assert::assertInstanceOf(Server::class, $context);
164 | /** @var Directive $directive */
165 | foreach ($context as $index => $directive) {
166 | switch ($index) {
167 | case 0:
168 | Assert::assertEquals('internal', $directive->getName());
169 | break;
170 | case 1:
171 | Assert::assertEquals('sendfile', $directive->getName());
172 | break;
173 | case 2:
174 | Assert::assertEquals('set', $directive->getName());
175 | break;
176 | case 3:
177 | Assert::assertInstanceOf(Location::class, $directive);
178 | break;
179 | }
180 | }
181 | break;
182 |
183 | case 1:
184 | Assert::assertInstanceOf(Directive::class, $context);
185 | Assert::assertEquals('sendfile', $context->getName());
186 | break;
187 |
188 | }
189 | }
190 | }
191 |
192 | function it_can_parse_Upstream_and_Http_contexts()
193 | {
194 | $root = $this->parse(<<shouldReturnAnInstanceOf(RootNode::class);
203 |
204 | $contexts = $root->search(function (Node $node) {
205 | return $node;
206 | })->getWrappedObject();
207 | /** @var Context $context */
208 | foreach ($contexts as $index => $context) {
209 | switch ($index) {
210 | case 0:
211 | Assert::assertInstanceOf(Http::class, $context);
212 | break;
213 | case 1:
214 | Assert::assertInstanceOf(Upstream::class, $context);
215 | /** @var Directive $directive */
216 | foreach ($context as $index => $directive) {
217 | switch ($index) {
218 | case 0:
219 | Assert::assertEquals('internal', $directive->getName());
220 | break;
221 | }
222 | }
223 | break;
224 | }
225 | }
226 | }
227 |
228 | /**
229 | * @throws ParseFailureException
230 | */
231 | function it_can_parse_Literal_directive()
232 | {
233 | /** @var RootNode $root */
234 | $root = $this->parse(<<shouldReturnAnInstanceOf(RootNode::class);
239 |
240 | $directives = $root->search(function (Node $node) {
241 | return $node;
242 | })->getWrappedObject();
243 | /** @var Directive $directive */
244 | foreach ($directives as $directive) {
245 | break;
246 | }
247 | Assert::assertEquals($directive->getName(), 'internal');
248 | Assert::assertInstanceOf(Traversable::class, $directive->getParams());
249 | /** @var Param $param */
250 | foreach ($directive->getParams() as $param) {
251 | Assert::assertInstanceOf(Literal::class, $param);
252 | }
253 | }
254 | }
255 |
--------------------------------------------------------------------------------