no message
This commit is contained in:
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "nette/neon",
|
||||
"description": "🍸 Nette NEON: encodes and decodes NEON file format.",
|
||||
"keywords": ["nette", "neon", "import", "export", "yaml"],
|
||||
"homepage": "http://ne-on.org",
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
},
|
||||
{
|
||||
"name": "Nette Community",
|
||||
"homepage": "https://nette.org/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"ext-iconv": "*",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.0",
|
||||
"tracy/tracy": "^2.3"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
How to contribute & use the issue tracker
|
||||
=========================================
|
||||
|
||||
Nette welcomes your contributions. There are several ways to help out:
|
||||
|
||||
* Create an issue on GitHub, if you have found a bug
|
||||
* Write test cases for open bug issues
|
||||
* Write fixes for open bug/feature issues, preferably with test cases included
|
||||
* Contribute to the [documentation](https://nette.org/en/writing)
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Please **do not use the issue tracker to ask questions**. We will be happy to help you
|
||||
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report.
|
||||
|
||||
**Feature requests** are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
|
||||
|
||||
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
|
||||
|
||||
Please do not fix whitespace, format code, or make a purely cosmetic patch.
|
||||
|
||||
Thanks! :heart:
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
Licenses
|
||||
========
|
||||
|
||||
Good news! You may use Nette Framework under the terms of either
|
||||
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
|
||||
|
||||
The BSD License is recommended for most projects. It is easy to understand and it
|
||||
places almost no restrictions on what you can do with the framework. If the GPL
|
||||
fits better to your project, you can use the framework under this license.
|
||||
|
||||
You don't have to notify anyone which license you are using. You can freely
|
||||
use Nette Framework in commercial projects as long as the copyright header
|
||||
remains intact.
|
||||
|
||||
Please be advised that the name "Nette Framework" is a protected trademark and its
|
||||
usage has some limitations. So please do not use word "Nette" in the name of your
|
||||
project or top-level domain, and choose a name that stands on its own merits.
|
||||
If your stuff is good, it will not take long to establish a reputation for yourselves.
|
||||
|
||||
|
||||
New BSD License
|
||||
---------------
|
||||
|
||||
Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of "Nette Framework" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall the copyright owner or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused and on
|
||||
any theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
||||
|
||||
|
||||
GNU General Public License
|
||||
--------------------------
|
||||
|
||||
GPL licenses are very very long, so instead of including them here we offer
|
||||
you URLs with full text:
|
||||
|
||||
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
|
||||
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
|
||||
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
[NEON](http://ne-on.org): Nette Object Notation
|
||||
===============================================
|
||||
|
||||
[](https://packagist.org/packages/nette/neon)
|
||||
[](https://travis-ci.org/nette/neon)
|
||||
[](https://coveralls.io/github/nette/neon?branch=master)
|
||||
[](https://github.com/nette/neon/releases)
|
||||
[](https://github.com/nette/neon/blob/master/license.md)
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
NEON is a human-readable data serialization language. It is commonly used for configuration files, but could be used in many applications where data is being stored
|
||||
|
||||
NEON is very similar to YAML.The main difference is that the NEON supports "entities" (so can be used e.g. to parse phpDoc annotations) and tab characters for indentation.
|
||||
NEON syntax is a little simpler and the parsing is faster.
|
||||
|
||||
Documentation can be found on the [website](https://doc.nette.org/neon).
|
||||
|
||||
If you like Nette, **[please make a donation now](https://nette.org/donate)**. Thank you!
|
||||
|
||||
|
||||
NEON language
|
||||
-------------
|
||||
|
||||
Try NEON [in sandbox](https://ne-on.org)!
|
||||
|
||||
Example of NEON code:
|
||||
|
||||
```
|
||||
# my web application config
|
||||
|
||||
php:
|
||||
date.timezone: Europe/Prague
|
||||
zlib.output_compression: yes # use gzip
|
||||
|
||||
database:
|
||||
driver: mysql
|
||||
username: root
|
||||
password: beruska92
|
||||
|
||||
users:
|
||||
- Dave
|
||||
- Kryten
|
||||
- Rimmer
|
||||
```
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The recommended way to install is via Composer:
|
||||
|
||||
```
|
||||
composer require nette/neon
|
||||
```
|
||||
|
||||
It requires PHP version 5.6 and supports PHP up to 7.3. The dev-master version requires PHP 7.0.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
`Nette\Neon\Neon` is a static class for encoding and decoding NEON files.
|
||||
|
||||
`Neon::encode()` returns $value encoded into NEON. Flags accepts Neon::BLOCK which formats NEON in multiline format.
|
||||
|
||||
```php
|
||||
use Nette\Neon\Neon;
|
||||
$neon = Neon::encode($value); // Returns $value encoded in NEON
|
||||
$neon = Neon::encode($value, Neon::BLOCK); // Returns formatted $value encoded in NEON
|
||||
```
|
||||
|
||||
`Neon::decode()` converts given NEON to PHP value:
|
||||
|
||||
```php
|
||||
$value = Neon::decode('hello: world'); // Returns an array ['hello' => 'world']
|
||||
```
|
||||
|
||||
Both methods throw `Nette\Neon\Exception` on error.
|
||||
|
||||
|
||||
Editors plugins
|
||||
---------------
|
||||
|
||||
- NetBeans IDE has built-in support
|
||||
- [PhpStorm](https://plugins.jetbrains.com/plugin/7060?pr)
|
||||
- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=Kasik96.latte)
|
||||
- [Emacs](https://github.com/Fuco1/neon-mode)
|
||||
|
||||
|
||||
Other languages
|
||||
---------------
|
||||
|
||||
- [Neon for Javascript](https://github.com/matej21/neon-js)
|
||||
- [Neon for Python](https://github.com/paveldedik/neon-py)
|
||||
+358
@@ -0,0 +1,358 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nette\Neon;
|
||||
|
||||
|
||||
/**
|
||||
* Parser for Nette Object Notation.
|
||||
* @internal
|
||||
*/
|
||||
final class Decoder
|
||||
{
|
||||
const PATTERNS = [
|
||||
'
|
||||
\'\'\'\n (?:(?: [^\n] | \n(?![\t\ ]*+\'\'\') )*+ \n)?[\t\ ]*+\'\'\' |
|
||||
"""\n (?:(?: [^\n] | \n(?![\t\ ]*+""") )*+ \n)?[\t\ ]*+""" |
|
||||
\'[^\'\n]*+\' |
|
||||
" (?: \\\\. | [^"\\\\\n] )*+ "
|
||||
', // string
|
||||
'
|
||||
(?: [^#"\',:=[\]{}()\x00-\x20!`-] | [:-][^"\',\]})\s] )
|
||||
(?:
|
||||
[^,:=\]})(\x00-\x20]++ |
|
||||
:(?! [\s,\]})] | $ ) |
|
||||
[\ \t]++ [^#,:=\]})(\x00-\x20]
|
||||
)*+
|
||||
', // literal / boolean / integer / float
|
||||
'
|
||||
[,:=[\]{}()-]
|
||||
', // symbol
|
||||
'?:\#.*+', // comment
|
||||
'\n[\t\ ]*+', // new line + indent
|
||||
'?:[\t\ ]++', // whitespace
|
||||
];
|
||||
|
||||
const PATTERN_DATETIME = '#\d\d\d\d-\d\d?-\d\d?(?:(?:[Tt]| ++)\d\d?:\d\d:\d\d(?:\.\d*+)? *+(?:Z|[-+]\d\d?(?::?\d\d)?)?)?\z#A';
|
||||
|
||||
const PATTERN_HEX = '#0x[0-9a-fA-F]++\z#A';
|
||||
|
||||
const PATTERN_OCTAL = '#0o[0-7]++\z#A';
|
||||
|
||||
const PATTERN_BINARY = '#0b[0-1]++\z#A';
|
||||
|
||||
const SIMPLE_TYPES = [
|
||||
'true' => 'TRUE', 'True' => 'TRUE', 'TRUE' => 'TRUE', 'yes' => 'TRUE', 'Yes' => 'TRUE', 'YES' => 'TRUE', 'on' => 'TRUE', 'On' => 'TRUE', 'ON' => 'TRUE',
|
||||
'false' => 'FALSE', 'False' => 'FALSE', 'FALSE' => 'FALSE', 'no' => 'FALSE', 'No' => 'FALSE', 'NO' => 'FALSE', 'off' => 'FALSE', 'Off' => 'FALSE', 'OFF' => 'FALSE',
|
||||
'null' => 'NULL', 'Null' => 'NULL', 'NULL' => 'NULL',
|
||||
];
|
||||
|
||||
const ESCAPE_SEQUENCES = [
|
||||
't' => "\t", 'n' => "\n", 'r' => "\r", 'f' => "\x0C", 'b' => "\x08", '"' => '"', '\\' => '\\', '/' => '/', '_' => "\u{A0}",
|
||||
];
|
||||
|
||||
const BRACKETS = [
|
||||
'[' => ']',
|
||||
'{' => '}',
|
||||
'(' => ')',
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
private $input;
|
||||
|
||||
/** @var array */
|
||||
private $tokens;
|
||||
|
||||
/** @var int */
|
||||
private $pos;
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a NEON string.
|
||||
* @return mixed
|
||||
*/
|
||||
public function decode(string $input)
|
||||
{
|
||||
if (!is_string($input)) {
|
||||
throw new \InvalidArgumentException(sprintf('Argument must be a string, %s given.', gettype($input)));
|
||||
|
||||
} elseif (substr($input, 0, 3) === "\u{FEFF}") { // BOM
|
||||
$input = substr($input, 3);
|
||||
}
|
||||
$this->input = "\n" . str_replace("\r", '', $input); // \n forces indent detection
|
||||
|
||||
$pattern = '~(' . implode(')|(', self::PATTERNS) . ')~Amix';
|
||||
$this->tokens = preg_split($pattern, $this->input, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE | PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
$last = end($this->tokens);
|
||||
if ($this->tokens && !preg_match($pattern, $last[0])) {
|
||||
$this->pos = count($this->tokens) - 1;
|
||||
$this->error();
|
||||
}
|
||||
|
||||
$this->pos = 0;
|
||||
$res = $this->parse(null);
|
||||
|
||||
while (isset($this->tokens[$this->pos])) {
|
||||
if ($this->tokens[$this->pos][0][0] === "\n") {
|
||||
$this->pos++;
|
||||
} else {
|
||||
$this->error();
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|bool|null $indent indentation (for block-parser)
|
||||
* @return mixed
|
||||
*/
|
||||
private function parse($indent, array $result = null, $key = null, bool $hasKey = false)
|
||||
{
|
||||
$inlineParser = $indent === false;
|
||||
$value = null;
|
||||
$hasValue = false;
|
||||
$tokens = $this->tokens;
|
||||
$n = &$this->pos;
|
||||
$count = count($tokens);
|
||||
$mainResult = &$result;
|
||||
|
||||
for (; $n < $count; $n++) {
|
||||
$t = $tokens[$n][0];
|
||||
|
||||
if ($t === ',') { // ArrayEntry separator
|
||||
if ((!$hasKey && !$hasValue) || !$inlineParser) {
|
||||
$this->error();
|
||||
}
|
||||
$this->addValue($result, $hasKey ? $key : null, $hasValue ? $value : null);
|
||||
$hasKey = $hasValue = false;
|
||||
|
||||
} elseif ($t === ':' || $t === '=') { // KeyValuePair separator
|
||||
if ($hasValue && (is_array($value) || is_object($value))) {
|
||||
$this->error('Unacceptable key');
|
||||
|
||||
} elseif ($hasKey && $key === null && $hasValue && !$inlineParser) {
|
||||
$n++;
|
||||
$result[] = $this->parse($indent . ' ', [], $value, true);
|
||||
$newIndent = isset($tokens[$n], $tokens[$n + 1]) ? (string) substr($tokens[$n][0], 1) : ''; // not last
|
||||
if (strlen($newIndent) > strlen($indent)) {
|
||||
$n++;
|
||||
$this->error('Bad indentation');
|
||||
} elseif (strlen($newIndent) < strlen($indent)) {
|
||||
return $mainResult; // block parser exit point
|
||||
}
|
||||
$hasKey = $hasValue = false;
|
||||
|
||||
} elseif ($hasKey || !$hasValue) {
|
||||
$this->error();
|
||||
|
||||
} else {
|
||||
$key = (string) $value;
|
||||
$hasKey = true;
|
||||
$hasValue = false;
|
||||
$result = &$mainResult;
|
||||
}
|
||||
|
||||
} elseif ($t === '-') { // BlockArray bullet
|
||||
if ($hasKey || $hasValue || $inlineParser) {
|
||||
$this->error();
|
||||
}
|
||||
$key = null;
|
||||
$hasKey = true;
|
||||
|
||||
} elseif (isset(self::BRACKETS[$t])) { // Opening bracket [ ( {
|
||||
if ($hasValue) {
|
||||
if ($t !== '(') {
|
||||
$this->error();
|
||||
}
|
||||
$n++;
|
||||
if ($value instanceof Entity && $value->value === Neon::CHAIN) {
|
||||
end($value->attributes)->attributes = $this->parse(false, []);
|
||||
} else {
|
||||
$value = new Entity($value, $this->parse(false, []));
|
||||
}
|
||||
} else {
|
||||
$n++;
|
||||
$value = $this->parse(false, []);
|
||||
}
|
||||
$hasValue = true;
|
||||
if (!isset($tokens[$n]) || $tokens[$n][0] !== self::BRACKETS[$t]) { // unexpected type of bracket or block-parser
|
||||
$this->error();
|
||||
}
|
||||
|
||||
} elseif ($t === ']' || $t === '}' || $t === ')') { // Closing bracket ] ) }
|
||||
if (!$inlineParser) {
|
||||
$this->error();
|
||||
}
|
||||
break;
|
||||
|
||||
} elseif ($t[0] === "\n") { // Indent
|
||||
if ($inlineParser) {
|
||||
if ($hasKey || $hasValue) {
|
||||
$this->addValue($result, $hasKey ? $key : null, $hasValue ? $value : null);
|
||||
$hasKey = $hasValue = false;
|
||||
}
|
||||
|
||||
} else {
|
||||
while (isset($tokens[$n + 1]) && $tokens[$n + 1][0][0] === "\n") {
|
||||
$n++; // skip to last indent
|
||||
}
|
||||
if (!isset($tokens[$n + 1])) {
|
||||
break;
|
||||
}
|
||||
|
||||
$newIndent = (string) substr($tokens[$n][0], 1);
|
||||
if ($indent === null) { // first iteration
|
||||
$indent = $newIndent;
|
||||
}
|
||||
$minlen = min(strlen($newIndent), strlen($indent));
|
||||
if ($minlen && (string) substr($newIndent, 0, $minlen) !== (string) substr($indent, 0, $minlen)) {
|
||||
$n++;
|
||||
$this->error('Invalid combination of tabs and spaces');
|
||||
}
|
||||
|
||||
if (strlen($newIndent) > strlen($indent)) { // open new block-array or hash
|
||||
if ($hasValue || !$hasKey) {
|
||||
$n++;
|
||||
$this->error('Bad indentation');
|
||||
}
|
||||
$this->addValue($result, $key, $this->parse($newIndent));
|
||||
$newIndent = isset($tokens[$n], $tokens[$n + 1]) ? (string) substr($tokens[$n][0], 1) : ''; // not last
|
||||
if (strlen($newIndent) > strlen($indent)) {
|
||||
$n++;
|
||||
$this->error('Bad indentation');
|
||||
}
|
||||
$hasKey = false;
|
||||
|
||||
} else {
|
||||
if ($hasValue && !$hasKey) { // block items must have "key"; null key means list item
|
||||
break;
|
||||
|
||||
} elseif ($hasKey) {
|
||||
$this->addValue($result, $key, $hasValue ? $value : null);
|
||||
if ($key !== null && !$hasValue && $newIndent === $indent && isset($tokens[$n + 1]) && $tokens[$n + 1][0] === '-') {
|
||||
$result = &$result[$key];
|
||||
}
|
||||
$hasKey = $hasValue = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($newIndent) < strlen($indent)) { // close block
|
||||
return $mainResult; // block parser exit point
|
||||
}
|
||||
}
|
||||
|
||||
} else { // Value
|
||||
if ($t[0] === '"' || $t[0] === "'") {
|
||||
if (preg_match('#^...\n++([\t ]*+)#', $t, $m)) {
|
||||
$converted = substr($t, 3, -3);
|
||||
$converted = str_replace("\n" . $m[1], "\n", $converted);
|
||||
$converted = preg_replace('#^\n|\n[\t ]*+\z#', '', $converted);
|
||||
} else {
|
||||
$converted = substr($t, 1, -1);
|
||||
}
|
||||
if ($t[0] === '"') {
|
||||
$converted = preg_replace_callback('#\\\\(?:ud[89ab][0-9a-f]{2}\\\\ud[c-f][0-9a-f]{2}|u[0-9a-f]{4}|x[0-9a-f]{2}|.)#i', [$this, 'cbString'], $converted);
|
||||
}
|
||||
} elseif (($fix56 = self::SIMPLE_TYPES) && isset($fix56[$t]) && (!isset($tokens[$n + 1][0]) || ($tokens[$n + 1][0] !== ':' && $tokens[$n + 1][0] !== '='))) {
|
||||
$converted = constant(self::SIMPLE_TYPES[$t]);
|
||||
} elseif (is_numeric($t)) {
|
||||
$converted = $t * 1;
|
||||
} elseif (preg_match(self::PATTERN_HEX, $t)) {
|
||||
$converted = hexdec($t);
|
||||
} elseif (preg_match(self::PATTERN_OCTAL, $t)) {
|
||||
$converted = octdec($t);
|
||||
} elseif (preg_match(self::PATTERN_BINARY, $t)) {
|
||||
$converted = bindec($t);
|
||||
} elseif (preg_match(self::PATTERN_DATETIME, $t)) {
|
||||
$converted = new \DateTimeImmutable($t);
|
||||
} else { // literal
|
||||
$converted = $t;
|
||||
}
|
||||
if ($hasValue) {
|
||||
if ($value instanceof Entity) { // Entity chaining
|
||||
if ($value->value !== Neon::CHAIN) {
|
||||
$value = new Entity(Neon::CHAIN, [$value]);
|
||||
}
|
||||
$value->attributes[] = new Entity($converted);
|
||||
} else {
|
||||
$this->error();
|
||||
}
|
||||
} else {
|
||||
$value = $converted;
|
||||
$hasValue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($inlineParser) {
|
||||
if ($hasKey || $hasValue) {
|
||||
$this->addValue($result, $hasKey ? $key : null, $hasValue ? $value : null);
|
||||
}
|
||||
} else {
|
||||
if ($hasValue && !$hasKey) { // block items must have "key"
|
||||
if ($result === null) {
|
||||
$result = $value; // simple value parser
|
||||
} else {
|
||||
$this->error();
|
||||
}
|
||||
} elseif ($hasKey) {
|
||||
$this->addValue($result, $key, $hasValue ? $value : null);
|
||||
}
|
||||
}
|
||||
return $mainResult;
|
||||
}
|
||||
|
||||
|
||||
private function addValue(&$result, $key, $value)
|
||||
{
|
||||
if ($key === null) {
|
||||
$result[] = $value;
|
||||
} elseif ($result && array_key_exists($key, $result)) {
|
||||
$this->error("Duplicated key '$key'");
|
||||
} else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function cbString(array $m): string
|
||||
{
|
||||
$sq = $m[0];
|
||||
if (($fix56 = self::ESCAPE_SEQUENCES) && isset($fix56[$sq[1]])) { // workaround for PHP 5.6
|
||||
return self::ESCAPE_SEQUENCES[$sq[1]];
|
||||
} elseif ($sq[1] === 'u' && strlen($sq) >= 6) {
|
||||
$lead = hexdec(substr($sq, 2, 4));
|
||||
$tail = hexdec(substr($sq, 8, 4));
|
||||
$code = $tail ? (0x2400 + (($lead - 0xD800) << 10) + $tail) : $lead;
|
||||
if ($code >= 0xD800 && $code <= 0xDFFF) {
|
||||
$this->error("Invalid UTF-8 (lone surrogate) $sq");
|
||||
}
|
||||
return iconv('UTF-32BE', 'UTF-8//IGNORE', pack('N', $code));
|
||||
} elseif ($sq[1] === 'x' && strlen($sq) === 4) {
|
||||
return chr(hexdec(substr($sq, 2)));
|
||||
} else {
|
||||
$this->error("Invalid escaping sequence $sq");
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function error(string $message = "Unexpected '%s'")
|
||||
{
|
||||
$last = isset($this->tokens[$this->pos]) ? $this->tokens[$this->pos] : null;
|
||||
$offset = $last ? $last[1] : strlen($this->input);
|
||||
$text = substr($this->input, 0, $offset);
|
||||
$line = substr_count($text, "\n");
|
||||
$col = $offset - strrpos("\n" . $text, "\n") + 1;
|
||||
$token = $last ? str_replace("\n", '<new line>', substr($last[0], 0, 40)) : 'end';
|
||||
throw new Exception(str_replace('%s', $token, $message) . " on line $line, column $col.");
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nette\Neon;
|
||||
|
||||
|
||||
/**
|
||||
* Simple generator for Nette Object Notation.
|
||||
*/
|
||||
final class Encoder
|
||||
{
|
||||
const BLOCK = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the NEON representation of a value.
|
||||
*/
|
||||
public function encode($var, int $flags = 0): string
|
||||
{
|
||||
if ($var instanceof \DateTimeInterface) {
|
||||
return $var->format('Y-m-d H:i:s O');
|
||||
|
||||
} elseif ($var instanceof Entity) {
|
||||
if ($var->value === Neon::CHAIN) {
|
||||
return implode('', array_map([$this, 'encode'], $var->attributes));
|
||||
}
|
||||
return $this->encode($var->value) . '('
|
||||
. (is_array($var->attributes) ? substr($this->encode($var->attributes), 1, -1) : '') . ')';
|
||||
}
|
||||
|
||||
if (is_object($var)) {
|
||||
$obj = $var;
|
||||
$var = [];
|
||||
foreach ($obj as $k => $v) {
|
||||
$var[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($var)) {
|
||||
$isList = !$var || array_keys($var) === range(0, count($var) - 1);
|
||||
$s = '';
|
||||
if ($flags & self::BLOCK) {
|
||||
if (count($var) === 0) {
|
||||
return '[]';
|
||||
}
|
||||
foreach ($var as $k => $v) {
|
||||
$v = $this->encode($v, self::BLOCK);
|
||||
$s .= ($isList ? '-' : $this->encode($k) . ':')
|
||||
. (strpos($v, "\n") === false
|
||||
? ' ' . $v . "\n"
|
||||
: "\n" . preg_replace('#^(?=.)#m', "\t", $v) . (substr($v, -2, 1) === "\n" ? '' : "\n"));
|
||||
}
|
||||
return $s;
|
||||
|
||||
} else {
|
||||
foreach ($var as $k => $v) {
|
||||
$s .= ($isList ? '' : $this->encode($k) . ': ') . $this->encode($v) . ', ';
|
||||
}
|
||||
return ($isList ? '[' : '{') . substr($s, 0, -2) . ($isList ? ']' : '}');
|
||||
}
|
||||
|
||||
} elseif (
|
||||
is_string($var)
|
||||
&& !is_numeric($var)
|
||||
&& !preg_match('~[\x00-\x1F]|^\d{4}|^(true|false|yes|no|on|off|null)\z~i', $var)
|
||||
&& preg_match('~^' . Decoder::PATTERNS[1] . '\z~x', $var) // 1 = literals
|
||||
) {
|
||||
return $var;
|
||||
|
||||
} elseif (is_float($var)) {
|
||||
$var = json_encode($var);
|
||||
return strpos($var, '.') === false ? $var . '.0' : $var;
|
||||
|
||||
} else {
|
||||
return json_encode($var, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nette\Neon;
|
||||
|
||||
|
||||
/**
|
||||
* Representation of 'foo(bar=1)' literal
|
||||
*/
|
||||
final class Entity extends \stdClass
|
||||
{
|
||||
/** @var mixed */
|
||||
public $value;
|
||||
|
||||
/** @var array */
|
||||
public $attributes;
|
||||
|
||||
|
||||
public function __construct($value, array $attrs = [])
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->attributes = $attrs;
|
||||
}
|
||||
|
||||
|
||||
public static function __set_state(array $properties)
|
||||
{
|
||||
return new self($properties['value'], $properties['attributes']);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nette\Neon;
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates error of NEON processing.
|
||||
*/
|
||||
class Exception extends \Exception
|
||||
{
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nette\Neon;
|
||||
|
||||
|
||||
/**
|
||||
* Simple parser & generator for Nette Object Notation.
|
||||
*/
|
||||
final class Neon
|
||||
{
|
||||
const BLOCK = Encoder::BLOCK;
|
||||
|
||||
const CHAIN = '!!chain';
|
||||
|
||||
|
||||
/**
|
||||
* Returns the NEON representation of a value.
|
||||
*/
|
||||
public static function encode($var, int $flags = 0): string
|
||||
{
|
||||
$encoder = new Encoder;
|
||||
return $encoder->encode($var, $flags);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a NEON string.
|
||||
* @return mixed
|
||||
*/
|
||||
public static function decode(string $input)
|
||||
{
|
||||
$decoder = new Decoder;
|
||||
return $decoder->decode($input);
|
||||
}
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/Neon/Decoder.php';
|
||||
require __DIR__ . '/Neon/Encoder.php';
|
||||
require __DIR__ . '/Neon/Entity.php';
|
||||
require __DIR__ . '/Neon/Exception.php';
|
||||
require __DIR__ . '/Neon/Neon.php';
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
Preprocessing
|
||||
-------------
|
||||
- tabs are converted to single space
|
||||
- \r is removed
|
||||
|
||||
|
||||
Comment
|
||||
-------
|
||||
Comment ::= '#' .*
|
||||
|
||||
|
||||
Values
|
||||
------
|
||||
Value ::= Boolean | Null | integer | float | String | DateTime | Literal | InlineArray | Entity
|
||||
Boolean ::= 'true' | 'TRUE' | 'false' | 'FALSE' | 'yes' | 'YES' | 'no' | 'NO'
|
||||
Null ::= 'null' | 'NULL' | ''
|
||||
String ::= "word\u231" | 'word'
|
||||
Literal ::= trimmed stream of characters [^#"',:=@[\]{}()\s!`] ( [^#,:=\]})(] | ':' [^\s,\]})] | \S '#' )*
|
||||
Entity ::= Value '(' ( ArrayEntry ',' )* ')'
|
||||
|
||||
|
||||
InlineArray
|
||||
-----------
|
||||
InlineArray ::= '{' ( ArrayEntry ',' )* '}' | '[' ( ArrayEntry ',' )* ']' | '(' ( ArrayEntry ',' )* ')'
|
||||
|
||||
ArrayEntry ::= Value | KeyValuePair
|
||||
KeyValuePair ::= Value '=' Value | Value ': ' Value
|
||||
|
||||
|
||||
BlockArray
|
||||
----------
|
||||
BlockArray ::= Indent ( '- ' Value | KeyValuePair ) EOL
|
||||
Reference in New Issue
Block a user