Skip to content

Commit

Permalink
Added new Grav\Framework\File\Formatter classes for encoding/decodi…
Browse files Browse the repository at this point in the history
…ng YAML, MarkDown, JSON, INI and PHP serialized formats
  • Loading branch information
mahagr committed Apr 24, 2018
1 parent b9a7341 commit 895e145
Show file tree
Hide file tree
Showing 7 changed files with 369 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Set minimum requirements to [PHP 5.6.4](https://getgrav.org/blog/raising-php-requirements-2018)
* Updated Doctrine Collections to 1.4
* Updated Symfony Components to 3.4 (with compatibility mode to fall back to Symfony YAML 2.8)
* Added new `Grav\Framework\File\Formatter` classes for encoding/decoding YAML, MarkDown, JSON, INI and PHP serialized formats

# v1.4.4
## 04/12/2018
Expand Down
28 changes: 28 additions & 0 deletions system/src/Grav/Framework/File/Formatter/FormatterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/**
* @package Grav\Framework\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

namespace Grav\Framework\Formatter;

interface FormatterInterface
{
/**
* Encode data into a string.
*
* @param array $data
* @return string
*/
public function encode($data);

/**
* Decode a string into data.
*
* @param string $data
* @return array
*/
public function decode($data);
}
51 changes: 51 additions & 0 deletions system/src/Grav/Framework/File/Formatter/IniFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* @package Grav\Framework\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

namespace Grav\Framework\Formatter;

/**
* Class IniFormatter
* @package Grav\Framework\Formatter
*/
class IniFormatter implements FormatterInterface
{
public function getFileExtension()
{
return 'ini';
}

/**
* {@inheritdoc}
*/
public function encode($data)
{
$string = '';
foreach ($data as $key => $value) {
$string .= $key . '="' . preg_replace(
['/"/', '/\\\/', "/\t/", "/\n/", "/\r/"],
['\"', '\\\\', '\t', '\n', '\r'],
$value
) . "\"\n";
}
return $string;
}

/**
* {@inheritdoc}
*/
public function decode($data)
{
$decoded = @parse_ini_string($data);

if ($decoded === false) {
throw new \RuntimeException("Decoding INI format failed'");
}

return $decoded;
}
}
51 changes: 51 additions & 0 deletions system/src/Grav/Framework/File/Formatter/JsonFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* @package Grav\Framework\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

namespace Grav\Framework\Formatter;

/**
* Class JsonFormatter
* @package Grav\Framework\Formatter
*/
class JsonFormatter implements FormatterInterface
{
/** @var array */
private $config;

public function __construct(array $config = [])
{
$this->config = $config + [
'encode_options' => 0,
'decode_assoc' => true
];
}

public function getFileExtension()
{
return 'json';
}

/**
* {@inheritdoc}
*/
public function encode($data)
{
$encoded = json_encode($data, $this->config['encode_options']);
if ($encoded === false) {
throw new \RuntimeException('');
}
}

/**
* {@inheritdoc}
*/
public function decode($data)
{
return json_decode($data, $this->config['decode_assoc']);
}
}
98 changes: 98 additions & 0 deletions system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php
/**
* @package Grav\Framework\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

namespace Grav\Framework\Formatter;

/**
* Class MarkdownFormatter
* @package Grav\Framework\Formatter
*/
class MarkdownFormatter implements FormatterInterface
{
/** @var array */
private $config;
/** @var FormatterInterface */
private $headerFormatter;

public function __construct(array $config = [], FormatterInterface $headerFormatter = null)
{
$this->config = $config + [
'header' => 'header',
'body' => 'markdown',
'raw' => 'frontmatter',
'formatter' => ['inline' => 20]
];

$this->headerFormatter = $headerFormatter ?: new YamlFormatter($this->config['formatter']);
}

public function getFileExtension()
{
return 'md';
}

/**
* {@inheritdoc}
*/
public function encode($data)
{
$headerVar = $this->config['header'];
$bodyVar = $this->config['body'];

$header = isset($data[$headerVar]) ? (array) $data[$headerVar] : [];
$body = isset($data[$bodyVar]) ? (string) $data[$bodyVar] : '';

// Create Markdown file with YAML header.
$encoded = '';
if ($header) {
$encoded = "---\n" . trim($this->headerFormatter->encode($data['header'])) . "\n---\n\n";
}
$encoded .= $body;

// Normalize line endings to Unix style.
$encoded = preg_replace("/(\r\n|\r)/", "\n", $encoded);

return $encoded;
}

/**
* {@inheritdoc}
*/
public function decode($data)
{
$headerVar = $this->config['header'];
$bodyVar = $this->config['body'];
$rawVar = $this->config['raw'];

$content = [
$headerVar => [],
$bodyVar => ''
];

$headerRegex = "/^---\n(.+?)\n---\n{0,}(.*)$/uis";

// Normalize line endings to Unix style.
$data = preg_replace("/(\r\n|\r)/", "\n", $data);

// Parse header.
preg_match($headerRegex, ltrim($data), $matches);
if(empty($matches)) {
$content[$bodyVar] = $data;
} else {
// Normalize frontmatter.
$frontmatter = preg_replace("/\n\t/", "\n ", $matches[1]);
if ($rawVar) {
$content[$rawVar] = $frontmatter;
}
$content[$headerVar] = $this->headerFormatter->decode($frontmatter);
$content[$bodyVar] = $matches[2];
}

return $content;
}
}
55 changes: 55 additions & 0 deletions system/src/Grav/Framework/File/Formatter/SerializeFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* @package Grav\Framework\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

