CornyPhoenix/fipa-sl

View on GitHub
src/CornyPhoenix/Fipa/Sl/Context/DefaultTupleContext.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace CornyPhoenix\Fipa\Sl\Context;

/**
 * @package CornyPhoenix\Fipa\Sl\Context
 * @author moellers
 */
class DefaultTupleContext implements TupleContext
{

    const OPENING_DELIMITER = '(';
    const CLOSING_DELIMITER = ')';
    const KEY_DELIMITER = ':';
    const TERM_SEPARATOR = ' ';
    const KEY_SEPARATOR = ' ';
    const FRAME_SEPARATOR = ' ';
    const STRING_DELIMITER = '"';
    const FRAME_REG_EX = '[\w-]';

    private static $instance = null;

    private $opening;
    private $closing;
    private $string;

    /**
     * DefaultTupleContext constructor.
     */
    public function __construct()
    {
        $this->opening = preg_quote($this->getOpeningDelimiter());
        $this->closing = preg_quote($this->getClosingDelimiter());
        $this->string = preg_quote($this->getStringDelimiter());
    }

    /**
     * @return DefaultTupleContext
     */
    public static function getInstance()
    {
        if (null === self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * @return string
     */
    public function getOpeningDelimiter()
    {
        return self::OPENING_DELIMITER;
    }

    /**
     * @return string
     */
    public function getClosingDelimiter()
    {
        return self::CLOSING_DELIMITER;
    }

    /**
     * @return string
     */
    public function getKeyDelimiter()
    {
        return self::KEY_DELIMITER;
    }

    /**
     * @return string
     */
    public function getTermSeparator()
    {
        return self::TERM_SEPARATOR;
    }

    /**
     * @return string
     */
    public function getFrameSeparator()
    {
        return self::FRAME_SEPARATOR;
    }

    /**
     * @return string
     */
    public function getKeySeparator()
    {
        return self::KEY_SEPARATOR;
    }

    /**
     * @return string
     */
    public function getStringDelimiter()
    {
        return self::STRING_DELIMITER;
    }

    /**
     * @return string
     */
    public function getOpeningDelimiterRegEx()
    {
        return $this->opening;
    }

    /**
     * @return string
     */
    public function getClosingDelimiterRegEx()
    {
        return $this->closing;
    }

    /**
     * @return string
     */
    public function getFrameRegEx()
    {
        return self::FRAME_REG_EX;
    }

    /**
     * Encodes a string to an SL string.
     *
     * @param string $string
     * @return string
     */
    public function encode($string)
    {
        if (!$string) {
            return '';
        }

        $lower = strtolower($string);
        if ($lower === 'null' || $lower === 'false' || $lower === 'true') {
            return sprintf('%2$s%1$s%2$s', $string, $this->getStringDelimiter());
        }

        $containsWhitespace = boolval(preg_match('/\s/', $string));

        // Escape special chars.
        $pattern = sprintf('/[%s%s%s]/', $this->opening, $this->closing, $this->string);
        $string = preg_replace($pattern, '\\\\$0', $string);

        // Wrap with string delimiter if needed.
        if ($containsWhitespace) {
            return $this->getStringDelimiter() . $string . $this->getStringDelimiter();
        }

        return $string;
    }

    /**
     * Decodes an SL string.
     *
     * @param string $string
     * @return string
     */
    public function decode($string)
    {
        if (!$string) {
            return '';
        }

        $len = strlen($string);
        if ($string[0] === $this->getStringDelimiter() && $string[$len - 1] === $this->getStringDelimiter()) {
            $string = substr($string, 1, $len - 2);
        }

        // Unescape special chars.
        $pattern = sprintf('/\\\\([%s%s%s])/', $this->opening, $this->closing, $this->string);
        $string = preg_replace($pattern, '$1', $string);

        return $string;
    }

    /**
     * @param string $string
     * @param int $offset
     * @return int
     */
    public function findEndOfString($string, $offset)
    {
        $quoted = $string[$offset] === $this->getStringDelimiter();

        if ($quoted) {
            $offset++;
        }

        for (; $offset < strlen($string); $offset++) {
            $c = $string[$offset];

            // Skip next char if found escape char.
            if ($c === '\\') {
                $offset++;
                continue;
            }

            if ($quoted) {
                // Break on ending delimiter if quoted.
                if ($c === $this->getStringDelimiter()) {
                    return $offset + 1;
                }
            } else {
                // Break on parenthesis if not quoted.
                if (preg_match(sprintf('/[%s%s]/', $this->opening, $this->closing), $c)) {
                    break;
                }

                // Break on whitespace if not quoted.
                if (trim($c) === '') {
                    break;
                }
            }
        }

        return $offset;
    }
}