romanpitak/Nginx-Config-Processor

View on GitHub
src/Directive.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

/**
 * This file is part of the Nginx Config Processor package.
 *
 * (c) Roman Piták <roman@pitak.net>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

namespace RomanPitak\Nginx\Config;

class Directive extends Printable
{
    /** @var string $name */
    private $name;

    /** @var string $value */
    private $value;

    /** @var Scope $childScope */
    private $childScope = null;

    /** @var Scope $parentScope */
    private $parentScope = null;

    /** @var Comment $comment */
    private $comment = null;

    /**
     * @param string $name
     * @param string $value
     * @param Scope $childScope
     * @param Scope $parentScope
     * @param Comment $comment
     */
    public function __construct(
        $name,
        $value = null,
        Scope $childScope = null,
        Scope $parentScope = null,
        Comment $comment = null
    ) {
        $this->name = $name;
        $this->value = $value;
        if (!is_null($childScope)) {
            $this->setChildScope($childScope);
        }
        if (!is_null($parentScope)) {
            $this->setParentScope($parentScope);
        }
        if (!is_null($comment)) {
            $this->setComment($comment);
        }
    }

    /*
     * ========== Factories ==========
     */

    /**
     * Provides fluid interface.
     *
     * @param $name
     * @param null $value
     * @param Scope $childScope
     * @param Scope $parentScope
     * @param Comment $comment
     * @return Directive
     */
    public static function create(
        $name,
        $value = null,
        Scope $childScope = null,
        Scope $parentScope = null,
        Comment $comment = null
    ) {
        return new self($name, $value, $childScope, $parentScope, $comment);
    }

    /**
     * @param \RomanPitak\Nginx\Config\Text $configString
     * @return self
     * @throws Exception
     */
    public static function fromString(Text $configString)
    {
        $text = '';
        while (false === $configString->eof()) {
            $char = $configString->getChar();
            if ('{' === $char) {
                return self::newDirectiveWithScope($text, $configString);
            }
            if (';' === $char) {
                return self::newDirectiveWithoutScope($text, $configString);
            }
            $text .= $char;
            $configString->inc();
        }
        throw new Exception('Could not create directive.');
    }

    private static function newDirectiveWithScope(
        $nameString,
        Text $scopeString
    ) {
        $scopeString->inc();
        list($name, $value) = self::processText($nameString);
        $directive = new Directive($name, $value);

        $comment = self::checkRestOfTheLineForComment($scopeString);
        if (false !== $comment) {
            $directive->setComment($comment);
        }

        $childScope = Scope::fromString($scopeString);
        $childScope->setParentDirective($directive);
        $directive->setChildScope($childScope);

        $scopeString->inc();

        $comment = self::checkRestOfTheLineForComment($scopeString);
        if (false !== $comment) {
            $directive->setComment($comment);
        }

        return $directive;
    }

    private static function newDirectiveWithoutScope(
        $nameString,
        Text $configString
    ) {
        $configString->inc();
        list($name, $value) = self::processText($nameString);
        $directive = new Directive($name, $value);

        $comment = self::checkRestOfTheLineForComment($configString);
        if (false !== $comment) {
            $directive->setComment($comment);
        }

        return $directive;
    }

    private static function checkRestOfTheLineForComment(Text $configString)
    {
        $restOfTheLine = $configString->getRestOfTheLine();
        if (1 !== preg_match('/^\s*#/', $restOfTheLine)) {
            return false;
        }

        $commentPosition = strpos($restOfTheLine, '#');
        $configString->inc($commentPosition);
        return Comment::fromString($configString);
    }

    private static function processText($text)
    {
        $result = self::checkKeyValue($text);
        if (is_array($result)) {
            return $result;
        }
        $result = self::checkKey($text);
        if (is_array($result)) {
            return $result;
        }
        throw new Exception('Text "' . $text . '" did not match pattern.');
    }

    private static function checkKeyValue($text) {
        if (1 === preg_match('#^([a-z][a-z0-9._/+-]*)\s+([^;{]+)$#', $text, $matches)) {
            return array($matches[1], rtrim($matches[2]));
        }
        return false;
    }

    private static function checkKey($text) {
        if (1 === preg_match('#^([a-z][a-z0-9._/+-]*)\s*$#', $text, $matches)) {
            return array($matches[1], null);
        }
        return false;
    }

    /*
     * ========== Getters ==========
     */

    /**
     * Get parent Scope
     *
     * @return Scope|null
     */
    public function getParentScope()
    {
        return $this->parentScope;
    }

    /**
     * Get child Scope.
     *
     * @return Scope|null
     */
    public function getChildScope()
    {
        return $this->childScope;
    }

    /**
     * Get the associated Comment for this Directive.
     *
     * @return Comment
     */
    public function getComment()
    {
        if (is_null($this->comment)) {
            $this->comment = new Comment();
        }
        return $this->comment;
    }

    /**
     * Does this Directive have a Comment associated with it?
     *
     * @return bool
     */
    public function hasComment()
    {
        return (!$this->getComment()->isEmpty());
    }

    /*
     * ========== Setters ==========
     */

    /**
     * Sets the parent Scope for this Directive.
     *
     * @param Scope $parentScope
     * @return $this
     */
    public function setParentScope(Scope $parentScope)
    {
        $this->parentScope = $parentScope;
        return $this;
    }

    /**
     * Sets the child Scope for this Directive.
     *
     * Sets the child Scope for this Directive and also
     * sets the $childScope->setParentDirective($this).
     *
     * @param Scope $childScope
     * @return $this
     */
    public function setChildScope(Scope $childScope)
    {
        $this->childScope = $childScope;

        if ($childScope->getParentDirective() !== $this) {
            $childScope->setParentDirective($this);
        }

        return $this;
    }

    /**
     * Set the associated Comment object for this Directive.
     *
     * This will overwrite the existing comment.
     *
     * @param Comment $comment
     * @return $this
     */
    public function setComment(Comment $comment)
    {
        $this->comment = $comment;
        return $this;
    }

    /**
     * Set the comment text for this Directive.
     *
     * This will overwrite the existing comment.
     *
     * @param $text
     * @return $this
     */
    public function setCommentText($text)
    {
        $this->getComment()->setText($text);
        return $this;
    }

    /*
     * ========== Printing ==========
     */

    /**
     * Pretty print with indentation.
     *
     * @param $indentLevel
     * @param int $spacesPerIndent
     * @return string
     */
    public function prettyPrint($indentLevel, $spacesPerIndent = 4)
    {
        $indent = str_repeat(str_repeat(' ', $spacesPerIndent), $indentLevel);

        $resultString = $indent . $this->name;
        if (!is_null($this->value)) {
            $resultString .= " " . $this->value;
        }

        if (is_null($this->getChildScope())) {
            $resultString .= ";";
        } else {
            $resultString .= " {";
        }

        if (false === $this->hasComment()) {
            $resultString .= "\n";
        } else {
            if (false === $this->getComment()->isMultiline()) {
                $resultString .= " " . $this->comment->prettyPrint(0, 0);
            } else {
                $comment = $this->getComment()->prettyPrint($indentLevel, $spacesPerIndent);
                $resultString = $comment . $resultString;
            }
        }

        if (!is_null($this->getChildScope())) {
            $resultString .= "" . $this->childScope->prettyPrint($indentLevel, $spacesPerIndent) . $indent . "}\n";
        }

        return $resultString;
    }
}