namespace Grav\Framework\Formatter;

class SerializeFormatter implements FormatterInterface
{
public function getFileExtension()
{
return 'raw';
}

/**
* {@inheritdoc}
*/
public function encode($data)
{
return serialize($this->preserveLines($data, ["\n", "\r"], ['\\n', '\\r']));
}

/**
* {@inheritdoc}
*/
public function decode($data)
{
return $this->preserveLines(unserialize($data), ['\\n', '\\r'], ["\n", "\r"]);
}

/**
* Preserve new lines, recursive function.
*
* @param mixed $data
* @param array $search
* @param array $replace
* @return mixed
*/
protected function preserveLines($data, $search, $replace)
{
if (is_string($data)) {
$data = str_replace($search, $replace, $data);
} elseif (is_array($data)) {
foreach ($data as &$value) {
$value = $this->preserveLines($value, $search, $replace);
}
unset($value);
}

return $data;
}
}
85 changes: 85 additions & 0 deletions system/src/Grav/Framework/File/Formatter/YamlFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php
/**
* @package Grav\Framework\Formatter
*
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/

namespace Grav\Framework\Formatter;

use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml as YamlParser;
use RocketTheme\Toolbox\Compat\Yaml\Yaml as FallbackYamlParser;

/**
* Class YamlFormatter
* @package Grav\Framework\Formatter
*/
class YamlFormatter implements FormatterInterface
{
/** @var array */
private $config;

public function __construct(array $config = [])
{
$this->config = $config + [
'inline' => 5,
'indent' => 2,
'native' => true,
'compat' => true
];
}

public function getFileExtension()
{
return 'yaml';
}

/**
* {@inheritdoc}
*/
public function encode($data)
{
try {
return (string) YamlParser::dump(
$data,
$this->config['inline'],
$this->config['indent'],
YamlParser::DUMP_EXCEPTION_ON_INVALID_TYPE
);
} catch (DumpException $e) {
throw new \RuntimeException($e->getMessage(), 500, $e);
}
}

/**
* {@inheritdoc}
*/
public function decode($data)
{
// Try native PECL YAML PHP extension first if available.
if ($this->config['native'] && function_exists('yaml_parse')) {
// Safely decode YAML.
$saved = @ini_get('yaml.decode_php');
@ini_set('yaml.decode_php', 0);
$decoded = @yaml_parse($data);
@ini_set('yaml.decode_php', $saved);

if ($decoded !== false) {
return (array) $decoded;
}
}

try {
return (array) YamlParser::parse($data);
} catch (ParseException $e) {
if ($this->config['compat']) {
return (array) FallbackYamlParser::parse($data);
}

throw new \RuntimeException($e->getMessage(), 500, $e);
}
}
}

0 comments on commit 895e145

Please sign in to comment.