Spade
Mini Shell
| Directory:~$ /home/lmsyaran/public_html/joomla4/ |
| [Home] [System Details] [Kill Me] |
ArrayTraits/src/ArrayAccess.php000064400000003070151162104010012500
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements ArrayAccess interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait ArrayAccess
{
/**
* Whether or not an offset exists.
*
* @param mixed $offset An offset to check for.
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function offsetExists($offset)
{
return isset($this->items[$offset]);
}
/**
* Returns the value at specified offset.
*
* @param mixed $offset The offset to retrieve.
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
return isset($this->items[$offset]) ? $this->items[$offset] :
null;
}
/**
* Assigns a value to the specified offset.
*
* @param mixed $offset The offset to assign the value to.
* @param mixed $value The value to set.
*/
public function offsetSet($offset, $value)
{
if (null === $offset) {
$this->items[] = $value;
} else {
$this->items[$offset] = $value;
}
}
/**
* Unsets an offset.
*
* @param mixed $offset The offset to unset.
*/
public function offsetUnset($offset)
{
// Hack to make Iterator trait work together with unset.
if (isset($this->iteratorUnset) && $offset ==
key($this->items)) {
$this->iteratorUnset = true;
}
unset($this->items[$offset]);
}
}
ArrayTraits/src/ArrayAccessWithGetters.php000064400000002231151162104010014670
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements getters and setters.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*/
trait ArrayAccessWithGetters
{
use ArrayAccess;
/**
* Magic setter method
*
* @param mixed $offset Asset name value
* @param mixed $value Asset value
*/
public function __set($offset, $value)
{
$this->offsetSet($offset, $value);
}
/**
* Magic getter method
*
* @param mixed $offset Asset name value
* @return mixed Asset value
*/
public function __get($offset)
{
return $this->offsetGet($offset);
}
/**
* Magic method to determine if the attribute is set
*
* @param mixed $offset Asset name value
* @return boolean True if the value is set
*/
public function __isset($offset)
{
return $this->offsetExists($offset);
}
/**
* Magic method to unset the attribute
*
* @param mixed $offset The name value to unset
*/
public function __unset($offset)
{
$this->offsetUnset($offset);
}
}
ArrayTraits/src/Constructor.php000064400000000723151162104010012627
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements Constructor for setting items.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Constructor
{
/**
* Constructor to initialize array.
*
* @param array $items Initial items inside the iterator.
*/
public function __construct(array $items = array())
{
$this->items = $items;
}
}
ArrayTraits/src/Countable.php000064400000000603151162104010012213
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements \Countable interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Countable
{
/**
* Implements Countable interface.
*
* @return int
*/
public function count()
{
return \count($this->items);
}
}
ArrayTraits/src/Export.php000064400000002106151162104010011560
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Yaml;
/**
* Implements ExportInterface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Export
{
/**
* Convert object into an array.
*
* @return array
*/
public function toArray()
{
return $this->items;
}
/**
* Convert object into YAML string.
*
* @param int $inline The level where you switch to inline YAML.
* @param int $indent The amount of spaces to use for indentation of
nested nodes.
*
* @return string A YAML string representing the object.
* @throws DumpException
*/
public function toYaml($inline = 3, $indent = 2)
{
return Yaml::dump($this->toArray(), $inline, $indent, true,
false);
}
/**
* Convert object into JSON string.
*
* @return string
*/
public function toJson()
{
return json_encode($this->toArray());
}
}
ArrayTraits/src/ExportInterface.php000064400000001157151162104010013406
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Defines Export interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*/
interface ExportInterface
{
/**
* Convert object into an array.
*
* @return array
*/
public function toArray();
/**
* Convert object into YAML string.
*
* @param int $inline
* @param int $indent
* @return string
*/
public function toYaml($inline = 3, $indent = 2);
/**
* Convert object into JSON string.
*
* @return string
*/
public function toJson();
}
ArrayTraits/src/Iterator.php000064400000003133151162104010012071
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements \Iterator interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Iterator
{
/**
* Hack to make Iterator work together with unset().
*
* @var bool
*/
private $iteratorUnset = false;
/**
* Returns the current element.
*
* @return mixed Can return any type.
*/
public function current()
{
return current($this->items);
}
/**
* Returns the key of the current element.
*
* @return mixed Returns scalar on success, or NULL on failure.
*/
public function key()
{
return key($this->items);
}
/**
* Moves the current position to the next element.
*
* @return void
*/
public function next()
{
if ($this->iteratorUnset) {
// If current item was unset, position is already in the next
element (do nothing).
$this->iteratorUnset = false;
} else {
next($this->items);
}
}
/**
* Rewinds back to the first element of the Iterator.
*
* @return void
*/
public function rewind()
{
$this->iteratorUnset = false;
reset($this->items);
}
/**
* This method is called after Iterator::rewind() and Iterator::next()
to check if the current position is valid.
*
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function valid()
{
return key($this->items) !== null;
}
}
ArrayTraits/src/NestedArrayAccess.php000064400000012463151162104010013651
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements nested ArrayAccess interface with dot notation.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait NestedArrayAccess
{
protected $nestedSeparator = '.';
/**
* Get value by using dot notation for nested arrays/objects.
*
* @example $value =
$this->get('this.is.my.nested.variable');
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @return mixed Value.
*/
public function get($name, $default = null, $separator = null)
{
$path = explode($separator ?: $this->nestedSeparator, $name);
$current = $this->items;
foreach ($path as $field) {
if (\is_object($current) &&
isset($current->{$field})) {
$current = $current->{$field};
} elseif (\is_array($current) &&
isset($current[$field])) {
$current = $current[$field];
} else {
return $default;
}
}
return $current;
}
/**
* Set value by using dot notation for nested arrays/objects.
*
* @example $data->set('this.is.my.nested.variable',
$value);
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $value New value.
* @param string $separator Separator, defaults to '.'
* @return $this
*/
public function set($name, $value, $separator = null)
{
$path = explode($separator ?: $this->nestedSeparator, $name);
$current = &$this->items;
foreach ($path as $field) {
if (\is_object($current)) {
// Handle objects.
if (!isset($current->{$field})) {
$current->{$field} = [];
}
$current = &$current->{$field};
} else {
// Handle arrays and scalars.
if (!\is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
}
$current = $value;
return $this;
}
/**
* Unset value by using dot notation for nested arrays/objects.
*
* @example $data->undef('this.is.my.nested.variable');
*
* @param string $name Dot separated path to the requested
value.
* @param string $separator Separator, defaults to '.'
* @return $this
*/
public function undef($name, $separator = null)
{
if ($name === '') {
$this->items = [];
return $this;
}
$path = explode($separator ?: $this->nestedSeparator, $name);
$var = array_pop($path);
$current = &$this->items;
foreach ($path as $field) {
if (\is_object($current)) {
// Handle objects.
if (!isset($current->{$field})) {
return $this;
}
$current = &$current->{$field};
} else {
// Handle arrays and scalars.
if (!\is_array($current) || !isset($current[$field])) {
return $this;
}
$current = &$current[$field];
}
}
unset($current[$var]);
return $this;
}
/**
* Set default value by using dot notation for nested arrays/objects.
*
* @example $data->def('this.is.my.nested.variable',
'default');
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @return $this
*/
public function def($name, $default = null, $separator = null)
{
$this->set($name, $this->get($name, $default, $separator),
$separator);
return $this;
}
/**
* Whether or not an offset exists.
*
* @param mixed $offset An offset to check for.
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function offsetExists($offset)
{
return $this->get($offset) !== null;
}
/**
* Returns the value at specified offset.
*
* @param mixed $offset The offset to retrieve.
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* Assigns a value to the specified offset.
*
* @param mixed $offset The offset to assign the value to.
* @param mixed $value The value to set.
*/
public function offsetSet($offset, $value)
{
if (null === $offset) {
$this->items[] = $value;
} else {
$this->set($offset, $value);
}
}
/**
* Unsets variable at specified offset.
*
* @param $offset
*/
public function offsetUnset($offset)
{
if (null === $offset) {
$this->items[] = [];
} else {
$this->undef($offset);
}
}
}
ArrayTraits/src/NestedArrayAccessWithGetters.php000064400000002245151162104010016040
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements getters and setters.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*/
trait NestedArrayAccessWithGetters
{
use NestedArrayAccess;
/**
* Magic setter method
*
* @param mixed $offset Asset name value
* @param mixed $value Asset value
*/
public function __set($offset, $value)
{
$this->offsetSet($offset, $value);
}
/**
* Magic getter method
*
* @param mixed $offset Asset name value
* @return mixed Asset value
*/
public function __get($offset)
{
return $this->offsetGet($offset);
}
/**
* Magic method to determine if the attribute is set
*
* @param mixed $offset Asset name value
* @return boolean True if the value is set
*/
public function __isset($offset)
{
return $this->offsetExists($offset);
}
/**
* Magic method to unset the attribute
*
* @param mixed $offset The name value to unset
*/
public function __unset($offset)
{
$this->offsetUnset($offset);
}
}
ArrayTraits/src/Serializable.php000064400000001323151162104010012705
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements \Serializable interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Serializable
{
/**
* Returns string representation of the object.
*
* @return string Returns the string representation of the object.
*/
public function serialize()
{
return serialize($this->items);
}
/**
* Called during unserialization of the object.
*
* @param string $serialized The string representation of the object.
*/
public function unserialize($serialized)
{
$this->items = unserialize($serialized);
}
}
Blueprints/src/BlueprintForm.php000064400000041067151162104010012762
0ustar00<?php
namespace RocketTheme\Toolbox\Blueprints;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RuntimeException;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
*/
abstract class BlueprintForm implements \ArrayAccess, ExportInterface
{
use NestedArrayAccessWithGetters;
use Export;
/** @var array */
protected $items;
/** @var string */
protected $filename;
/** @var string */
protected $context;
/** @var array */
protected $overrides = [];
/** @var array */
protected $dynamic = [];
/**
* Load file and return its contents.
*
* @param string $filename
* @return array
*/
abstract protected function loadFile($filename);
/**
* Get list of blueprint form files (file and its parents for
overrides).
*
* @param string|array $path
* @param string $context
* @return array
*/
abstract protected function getFiles($path, $context = null);
/**
* Constructor.
*
* @param string|array $filename
* @param array $items
*/
public function __construct($filename = null, array $items = [])
{
$this->nestedSeparator = '/';
$this->filename = $filename;
$this->items = $items;
}
/**
* Set filename for the blueprint. Can also be array of files for
parent lookup.
*
* @param string|array $filename
* @return $this
*/
public function setFilename($filename)
{
$this->filename = $filename;
return $this;
}
/**
* Get the filename of the blueprint.
*
* @return array|null|string
*/
public function getFilename()
{
return $this->filename;
}
/**
* Set context for import@ and extend@.
*
* @param $context
* @return $this
*/
public function setContext($context)
{
$this->context = $context;
return $this;
}
/**
* Set custom overrides for import@ and extend@.
*
* @param array $overrides
* @return $this
*/
public function setOverrides($overrides)
{
$this->overrides = $overrides;
return $this;
}
/**
* Load blueprint.
*
* @return $this
*/
public function load($extends = null)
{
// Only load and extend blueprint if it has not yet been loaded.
if (empty($this->items) && $this->filename) {
// Get list of files.
$files = $this->getFiles($this->filename);
// Load and extend blueprints.
$data = $this->doLoad($files, $extends);
$this->items = (array) array_shift($data);
foreach ($data as $content) {
$this->extend($content, true);
}
}
// Import blueprints.
$this->deepInit($this->items);
return $this;
}
/**
* Initialize blueprints with its dynamic fields.
*
* @return $this
*/
public function init()
{
foreach ($this->dynamic as $key => $data) {
// Locate field.
$path = explode('/', $key);
$current = &$this->items;
foreach ($path as $field) {
if (\is_object($current)) {
// Handle objects.
if (!isset($current->{$field})) {
$current->{$field} = [];
}
$current = &$current->{$field};
} else {
// Handle arrays and scalars.
if (!\is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
}
// Set dynamic property.
foreach ($data as $property => $call) {
$action = 'dynamic' .
ucfirst($call['action']);
if (method_exists($this, $action)) {
$this->{$action}($current, $property, $call);
}
}
}
return $this;
}
/**
* Get form.
*
* @return array
*/
public function form()
{
return (array) $this->get('form');
}
/**
* Get form fields.
*
* @return array
*/
public function fields()
{
$fields = $this->get('form/fields');
if ($fields === null) {
$field = $this->get('form/field');
$fields = $field !== null ? ['' => (array) $field]
: $fields;
}
return (array) $fields;
}
/**
* Extend blueprint with another blueprint.
*
* @param BlueprintForm|array $extends
* @param bool $append
* @return $this
*/
public function extend($extends, $append = false)
{
if ($extends instanceof self) {
$extends = $extends->toArray();
}
if ($append) {
$a = $this->items;
$b = $extends;
} else {
$a = $extends;
$b = $this->items;
}
$this->items = $this->deepMerge($a, $b);
return $this;
}
/**
* @param string $name
* @param mixed $value
* @param string $separator
* @param bool $append
* @return $this
*/
public function embed($name, $value, $separator = '/',
$append = false)
{
$oldValue = $this->get($name, null, $separator);
if (\is_array($oldValue) && \is_array($value)) {
if ($append) {
$a = $oldValue;
$b = $value;
} else {
$a = $value;
$b = $oldValue;
}
$value = $this->deepMerge($a, $b);
}
$this->set($name, $value, $separator);
return $this;
}
/**
* Get blueprints by using slash notation for nested arrays/objects.
*
* @example $value =
$this->resolve('this/is/my/nested/variable');
* returns ['this/is/my', 'nested/variable']
*
* @param array $path
* @param string $separator
* @return array
*/
public function resolve(array $path, $separator = '/')
{
$fields = false;
$parts = [];
$current = $this['form/fields'];
$result = [null, null, null];
while (($field = current($path)) !== null) {
if (!$fields && isset($current['fields'])) {
if (!empty($current['array'])) {
$result = [$current, $parts, $path ?
implode($separator, $path) : null];
// Skip item offset.
$parts[] = array_shift($path);
}
$current = $current['fields'];
$fields = true;
} elseif (isset($current[$field])) {
$parts[] = array_shift($path);
$current = $current[$field];
$fields = false;
} elseif (isset($current[$index = '.' . $field])) {
$parts[] = array_shift($path);
$current = $current[$index];
$fields = false;
} else {
break;
}
}
return $result;
}
/**
* Deep merge two arrays together.
*
* @param array $a
* @param array $b
* @return array
*/
protected function deepMerge(array $a, array $b)
{
$bref_stack = [&$a];
$head_stack = [$b];
do {
end($bref_stack);
$bref = &$bref_stack[key($bref_stack)];
$head = array_pop($head_stack);
unset($bref_stack[key($bref_stack)]);
foreach ($head as $key => $value) {
if (strpos($key, '@') !== false) {
// Remove @ from the start and the end. Key syntax
`import@2` is supported to allow multiple operations of the same type.
$list = explode('-',
preg_replace('/^(@*)?([^@]+)(@\d*)?$/', '\2', $key),
2);
$action = array_shift($list);
$property = array_shift($list);
switch ($action) {
case 'unset':
case 'replace':
if (!$property) {
$bref = ['unset@' => true];
} else {
unset($bref[$property]);
}
continue 2;
}
}
if (isset($key, $bref[$key]) &&
\is_array($bref[$key]) && \is_array($head[$key])) {
$bref_stack[] = &$bref[$key];
$head_stack[] = $head[$key];
} else {
$bref = array_merge($bref, [$key => $head[$key]]);
}
}
} while (\count($head_stack));
return $a;
}
/**
* @param array $items
* @param array $path
* @return string
*/
protected function deepInit(array &$items, $path = [])
{
$ordering = '';
$order = [];
$field = end($path) === 'fields';
foreach ($items as $key => &$item) {
// Set name for nested field.
if ($field && isset($item['type'])) {
$item['name'] = $key;
}
// Handle special instructions in the form.
if (strpos($key, '@') !== false) {
// Remove @ from the start and the end. Key syntax
`import@2` is supported to allow multiple operations of the same type.
$list = explode('-',
preg_replace('/^(@*)?([^@]+)(@\d*)?$/', '\2', $key),
2);
$action = array_shift($list);
$property = array_shift($list);
switch ($action) {
case 'unset':
unset($items[$key]);
if (empty($items)) {
return null;
}
break;
case 'import':
unset($items[$key]);
$this->doImport($item, $path);
break;
case 'ordering':
$ordering = $item;
unset($items[$key]);
break;
default:
$this->dynamic[implode('/',
$path)][$property] = ['action' => $action, 'params'
=> $item];
}
} elseif (\is_array($item)) {
// Recursively initialize form.
$newPath = array_merge($path, [$key]);
$location = $this->deepInit($item, $newPath);
if ($location) {
$order[$key] = $location;
} elseif ($location === null) {
unset($items[$key]);
}
}
}
unset($item);
if ($order) {
// Reorder fields if needed.
$items = $this->doReorder($items, $order);
}
return $ordering;
}
/**
* @param array|string $value
* @return array|null
*/
protected function loadImport($value)
{
$type = !\is_string($value) ? (!isset($value['type']) ?
null : $value['type']) : $value;
$field = 'form';
if ($type && strpos($type, ':') !== false) {
list ($type, $field) = explode(':', $type, 2);
}
if (!$type && !$field) {
return null;
}
if ($type) {
$files = $this->getFiles($type,
isset($value['context']) ? $value['context'] : null);
if (!$files) {
return null;
}
/** @var BlueprintForm $blueprint */
$blueprint = new static($files);
$blueprint->setContext($this->context)->setOverrides($this->overrides)->load();
} else {
$blueprint = $this;
}
$import = $blueprint->get($field);
return \is_array($import) ? $import : null;
}
/**
* @param array|string $value
* @param array $path
*/
protected function doImport($value, array &$path)
{
$imported = $this->loadImport($value);
if ($imported) {
$this->deepInit($imported, $path);
$name = implode('/', $path);
$this->embed($name, $imported, '/', false);
}
}
/**
* Internal function that handles loading extended blueprints.
*
* @param array $files
* @param string|array|null $extends
* @return array
*/
protected function doLoad(array $files, $extends = null)
{
$filename = array_shift($files);
$content = $this->loadFile($filename);
$key = '';
if (isset($content['extends@'])) {
$key = 'extends@';
} elseif (isset($content['@extends'])) {
$key = '@extends';
} elseif (isset($content['@extends@'])) {
$key = '@extends@';
}
$override = (bool)$extends;
$extends = (array)($key && !$extends ? $content[$key] :
$extends);
unset($content['extends@'],
$content['@extends'], $content['@extends@']);
$data = $extends ? $this->doExtend($filename, $files, $extends,
$override) : [];
$data[] = $content;
return $data;
}
/**
* Internal function to recursively load extended blueprints.
*
* @param string $filename
* @param array $parents
* @param array $extends
* @return array
*/
protected function doExtend($filename, array $parents, array $extends,
$override = false)
{
if (\is_string(key($extends))) {
$extends = [$extends];
}
$data = [[]];
foreach ($extends as $value) {
// Accept array of type and context or a string.
$type = !\is_string($value) ? (!isset($value['type'])
? null : $value['type']) : $value;
if (!$type) {
continue;
}
if ($type === '@parent' || $type ===
'parent@') {
if (!$parents) {
throw new RuntimeException("Parent blueprint
missing for '{$filename}'");
}
$files = $parents;
} else {
$files = $this->getFiles($type,
isset($value['context']) ? $value['context'] : null);
if ($override && !$files) {
throw new RuntimeException("Blueprint
'{$type}' missing for '{$filename}'");
}
// Detect extend loops.
if ($files && array_intersect($files, $parents)) {
// Let's check if user really meant extends@:
parent@.
$index = \array_search($filename, $files, true);
if ($index !== false) {
// We want to grab only the parents of the file
which is currently being loaded.
$files = \array_slice($files, $index + 1);
}
if ($files !== $parents) {
throw new RuntimeException("Loop detected
while extending blueprint file '{$filename}'");
}
if (!$parents) {
throw new RuntimeException("Parent blueprint
missing for '{$filename}'");
}
}
}
if ($files) {
$data[] = $this->doLoad($files);
}
}
// TODO: In PHP 5.6+ use array_merge(...$data);
return call_user_func_array('array_merge', $data);
}
/**
* Internal function to reorder items.
*
* @param array $items
* @param array $keys
* @return array
*/
protected function doReorder(array $items, array $keys)
{
$reordered = array_keys($items);
foreach ($keys as $item => $ordering) {
if ((string)(int) $ordering === (string) $ordering) {
$location = array_search($item, $reordered, true);
$rel = array_splice($reordered, $location, 1);
array_splice($reordered, $ordering, 0, $rel);
} elseif (isset($items[$ordering])) {
$location = array_search($item, $reordered, true);
$rel = array_splice($reordered, $location, 1);
$location = array_search($ordering, $reordered, true);
array_splice($reordered, $location + 1, 0, $rel);
}
}
return array_merge(array_flip($reordered), $items);
}
}
Blueprints/src/Blueprints.php000064400000000373151162104040012317
0ustar00<?php
namespace RocketTheme\Toolbox\Blueprints;
/**
* Deprecated class, use BlueprintSchema instead.
*
* @package RocketTheme\Toolbox\Blueprints
* @author RocketTheme
* @license MIT
* @deprecated
*/
class Blueprints extends BlueprintSchema {}
Blueprints/src/BlueprintSchema.php000064400000050327151162104040013261
0ustar00<?php
namespace RocketTheme\Toolbox\Blueprints;
/**
* BlueprintSchema is used to define a data structure.
*
* @package RocketTheme\Toolbox\Blueprints
* @author RocketTheme
* @license MIT
*/
class BlueprintSchema
{
/** @var array */
protected $items = [];
/** @var array */
protected $rules = [];
/** @var array */
protected $nested = [];
/** @var array */
protected $dynamic = [];
/** @var array */
protected $filter = ['validation' => true];
/** @var array */
protected $ignoreFormKeys = ['fields' => 1];
/** @var array */
protected $types = [];
/**
* Constructor.
*
* @param array $serialized Serialized content if available.
*/
public function __construct($serialized = null)
{
if (\is_array($serialized) && !empty($serialized)) {
$this->items = (array) $serialized['items'];
$this->rules = (array) $serialized['rules'];
$this->nested = (array) $serialized['nested'];
$this->dynamic = (array) $serialized['dynamic'];
$this->filter = (array) $serialized['filter'];
}
}
/**
* @param array $types
* @return $this
*/
public function setTypes(array $types)
{
$this->types = $types;
return $this;
}
/**
* Restore Blueprints object.
*
* @param array $serialized
* @return static
*/
public static function restore(array $serialized)
{
return new static($serialized);
}
/**
* Initialize blueprints with its dynamic fields.
*
* @return $this
*/
public function init()
{
foreach ($this->dynamic as $key => $data) {
$field = &$this->items[$key];
foreach ($data as $property => $call) {
$action = 'dynamic' .
ucfirst($call['action']);
if (method_exists($this, $action)) {
$this->{$action}($field, $property, $call);
}
}
}
return $this;
}
/**
* Set filter for inherited properties.
*
* @param array $filter List of field names to be inherited.
*/
public function setFilter(array $filter)
{
$this->filter = array_flip($filter);
}
/**
* Get value by using dot notation for nested arrays/objects.
*
* @example $value =
$data->get('this.is.my.nested.variable');
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
*
* @return mixed Value.
*/
public function get($name, $default = null, $separator = '.')
{
$name = $separator !== '.' ? str_replace($separator,
'.', $name) : $name;
return isset($this->items[$name]) ? $this->items[$name] :
$default;
}
/**
* Set value by using dot notation for nested arrays/objects.
*
* @example $value =
$data->set('this.is.my.nested.variable', $newField);
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $value New value.
* @param string $separator Separator, defaults to '.'
*/
public function set($name, $value, $separator = '.')
{
$name = $separator !== '.' ? str_replace($separator,
'.', $name) : $name;
$this->items[$name] = $value;
$this->addProperty($name);
}
/**
* Define value by using dot notation for nested arrays/objects.
*
* @example $value =
$data->set('this.is.my.nested.variable', true);
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $value New value.
* @param string $separator Separator, defaults to '.'
*/
public function def($name, $value, $separator = '.')
{
$this->set($name, $this->get($name, $value, $separator),
$separator);
}
/**
* @return array
* @deprecated
*/
public function toArray()
{
return $this->getState();
}
/**
* Convert object into an array.
*
* @return array
*/
public function getState()
{
return [
'items' => $this->items,
'rules' => $this->rules,
'nested' => $this->nested,
'dynamic' => $this->dynamic,
'filter' => $this->filter
];
}
/**
* Get nested structure containing default values defined in the
blueprints.
*
* Fields without default value are ignored in the list.
*
* @return array
*/
public function getDefaults()
{
return $this->buildDefaults($this->nested);
}
/**
* Embed an array to the blueprint.
*
* @param $name
* @param array $value
* @param string $separator
* @param bool $merge Merge fields instead replacing them.
* @return $this
*/
public function embed($name, array $value, $separator = '.',
$merge = false)
{
if (isset($value['rules'])) {
$this->rules = array_merge($this->rules,
$value['rules']);
}
$name = $separator !== '.' ? str_replace($separator,
'.', $name) : $name;
if (isset($value['form'])) {
$form = array_diff_key($value['form'],
['fields' => 1, 'field' => 1]);
} else {
$form = [];
}
$items = isset($this->items[$name]) ? $this->items[$name] :
['type' => '_root', 'form_field' =>
false];
$this->items[$name] = $items;
$this->addProperty($name);
$prefix = $name ? $name . '.' : '';
$params = array_intersect_key($form, $this->filter);
$location = [$name];
if (isset($value['form']['field'])) {
$this->parseFormField($name,
$value['form']['field'], $params, $prefix,
'', $merge, $location);
} elseif (isset($value['form']['fields'])) {
$this->parseFormFields($value['form']['fields'],
$params, $prefix, '', $merge, $location);
}
$this->items[$name] += ['form' => $form];
return $this;
}
/**
* Merge two arrays by using blueprints.
*
* @param array $data1
* @param array $data2
* @param string $name Optional
* @param string $separator Optional
* @return array
*/
public function mergeData(array $data1, array $data2, $name = null,
$separator = '.')
{
$nested = $this->getNested($name, $separator);
if (!\is_array($nested)) {
$nested = [];
}
return $this->mergeArrays($data1, $data2, $nested);
}
/**
* Get the property with given path.
*
* @param string $path
* @param string $separator
* @return mixed
*/
public function getProperty($path = null, $separator = '.')
{
$name = $this->getPropertyName($path, $separator);
$property = $this->get($name);
$nested = $this->getNested($name);
return $this->getPropertyRecursion($property, $nested);
}
/**
* Returns name of the property with given path.
*
* @param string $path
* @param string $separator
* @return string
*/
public function getPropertyName($path = null, $separator =
'.')
{
$parts = explode($separator, $path);
$nested = $this->nested;
$result = [];
while (($part = array_shift($parts)) !== null) {
if (!isset($nested[$part])) {
if (isset($nested['*'])) {
$part = '*';
} else {
return implode($separator, array_merge($result,
[$part], $parts));
}
}
$result[] = $part;
$nested = $nested[$part];
}
return implode('.', $result);
}
/**
* Return data fields that do not exist in blueprints.
*
* @param array $data
* @param string $prefix
* @return array
*/
public function extra(array $data, $prefix = '')
{
$rules = $this->nested;
// Drill down to prefix level
if (!empty($prefix)) {
$parts = explode('.', trim($prefix, '.'));
foreach ($parts as $part) {
$rules = isset($rules[$part]) ? $rules[$part] : [];
}
}
// Check if the form cannot have extra fields.
if (isset($rules[''])) {
$rule = $this->items[''];
if (isset($rule['type']) &&
$rule['type'] !== '_root') {
return [];
}
}
return $this->extraArray($data, $rules, $prefix);
}
/**
* Get the property with given path.
*
* @param $property
* @param $nested
* @return mixed
*/
protected function getPropertyRecursion($property, $nested)
{
if (empty($nested) || !\is_array($nested) ||
!isset($property['type'])) {
return $property;
}
if ($property['type'] === '_root') {
foreach ($nested as $key => $value) {
if ($key === '') {
continue;
}
$name = \is_array($value) ? $key : $value;
$property['fields'][$key] =
$this->getPropertyRecursion($this->get($name), $value);
}
} elseif ($property['type'] === '_parent' ||
!empty($property['array'])) {
foreach ($nested as $key => $value) {
$name = \is_array($value) ?
"{$property['name']}.{$key}" : $value;
$property['fields'][$key] =
$this->getPropertyRecursion($this->get($name), $value);
}
}
return $property;
}
/**
* Get property from the definition.
*
* @param string $path Comma separated path to the property.
* @param string $separator
* @return array|string|null
* @internal
*/
protected function getNested($path = null, $separator = '.')
{
if (!$path) {
return $this->nested;
}
$parts = explode($separator, $path);
$item = array_pop($parts);
$nested = $this->nested;
foreach ($parts as $part) {
if (!isset($nested[$part])) {
$part = '*';
if (!isset($nested[$part])) {
return [];
}
}
$nested = $nested[$part];
}
return isset($nested[$item]) ? $nested[$item] :
(isset($nested['*']) ? $nested['*'] : null);
}
/**
* @param array $nested
* @return array
*/
protected function buildDefaults(array $nested)
{
$defaults = [];
foreach ($nested as $key => $value) {
if ($key === '*') {
// TODO: Add support for adding defaults to collections.
continue;
}
if (\is_array($value)) {
// Recursively fetch the items.
$list = $this->buildDefaults($value);
// Only return defaults if there are any.
if (!empty($list)) {
$defaults[$key] = $list;
}
} else {
// We hit a field; get default from it if it exists.
$item = $this->get($value);
// Only return default value if it exists.
if (isset($item['default'])) {
$defaults[$key] = $item['default'];
}
}
}
return $defaults;
}
/**
* @param array $data1
* @param array $data2
* @param array $rules
* @return array
* @internal
*/
protected function mergeArrays(array $data1, array $data2, array
$rules)
{
foreach ($data2 as $key => $field) {
$val = isset($rules[$key]) ? $rules[$key] : null;
$rule = \is_string($val) ? $this->items[$val] : null;
if ((array_key_exists($key, $data1) &&
\is_array($data1[$key]) && \is_array($field) &&
\is_array($val) && !isset($val['*']))
|| (!empty($rule['type']) &&
strpos($rule['type'], '_') === 0)) {
// Array has been defined in blueprints and is not a
collection of items.
$data1[$key] = $this->mergeArrays($data1[$key], $field,
$val);
} else {
// Otherwise just take value from the data2.
$data1[$key] = $field;
}
}
return $data1;
}
/**
* Gets all field definitions from the blueprints.
*
* @param array $fields Fields to parse.
* @param array $params Property parameters.
* @param string $prefix Property prefix.
* @param string $parent Parent property.
* @param bool $merge Merge fields instead replacing them.
* @param array $formPath
*/
protected function parseFormFields(array $fields, array $params,
$prefix = '', $parent = '', $merge = false, array
$formPath = [])
{
if (isset($fields['type']) &&
!\is_array($fields['type'])) {
return;
}
// Go though all the fields in current level.
foreach ($fields as $key => $field) {
$this->parseFormField($key, $field, $params, $prefix,
$parent, $merge, $formPath);
}
}
/**
* @param string $key
* @param array $field
* @param array $params
* @param string $prefix
* @param string $parent
* @param bool $merge
* @param array $formPath
*/
protected function parseFormField($key, array $field, array $params,
$prefix = '', $parent = '', $merge = false, array
$formPath = [])
{
// Skip illegal field (needs to be an array).
if (!\is_array($field)) {
return;
}
$key = $this->getFieldKey($key, $prefix, $parent);
$newPath = array_merge($formPath, [$key]);
$properties = array_diff_key($field, $this->ignoreFormKeys) +
$params;
$properties['name'] = $key;
// Set default properties for the field type.
$type = isset($properties['type']) ?
$properties['type'] : '';
if (isset($this->types[$type])) {
$properties += $this->types[$type];
}
// Merge properties with existing ones.
if ($merge && isset($this->items[$key])) {
$properties += $this->items[$key];
}
$isInputField = !isset($properties['input@']) ||
$properties['input@'];
$propertyExists = isset($this->items[$key]);
if (!$isInputField) {
// Remove property if it exists.
if ($propertyExists) {
$this->removeProperty($key);
}
} elseif (!$propertyExists) {
// Add missing property.
$this->addProperty($key);
}
if (isset($field['fields'])) {
// Recursively get all the nested fields.
$isArray = !empty($properties['array']);
$newParams = array_intersect_key($properties,
$this->filter);
$this->parseFormFields($field['fields'],
$newParams, $prefix, $key . ($isArray ? '.*': ''),
$merge, $newPath);
} else {
if (!isset($this->items[$key])) {
// Add parent rules.
$path = explode('.', $key);
array_pop($path);
$parent = '';
foreach ($path as $part) {
$parent .= ($parent ? '.' : '') .
$part;
if (!isset($this->items[$parent])) {
$this->items[$parent] = ['type' =>
'_parent', 'name' => $parent, 'form_field'
=> false];
}
}
}
if ($isInputField) {
$this->parseProperties($key, $properties);
}
}
if ($isInputField) {
$this->items[$key] = $properties;
}
}
protected function getFieldKey($key, $prefix, $parent)
{
// Set name from the array key.
if (strpos($key[0], '.') === 0) {
return ($parent ?: rtrim($prefix, '.')) . $key;
}
return $prefix . $key;
}
protected function parseProperties($key, array &$properties)
{
$key = ltrim($key, '.');
if (!empty($properties['data'])) {
$this->dynamic[$key] = $properties['data'];
}
foreach ($properties as $name => $value) {
if (strpos($name[0], '@') !== false) {
$list = explode('-', trim($name, '@'),
2);
$action = array_shift($list);
$property = array_shift($list);
$this->dynamic[$key][$property] = ['action'
=> $action, 'params' => $value];
}
}
// Initialize predefined validation rule.
if (isset($properties['validate']['rule'])) {
$properties['validate'] +=
$this->getRule($properties['validate']['rule']);
}
}
/**
* Add property to the definition.
*
* @param string $path Comma separated path to the property.
* @internal
*/
protected function addProperty($path)
{
$parts = explode('.', $path);
$item = array_pop($parts);
$nested = &$this->nested;
foreach ($parts as $part) {
if (!isset($nested[$part]) || !\is_array($nested[$part])) {
$nested[$part] = [];
}
$nested = &$nested[$part];
}
if (!isset($nested[$item])) {
$nested[$item] = $path;
}
}
/**
* Remove property to the definition.
*
* @param string $path Comma separated path to the property.
* @internal
*/
protected function removeProperty($path)
{
$parts = explode('.', $path);
$item = array_pop($parts);
$nested = &$this->nested;
foreach ($parts as $part) {
if (!isset($nested[$part]) || !\is_array($nested[$part])) {
return;
}
$nested = &$nested[$part];
}
if (isset($nested[$item])) {
unset($nested[$item]);
}
}
/**
* @param $rule
* @return array
* @internal
*/
protected function getRule($rule)
{
if (isset($this->rules[$rule]) &&
\is_array($this->rules[$rule])) {
return $this->rules[$rule];
}
return [];
}
/**
* @param array $data
* @param array $rules
* @param string $prefix
* @return array
* @internal
*/
protected function extraArray(array $data, array $rules, $prefix)
{
$array = [];
foreach ($data as $key => $field) {
$val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
$rule = \is_string($val) ? $this->items[$val] : null;
if ($rule || isset($val['*'])) {
// Item has been defined in blueprints.
} elseif (\is_array($field) && \is_array($val)) {
// Array has been defined in blueprints.
$array += $this->extraArray($field, $val, $prefix . $key
. '.');
} else {
// Undefined/extra item.
$array[$prefix.$key] = $field;
}
}
return $array;
}
/**
* @param array $field
* @param string $property
* @param array $call
*/
protected function dynamicData(array &$field, $property, array
$call)
{
$params = $call['params'];
if (\is_array($params)) {
$function = array_shift($params);
} else {
$function = $params;
$params = [];
}
$list = explode('::', $function, 2);
$f = array_pop($list);
$o = array_pop($list);
if (!$o) {
if (\function_exists($f)) {
$data = \call_user_func_array($f, $params);
}
} else {
if (method_exists($o, $f)) {
$data = \call_user_func_array(array($o, $f), $params);
}
}
// If function returns a value,
if (isset($data)) {
if (\is_array($data) && isset($field[$property])
&& \is_array($field[$property])) {
// Combine field and @data-field together.
$field[$property] += $data;
} else {
// Or create/replace field with @data-field.
$field[$property] = $data;
}
}
}
}
Compat/src/Yaml/Exception/ExceptionInterface.php000064400000000704151162104040015701
0ustar00<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml\Exception;
/**
* Exception interface for all exceptions thrown by the component.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ExceptionInterface
{
}
Compat/src/Yaml/Exception/ParseException.php000064400000007002151162104040015051
0ustar00<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml\Exception;
/**
* Exception class thrown when an error occurs during parsing.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ParseException extends RuntimeException
{
private $parsedFile;
private $parsedLine;
private $snippet;
private $rawMessage;
/**
* @param string $message The error message
* @param int $parsedLine The line where the error occurred
* @param string|null $snippet The snippet of code near the
problem
* @param string|null $parsedFile The file name where the error
occurred
* @param \Exception|null $previous The previous exception
*/
public function __construct($message, $parsedLine = -1, $snippet =
null, $parsedFile = null, \Exception $previous = null)
{
$this->parsedFile = $parsedFile;
$this->parsedLine = $parsedLine;
$this->snippet = $snippet;
$this->rawMessage = $message;
$this->updateRepr();
parent::__construct($this->message, 0, $previous);
}
/**
* Gets the snippet of code near the error.
*
* @return string The snippet of code
*/
public function getSnippet()
{
return $this->snippet;
}
/**
* Sets the snippet of code near the error.
*
* @param string $snippet The code snippet
*/
public function setSnippet($snippet)
{
$this->snippet = $snippet;
$this->updateRepr();
}
/**
* Gets the filename where the error occurred.
*
* This method returns null if a string is parsed.
*
* @return string The filename
*/
public function getParsedFile()
{
return $this->parsedFile;
}
/**
* Sets the filename where the error occurred.
*
* @param string $parsedFile The filename
*/
public function setParsedFile($parsedFile)
{
$this->parsedFile = $parsedFile;
$this->updateRepr();
}
/**
* Gets the line where the error occurred.
*
* @return int The file line
*/
public function getParsedLine()
{
return $this->parsedLine;
}
/**
* Sets the line where the error occurred.
*
* @param int $parsedLine The file line
*/
public function setParsedLine($parsedLine)
{
$this->parsedLine = $parsedLine;
$this->updateRepr();
}
private function updateRepr()
{
$this->message = $this->rawMessage;
$dot = false;
if ('.' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$dot = true;
}
if (null !== $this->parsedFile) {
if (\PHP_VERSION_ID >= 50400) {
$jsonOptions = JSON_UNESCAPED_SLASHES |
JSON_UNESCAPED_UNICODE;
} else {
$jsonOptions = 0;
}
$this->message .= sprintf(' in %s',
json_encode($this->parsedFile, $jsonOptions));
}
if ($this->parsedLine >= 0) {
$this->message .= sprintf(' at line %d',
$this->parsedLine);
}
if ($this->snippet) {
$this->message .= sprintf(' (near
"%s")', $this->snippet);
}
if ($dot) {
$this->message .= '.';
}
}
}
Compat/src/Yaml/Exception/RuntimeException.php000064400000000756151162104040015433
0ustar00<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml\Exception;
/**
* Exception class thrown when an error occurs during parsing.
*
* @author Romain Neutron <imprec@gmail.com>
*/
class RuntimeException extends \RuntimeException implements
ExceptionInterface
{
}
Compat/src/Yaml/Inline.php000064400000043025151162104040011405
0ustar00<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml;
use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;
/**
* Inline implements a YAML parser/dumper for the YAML inline syntax.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Inline
{
const REGEX_QUOTED_STRING =
'(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
private static $exceptionOnInvalidType = false;
private static $objectSupport = false;
private static $objectForMap = false;
/**
* Converts a YAML string to a PHP value.
*
* @param string $value A YAML string
* @param bool $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
* @param bool $objectSupport True if object support is
enabled, false otherwise
* @param bool $objectForMap True if maps should return a
stdClass instead of array()
* @param array $references Mapping of variable names to
values
*
* @return mixed A PHP value
*
* @throws ParseException
*/
public static function parse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false, $references = array())
{
self::$exceptionOnInvalidType = $exceptionOnInvalidType;
self::$objectSupport = $objectSupport;
self::$objectForMap = $objectForMap;
$value = trim($value);
if ('' === $value) {
return '';
}
if (2 /* MB_OVERLOAD_STRING */ & (int)
ini_get('mbstring.func_overload')) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('ASCII');
}
$i = 0;
switch ($value[0]) {
case '[':
$result = self::parseSequence($value, $i, $references);
++$i;
break;
case '{':
$result = self::parseMapping($value, $i, $references);
++$i;
break;
default:
$result = self::parseScalar($value, null,
array('"', "'"), $i, true, $references);
}
// some comments are allowed at the end
if (preg_replace('/\s+#.*$/A', '',
substr($value, $i))) {
throw new ParseException(sprintf('Unexpected characters
near "%s".', substr($value, $i)));
}
if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding);
}
return $result;
}
/**
* Check if given array is hash or just normal indexed array.
*
* @internal
*
* @param array $value The PHP array to check
*
* @return bool true if value is hash array, false otherwise
*/
public static function isHash(array $value)
{
$expectedKey = 0;
foreach ($value as $key => $val) {
if ($key !== $expectedKey++) {
return true;
}
}
return false;
}
/**
* Parses a YAML scalar.
*
* @param string $scalar
* @param string[] $delimiters
* @param string[] $stringDelimiters
* @param int &$i
* @param bool $evaluate
* @param array $references
*
* @return string
*
* @throws ParseException When malformed inline YAML string is parsed
*
* @internal
*/
public static function parseScalar($scalar, $delimiters = null,
$stringDelimiters = array('"', "'"), &$i
= 0, $evaluate = true, $references = array())
{
if (in_array($scalar[$i], $stringDelimiters)) {
// quoted scalar
$output = self::parseQuotedScalar($scalar, $i);
if (null !== $delimiters) {
$tmp = ltrim(substr($scalar, $i), ' ');
if (!in_array($tmp[0], $delimiters)) {
throw new ParseException(sprintf('Unexpected
characters (%s).', substr($scalar, $i)));
}
}
} else {
// "normal" string
if (!$delimiters) {
$output = substr($scalar, $i);
$i += strlen($output);
// remove comments
if (Parser::preg_match('/[ \t]+#/', $output,
$match, PREG_OFFSET_CAPTURE)) {
$output = substr($output, 0, $match[0][1]);
}
} elseif
(Parser::preg_match('/^(.+?)('.implode('|',
$delimiters).')/', substr($scalar, $i), $match)) {
$output = $match[1];
$i += strlen($output);
} else {
throw new ParseException(sprintf('Malformed inline
YAML string: %s.', $scalar));
}
// a non-quoted string cannot start with @ or ` (reserved) nor
with a scalar indicator (| or >)
if ($output && ('@' === $output[0] ||
'`' === $output[0] || '|' === $output[0] ||
'>' === $output[0])) {
@trigger_error(sprintf('Not quoting the scalar
"%s" starting with "%s" is deprecated since Symfony 2.8
and will throw a ParseException in 3.0.', $output, $output[0]),
E_USER_DEPRECATED);
// to be thrown in 3.0
// throw new ParseException(sprintf('The reserved
indicator "%s" cannot start a plain scalar; you need to quote the
scalar.', $output[0]));
}
if ($evaluate) {
$output = self::evaluateScalar($output, $references);
}
}
return $output;
}
/**
* Parses a YAML quoted scalar.
*
* @param string $scalar
* @param int &$i
*
* @return string
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseQuotedScalar($scalar, &$i)
{
if
(!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au',
substr($scalar, $i), $match)) {
throw new ParseException(sprintf('Malformed inline YAML
string: %s.', substr($scalar, $i)));
}
$output = substr($match[0], 1, strlen($match[0]) - 2);
$unescaper = new Unescaper();
if ('"' == $scalar[$i]) {
$output = $unescaper->unescapeDoubleQuotedString($output);
} else {
$output = $unescaper->unescapeSingleQuotedString($output);
}
$i += strlen($match[0]);
return $output;
}
/**
* Parses a YAML sequence.
*
* @param string $sequence
* @param int &$i
* @param array $references
*
* @return array
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseSequence($sequence, &$i = 0,
$references = array())
{
$output = array();
$len = strlen($sequence);
++$i;
// [foo, bar, ...]
while ($i < $len) {
switch ($sequence[$i]) {
case '[':
// nested sequence
$output[] = self::parseSequence($sequence, $i,
$references);
break;
case '{':
// nested mapping
$output[] = self::parseMapping($sequence, $i,
$references);
break;
case ']':
return $output;
case ',':
case ' ':
break;
default:
$isQuoted = in_array($sequence[$i],
array('"', "'"));
$value = self::parseScalar($sequence,
array(',', ']'), array('"',
"'"), $i, true, $references);
// the value can be an array if a reference has been
resolved to an array var
if (!is_array($value) && !$isQuoted &&
false !== strpos($value, ': ')) {
// embedded mapping?
try {
$pos = 0;
$value =
self::parseMapping('{'.$value.'}', $pos, $references);
} catch (\InvalidArgumentException $e) {
// no, it's not
}
}
$output[] = $value;
--$i;
}
++$i;
}
throw new ParseException(sprintf('Malformed inline YAML
string: %s.', $sequence));
}
/**
* Parses a YAML mapping.
*
* @param string $mapping
* @param int &$i
* @param array $references
*
* @return array|\stdClass
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseMapping($mapping, &$i = 0, $references
= array())
{
$output = array();
$len = strlen($mapping);
++$i;
$allowOverwrite = false;
// {foo: bar, bar:foo, ...}
while ($i < $len) {
switch ($mapping[$i]) {
case ' ':
case ',':
++$i;
continue 2;
case '}':
if (self::$objectForMap) {
return (object) $output;
}
return $output;
}
// key
$key = self::parseScalar($mapping, array(':', '
'), array('"', "'"), $i, false);
if ('<<' === $key) {
$allowOverwrite = true;
}
// value
$done = false;
while ($i < $len) {
switch ($mapping[$i]) {
case '[':
// nested sequence
$value = self::parseSequence($mapping, $i,
$references);
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since
lines
// are processed sequentially.
// But overwriting is allowed when a merge node is
used in current block.
if ('<<' === $key) {
foreach ($value as $parsedValue) {
$output += $parsedValue;
}
} elseif ($allowOverwrite || !isset($output[$key]))
{
$output[$key] = $value;
}
$done = true;
break;
case '{':
// nested mapping
$value = self::parseMapping($mapping, $i,
$references);
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since
lines
// are processed sequentially.
// But overwriting is allowed when a merge node is
used in current block.
if ('<<' === $key) {
$output += $value;
} elseif ($allowOverwrite || !isset($output[$key]))
{
$output[$key] = $value;
}
$done = true;
break;
case ':':
case ' ':
break;
default:
$value = self::parseScalar($mapping,
array(',', '}'), array('"',
"'"), $i, true, $references);
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since
lines
// are processed sequentially.
// But overwriting is allowed when a merge node is
used in current block.
if ('<<' === $key) {
$output += $value;
} elseif ($allowOverwrite || !isset($output[$key]))
{
$output[$key] = $value;
}
$done = true;
--$i;
}
++$i;
if ($done) {
continue 2;
}
}
}
throw new ParseException(sprintf('Malformed inline YAML
string: %s.', $mapping));
}
/**
* Evaluates scalars and replaces magic values.
*
* @param string $scalar
* @param array $references
*
* @return mixed The evaluated YAML string
*
* @throws ParseException when object parsing support was disabled and
the parser detected a PHP object or when a reference could not be resolved
*/
private static function evaluateScalar($scalar, $references = array())
{
$scalar = trim($scalar);
$scalarLower = strtolower($scalar);
if (0 === strpos($scalar, '*')) {
if (false !== $pos = strpos($scalar, '#')) {
$value = substr($scalar, 1, $pos - 2);
} else {
$value = substr($scalar, 1);
}
// an unquoted *
if (false === $value || '' === $value) {
throw new ParseException('A reference must contain at
least one character.');
}
if (!array_key_exists($value, $references)) {
throw new ParseException(sprintf('Reference
"%s" does not exist.', $value));
}
return $references[$value];
}
switch (true) {
case 'null' === $scalarLower:
case '' === $scalar:
case '~' === $scalar:
return;
case 'true' === $scalarLower:
return true;
case 'false' === $scalarLower:
return false;
// Optimise for returning strings.
case '+' === $scalar[0] || '-' ===
$scalar[0] || '.' === $scalar[0] || '!' === $scalar[0]
|| is_numeric($scalar[0]):
switch (true) {
case 0 === strpos($scalar, '!str'):
return (string) substr($scalar, 5);
case 0 === strpos($scalar, '! '):
return (int) self::parseScalar(substr($scalar, 2));
case 0 === strpos($scalar, '!php/object:'):
if (self::$objectSupport) {
return unserialize(substr($scalar, 12));
}
if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support
when parsing a YAML file has been disabled.');
}
return;
case 0 === strpos($scalar, '!!php/object:'):
if (self::$objectSupport) {
return unserialize(substr($scalar, 13));
}
if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support
when parsing a YAML file has been disabled.');
}
return;
case 0 === strpos($scalar, '!!float '):
return (float) substr($scalar, 8);
case ctype_digit($scalar):
$raw = $scalar;
$cast = (int) $scalar;
return '0' == $scalar[0] ?
octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
case '-' === $scalar[0] &&
ctype_digit(substr($scalar, 1)):
$raw = $scalar;
$cast = (int) $scalar;
return '0' == $scalar[1] ?
octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
case is_numeric($scalar):
case Parser::preg_match(self::getHexRegex(), $scalar):
return '0x' === $scalar[0].$scalar[1] ?
hexdec($scalar) : (float) $scalar;
case '.inf' === $scalarLower:
case '.nan' === $scalarLower:
return -log(0);
case '-.inf' === $scalarLower:
return log(0);
case
Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
return (float) str_replace(',',
'', $scalar);
case Parser::preg_match(self::getTimestampRegex(),
$scalar):
$timeZone = date_default_timezone_get();
date_default_timezone_set('UTC');
$time = strtotime($scalar);
date_default_timezone_set($timeZone);
return $time;
}
// no break
default:
return (string) $scalar;
}
}
/**
* Gets a regex that matches a YAML date.
*
* @return string The regular expression
*
* @see http://www.yaml.org/spec/1.2/spec.html#id2761573
*/
private static function getTimestampRegex()
{
return <<<EOF
~^
(?P<year>[0-9][0-9][0-9][0-9])
-(?P<month>[0-9][0-9]?)
-(?P<day>[0-9][0-9]?)
(?:(?:[Tt]|[ \t]+)
(?P<hour>[0-9][0-9]?)
:(?P<minute>[0-9][0-9])
:(?P<second>[0-9][0-9])
(?:\.(?P<fraction>[0-9]*))?
(?:[
\t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
(?::(?P<tz_minute>[0-9][0-9]))?))?)?
$~x
EOF;
}
/**
* Gets a regex that matches a YAML number in hexadecimal notation.
*
* @return string
*/
private static function getHexRegex()
{
return '~^0x[0-9a-f]++$~i';
}
}
Compat/src/Yaml/Parser.php000064400000100472151162104040011423
0ustar00<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml;
use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;
/**
* Parser parses YAML strings to convert them to PHP arrays.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Parser
{
const BLOCK_SCALAR_HEADER_PATTERN =
'(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments>
+#.*)?';
// BC - wrongly named
const FOLDED_SCALAR_PATTERN = self::BLOCK_SCALAR_HEADER_PATTERN;
private $offset = 0;
private $totalNumberOfLines;
private $lines = array();
private $currentLineNb = -1;
private $currentLine = '';
private $refs = array();
private $skippedLineNumbers = array();
private $locallySkippedLineNumbers = array();
/**
* @param int $offset The offset of YAML document
(used for line numbers in error messages)
* @param int|null $totalNumberOfLines The overall number of lines
being parsed
* @param int[] $skippedLineNumbers Number of comment lines that
have been skipped by the parser
*/
public function __construct($offset = 0, $totalNumberOfLines = null,
array $skippedLineNumbers = array())
{
$this->offset = $offset;
$this->totalNumberOfLines = $totalNumberOfLines;
$this->skippedLineNumbers = $skippedLineNumbers;
}
/**
* Parses a YAML string to a PHP value.
*
* @param string $value A YAML string
* @param bool $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
* @param bool $objectSupport True if object support is
enabled, false otherwise
* @param bool $objectForMap True if maps should return a
stdClass instead of array()
*
* @return mixed A PHP value
*
* @throws ParseException If the YAML is not valid
*/
public function parse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
{
if (false === preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear
to be valid UTF-8.');
}
$this->refs = array();
$mbEncoding = null;
$e = null;
$data = null;
if (2 /* MB_OVERLOAD_STRING */ & (int)
ini_get('mbstring.func_overload')) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8');
}
try {
$data = $this->doParse($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
} catch (\Exception $e) {
} catch (\Throwable $e) {
}
if (null !== $mbEncoding) {
mb_internal_encoding($mbEncoding);
}
$this->lines = array();
$this->currentLine = '';
$this->refs = array();
$this->skippedLineNumbers = array();
$this->locallySkippedLineNumbers = array();
if (null !== $e) {
throw $e;
}
return $data;
}
private function doParse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
{
$this->currentLineNb = -1;
$this->currentLine = '';
$value = $this->cleanup($value);
$this->lines = explode("\n", $value);
$this->locallySkippedLineNumbers = array();
if (null === $this->totalNumberOfLines) {
$this->totalNumberOfLines = count($this->lines);
}
$data = array();
$context = null;
$allowOverwrite = false;
while ($this->moveToNextLine()) {
if ($this->isCurrentLineEmpty()) {
continue;
}
// tab?
if ("\t" === $this->currentLine[0]) {
throw new ParseException('A YAML file cannot contain
tabs as indentation.', $this->getRealCurrentLineNb() + 1,
$this->currentLine);
}
$isRef = $mergeNode = false;
if
(self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u',
rtrim($this->currentLine), $values)) {
if ($context && 'mapping' == $context) {
throw new ParseException('You cannot define a
sequence item when in a mapping', $this->getRealCurrentLineNb() +
1, $this->currentLine);
}
$context = 'sequence';
if (isset($values['value']) &&
self::preg_match('#^&(?P<ref>[^ ]+)
*(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] =
$matches['value'];
}
// array
if (!isset($values['value']) || '' ==
trim($values['value'], ' ') || 0 ===
strpos(ltrim($values['value'], ' '), '#')) {
$data[] =
$this->parseBlock($this->getRealCurrentLineNb() + 1,
$this->getNextEmbedBlock(null, true), $exceptionOnInvalidType,
$objectSupport, $objectForMap);
} else {
if (isset($values['leadspaces'])
&&
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^
\'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u',
rtrim($values['value']), $matches)
) {
// this is a compact notation element, add to next
block and parse
$block = $values['value'];
if ($this->isNextLineIndented()) {
$block .=
"\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation()
+ strlen($values['leadspaces']) + 1);
}
$data[] =
$this->parseBlock($this->getRealCurrentLineNb(), $block,
$exceptionOnInvalidType, $objectSupport, $objectForMap);
} else {
$data[] =
$this->parseValue($values['value'], $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context);
}
}
if ($isRef) {
$this->refs[$isRef] = end($data);
}
} elseif (
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^
\'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u',
rtrim($this->currentLine), $values)
&& (false === strpos($values['key'],
' #') || in_array($values['key'][0],
array('"', "'")))
) {
if ($context && 'sequence' == $context) {
throw new ParseException('You cannot define a
mapping item when in a sequence', $this->currentLineNb + 1,
$this->currentLine);
}
$context = 'mapping';
// force correct settings
Inline::parse(null, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $this->refs);
try {
$key = Inline::parseScalar($values['key']);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() +
1);
$e->setSnippet($this->currentLine);
throw $e;
}
// Convert float keys to strings, to avoid being converted
to integers by PHP
if (is_float($key)) {
$key = (string) $key;
}
if ('<<' === $key &&
(!isset($values['value']) ||
!self::preg_match('#^&(?P<ref>[^ ]+)#u',
$values['value'], $refMatches))) {
$mergeNode = true;
$allowOverwrite = true;
if (isset($values['value']) && 0 ===
strpos($values['value'], '*')) {
$refName = substr($values['value'], 1);
if (!array_key_exists($refName, $this->refs)) {
throw new
ParseException(sprintf('Reference "%s" does not
exist.', $refName), $this->getRealCurrentLineNb() + 1,
$this->currentLine);
}
$refValue = $this->refs[$refName];
if (!is_array($refValue)) {
throw new ParseException('YAML merge keys
used with a scalar value instead of an array.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
$data += $refValue; // array union
} else {
if (isset($values['value']) &&
'' !== $values['value']) {
$value = $values['value'];
} else {
$value = $this->getNextEmbedBlock();
}
$parsed =
$this->parseBlock($this->getRealCurrentLineNb() + 1, $value,
$exceptionOnInvalidType, $objectSupport, $objectForMap);
if (!is_array($parsed)) {
throw new ParseException('YAML merge keys
used with a scalar value instead of an array.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
if (isset($parsed[0])) {
// If the value associated with the merge key
is a sequence, then this sequence is expected to contain mapping nodes
// and each of these nodes is merged in turn
according to its order in the sequence. Keys in mapping nodes earlier
// in the sequence override keys specified in
later mapping nodes.
foreach ($parsed as $parsedItem) {
if (!is_array($parsedItem)) {
throw new ParseException('Merge
items must be arrays.', $this->getRealCurrentLineNb() + 1,
$parsedItem);
}
$data += $parsedItem; // array union
}
} else {
// If the value associated with the key is a
single mapping node, each of its key/value pairs is inserted into the
// current mapping, unless the key already
exists in it.
$data += $parsed; // array union
}
}
} elseif ('<<' !== $key &&
isset($values['value']) &&
self::preg_match('#^&(?P<ref>[^ ]+)
*(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] =
$matches['value'];
}
if ($mergeNode) {
// Merge keys
} elseif (!isset($values['value']) ||
'' == trim($values['value'], ' ') || 0 ===
strpos(ltrim($values['value'], ' '), '#') ||
'<<' === $key) {
// hash
// if next line is less indented or equal, then it
means that the current value is null
if (!$this->isNextLineIndented() &&
!$this->isNextLineUnIndentedCollection()) {
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is
used in current block.
if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = null;
}
} else {
$value =
$this->parseBlock($this->getRealCurrentLineNb() + 1,
$this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport,
$objectForMap);
if ('<<' === $key) {
$this->refs[$refMatches['ref']] =
$value;
$data += $value;
} elseif ($allowOverwrite || !isset($data[$key])) {
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node
is used in current block.
$data[$key] = $value;
}
}
} else {
$value =
$this->parseValue($values['value'], $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context);
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used
in current block.
if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value;
}
}
if ($isRef) {
$this->refs[$isRef] = $data[$key];
}
} else {
// multiple documents are not supported
if ('---' === $this->currentLine) {
throw new ParseException('Multiple documents are
not supported.', $this->currentLineNb + 1, $this->currentLine);
}
// 1-liner optionally followed by newline(s)
if (is_string($value) && $this->lines[0] ===
trim($value)) {
try {
$value = Inline::parse($this->lines[0],
$exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
throw $e;
}
return $value;
}
throw new ParseException('Unable to parse.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
}
if ($objectForMap && !is_object($data) &&
'mapping' === $context) {
$object = new \stdClass();
foreach ($data as $key => $value) {
$object->$key = $value;
}
$data = $object;
}
return empty($data) ? null : $data;
}
private function parseBlock($offset, $yaml, $exceptionOnInvalidType,
$objectSupport, $objectForMap)
{
$skippedLineNumbers = $this->skippedLineNumbers;
foreach ($this->locallySkippedLineNumbers as $lineNumber) {
if ($lineNumber < $offset) {
continue;
}
$skippedLineNumbers[] = $lineNumber;
}
$parser = new self($offset, $this->totalNumberOfLines,
$skippedLineNumbers);
$parser->refs = &$this->refs;
return $parser->doParse($yaml, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
}
/**
* Returns the current line number (takes the offset into account).
*
* @return int The current line number
*/
private function getRealCurrentLineNb()
{
$realCurrentLineNumber = $this->currentLineNb +
$this->offset;
foreach ($this->skippedLineNumbers as $skippedLineNumber) {
if ($skippedLineNumber > $realCurrentLineNumber) {
break;
}
++$realCurrentLineNumber;
}
return $realCurrentLineNumber;
}
/**
* Returns the current line indentation.
*
* @return int The current line indentation
*/
private function getCurrentLineIndentation()
{
return strlen($this->currentLine) -
strlen(ltrim($this->currentLine, ' '));
}
/**
* Returns the next embed block of YAML.
*
* @param int $indentation The indent level at which the block is to
be read, or null for default
* @param bool $inSequence True if the enclosing data structure is a
sequence
*
* @return string A YAML string
*
* @throws ParseException When indentation problem are detected
*/
private function getNextEmbedBlock($indentation = null, $inSequence =
false)
{
$oldLineIndentation = $this->getCurrentLineIndentation();
$blockScalarIndentations = array();
if ($this->isBlockScalarHeader()) {
$blockScalarIndentations[] =
$this->getCurrentLineIndentation();
}
if (!$this->moveToNextLine()) {
return;
}
if (null === $indentation) {
$newIndent = $this->getCurrentLineIndentation();
$unindentedEmbedBlock =
$this->isStringUnIndentedCollectionItem();
if (!$this->isCurrentLineEmpty() && 0 === $newIndent
&& !$unindentedEmbedBlock) {
throw new ParseException('Indentation problem.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
} else {
$newIndent = $indentation;
}
$data = array();
if ($this->getCurrentLineIndentation() >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
} else {
$this->moveToPreviousLine();
return;
}
if ($inSequence && $oldLineIndentation === $newIndent
&& isset($data[0][0]) && '-' === $data[0][0]) {
// the previous line contained a dash but no item content, this
line is a sequence item with the same indentation
// and therefore no nested list or mapping
$this->moveToPreviousLine();
return;
}
$isItUnindentedCollection =
$this->isStringUnIndentedCollectionItem();
if (empty($blockScalarIndentations) &&
$this->isBlockScalarHeader()) {
$blockScalarIndentations[] =
$this->getCurrentLineIndentation();
}
$previousLineIndentation = $this->getCurrentLineIndentation();
while ($this->moveToNextLine()) {
$indent = $this->getCurrentLineIndentation();
// terminate all block scalars that are more indented than the
current line
if (!empty($blockScalarIndentations) && $indent <
$previousLineIndentation && '' !==
trim($this->currentLine)) {
foreach ($blockScalarIndentations as $key =>
$blockScalarIndentation) {
if ($blockScalarIndentation >=
$this->getCurrentLineIndentation()) {
unset($blockScalarIndentations[$key]);
}
}
}
if (empty($blockScalarIndentations) &&
!$this->isCurrentLineComment() &&
$this->isBlockScalarHeader()) {
$blockScalarIndentations[] =
$this->getCurrentLineIndentation();
}
$previousLineIndentation = $indent;
if ($isItUnindentedCollection &&
!$this->isCurrentLineEmpty() &&
!$this->isStringUnIndentedCollectionItem() && $newIndent ===
$indent) {
$this->moveToPreviousLine();
break;
}
if ($this->isCurrentLineBlank()) {
$data[] = substr($this->currentLine, $newIndent);
continue;
}
// we ignore "comment" lines only when we are not
inside a scalar block
if (empty($blockScalarIndentations) &&
$this->isCurrentLineComment()) {
// remember ignored comment lines (they are used later in
nested
// parser calls to determine real line numbers)
//
// CAUTION: beware to not populate the global property here
as it
// will otherwise influence the getRealCurrentLineNb() call
here
// for consecutive comment lines and subsequent embedded
blocks
$this->locallySkippedLineNumbers[] =
$this->getRealCurrentLineNb();
continue;
}
if ($indent >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
} elseif (0 == $indent) {
$this->moveToPreviousLine();
break;
} else {
throw new ParseException('Indentation problem.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
}
return implode("\n", $data);
}
/**
* Moves the parser to the next line.
*
* @return bool
*/
private function moveToNextLine()
{
if ($this->currentLineNb >= count($this->lines) - 1) {
return false;
}
$this->currentLine = $this->lines[++$this->currentLineNb];
return true;
}
/**
* Moves the parser to the previous line.
*
* @return bool
*/
private function moveToPreviousLine()
{
if ($this->currentLineNb < 1) {
return false;
}
$this->currentLine = $this->lines[--$this->currentLineNb];
return true;
}
/**
* Parses a YAML value.
*
* @param string $value A YAML value
* @param bool $exceptionOnInvalidType True if an exception must be
thrown on invalid types false otherwise
* @param bool $objectSupport True if object support is
enabled, false otherwise
* @param bool $objectForMap True if maps should return a
stdClass instead of array()
* @param string $context The parser context (either
sequence or mapping)
*
* @return mixed A PHP value
*
* @throws ParseException When reference does not exist
*/
private function parseValue($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context)
{
if (0 === strpos($value, '*')) {
if (false !== $pos = strpos($value, '#')) {
$value = substr($value, 1, $pos - 2);
} else {
$value = substr($value, 1);
}
if (!array_key_exists($value, $this->refs)) {
throw new ParseException(sprintf('Reference
"%s" does not exist.', $value), $this->currentLineNb + 1,
$this->currentLine);
}
return $this->refs[$value];
}
if
(self::preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/',
$value, $matches)) {
$modifiers = isset($matches['modifiers']) ?
$matches['modifiers'] : '';
return
$this->parseBlockScalar($matches['separator'],
preg_replace('#\d+#', '', $modifiers), (int)
abs($modifiers));
}
try {
$parsedValue = Inline::parse($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $this->refs);
if ('mapping' === $context &&
'"' !== $value[0] && "'" !==
$value[0] && '[' !== $value[0] && '{'
!== $value[0] && '!' !== $value[0] && false !==
strpos($parsedValue, ': ')) {
@trigger_error(sprintf('Using a colon in the unquoted
mapping value "%s" in line %d is deprecated since Symfony 2.8 and
will throw a ParseException in 3.0.', $value,
$this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
// to be thrown in 3.0
// throw new ParseException('A colon cannot be used in
an unquoted mapping value.');
}
return $parsedValue;
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
throw $e;
}
}
/**
* Parses a block scalar.
*
* @param string $style The style indicator that was used to
begin this block scalar (| or >)
* @param string $chomping The chomping indicator that was used to
begin this block scalar (+ or -)
* @param int $indentation The indentation indicator that was used
to begin this block scalar
*
* @return string The text value
*/
private function parseBlockScalar($style, $chomping = '',
$indentation = 0)
{
$notEOF = $this->moveToNextLine();
if (!$notEOF) {
return '';
}
$isCurrentLineBlank = $this->isCurrentLineBlank();
$blockLines = array();
// leading blank lines are consumed before determining indentation
while ($notEOF && $isCurrentLineBlank) {
// newline only if not EOF
if ($notEOF = $this->moveToNextLine()) {
$blockLines[] = '';
$isCurrentLineBlank = $this->isCurrentLineBlank();
}
}
// determine indentation if not specified
if (0 === $indentation) {
if (self::preg_match('/^ +/', $this->currentLine,
$matches)) {
$indentation = strlen($matches[0]);
}
}
if ($indentation > 0) {
$pattern = sprintf('/^ {%d}(.*)$/', $indentation);
while (
$notEOF && (
$isCurrentLineBlank ||
self::preg_match($pattern, $this->currentLine,
$matches)
)
) {
if ($isCurrentLineBlank &&
strlen($this->currentLine) > $indentation) {
$blockLines[] = substr($this->currentLine,
$indentation);
} elseif ($isCurrentLineBlank) {
$blockLines[] = '';
} else {
$blockLines[] = $matches[1];
}
// newline only if not EOF
if ($notEOF = $this->moveToNextLine()) {
$isCurrentLineBlank = $this->isCurrentLineBlank();
}
}
} elseif ($notEOF) {
$blockLines[] = '';
}
if ($notEOF) {
$blockLines[] = '';
$this->moveToPreviousLine();
} elseif (!$notEOF &&
!$this->isCurrentLineLastLineInDocument()) {
$blockLines[] = '';
}
// folded style
if ('>' === $style) {
$text = '';
$previousLineIndented = false;
$previousLineBlank = false;
for ($i = 0, $blockLinesCount = count($blockLines); $i <
$blockLinesCount; ++$i) {
if ('' === $blockLines[$i]) {
$text .= "\n";
$previousLineIndented = false;
$previousLineBlank = true;
} elseif (' ' === $blockLines[$i][0]) {
$text .= "\n".$blockLines[$i];
$previousLineIndented = true;
$previousLineBlank = false;
} elseif ($previousLineIndented) {
$text .= "\n".$blockLines[$i];
$previousLineIndented = false;
$previousLineBlank = false;
} elseif ($previousLineBlank || 0 === $i) {
$text .= $blockLines[$i];
$previousLineIndented = false;
$previousLineBlank = false;
} else {
$text .= ' '.$blockLines[$i];
$previousLineIndented = false;
$previousLineBlank = false;
}
}
} else {
$text = implode("\n", $blockLines);
}
// deal with trailing newlines
if ('' === $chomping) {
$text = preg_replace('/\n+$/', "\n",
$text);
} elseif ('-' === $chomping) {
$text = preg_replace('/\n+$/', '', $text);
}
return $text;
}
/**
* Returns true if the next line is indented.
*
* @return bool Returns true if the next line is indented, false
otherwise
*/
private function isNextLineIndented()
{
$currentIndentation = $this->getCurrentLineIndentation();
$EOF = !$this->moveToNextLine();
while (!$EOF && $this->isCurrentLineEmpty()) {
$EOF = !$this->moveToNextLine();
}
if ($EOF) {
return false;
}
$ret = $this->getCurrentLineIndentation() >
$currentIndentation;
$this->moveToPreviousLine();
return $ret;
}
/**
* Returns true if the current line is blank or if it is a comment
line.
*
* @return bool Returns true if the current line is empty or if it is a
comment line, false otherwise
*/
private function isCurrentLineEmpty()
{
return $this->isCurrentLineBlank() ||
$this->isCurrentLineComment();
}
/**
* Returns true if the current line is blank.
*
* @return bool Returns true if the current line is blank, false
otherwise
*/
private function isCurrentLineBlank()
{
return '' == trim($this->currentLine, ' ');
}
/**
* Returns true if the current line is a comment line.
*
* @return bool Returns true if the current line is a comment line,
false otherwise
*/
private function isCurrentLineComment()
{
//checking explicitly the first char of the trim is faster than
loops or strpos
$ltrimmedLine = ltrim($this->currentLine, ' ');
return '' !== $ltrimmedLine && '#' ===
$ltrimmedLine[0];
}
private function isCurrentLineLastLineInDocument()
{
return ($this->offset + $this->currentLineNb) >=
($this->totalNumberOfLines - 1);
}
/**
* Cleanups a YAML string to be parsed.
*
* @param string $value The input YAML string
*
* @return string A cleaned up YAML string
*/
private function cleanup($value)
{
$value = str_replace(array("\r\n", "\r"),
"\n", $value);
// strip YAML header
$count = 0;
$value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u',
'', $value, -1, $count);
$this->offset += $count;
// remove leading comments
$trimmedValue = preg_replace('#^(\#.*?\n)+#s',
'', $value, -1, $count);
if (1 == $count) {
// items have been removed, update the offset
$this->offset += substr_count($value, "\n") -
substr_count($trimmedValue, "\n");
$value = $trimmedValue;
}
// remove start of the document marker (---)
$trimmedValue = preg_replace('#^\-\-\-.*?\n#s',
'', $value, -1, $count);
if (1 == $count) {
// items have been removed, update the offset
$this->offset += substr_count($value, "\n") -
substr_count($trimmedValue, "\n");
$value = $trimmedValue;
// remove end of the document marker (...)
$value = preg_replace('#\.\.\.\s*$#', '',
$value);
}
return $value;
}
/**
* Returns true if the next line starts unindented collection.
*
* @return bool Returns true if the next line starts unindented
collection, false otherwise
*/
private function isNextLineUnIndentedCollection()
{
$currentIndentation = $this->getCurrentLineIndentation();
$notEOF = $this->moveToNextLine();
while ($notEOF && $this->isCurrentLineEmpty()) {
$notEOF = $this->moveToNextLine();
}
if (false === $notEOF) {
return false;
}
$ret = $this->getCurrentLineIndentation() ===
$currentIndentation &&
$this->isStringUnIndentedCollectionItem();
$this->moveToPreviousLine();
return $ret;
}
/**
* Returns true if the string is un-indented collection item.
*
* @return bool Returns true if the string is un-indented collection
item, false otherwise
*/
private function isStringUnIndentedCollectionItem()
{
return '-' === rtrim($this->currentLine) || 0 ===
strpos($this->currentLine, '- ');
}
/**
* Tests whether or not the current line is the header of a block
scalar.
*
* @return bool
*/
private function isBlockScalarHeader()
{
return (bool)
self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~',
$this->currentLine);
}
/**
* A local wrapper for `preg_match` which will throw a ParseException
if there
* is an internal error in the PCRE engine.
*
* This avoids us needing to check for "false" every time
PCRE is used
* in the YAML engine
*
* @throws ParseException on a PCRE internal error
*
* @see preg_last_error()
*
* @internal
*/
public static function preg_match($pattern, $subject, &$matches =
null, $flags = 0, $offset = 0)
{
if (false === $ret = preg_match($pattern, $subject, $matches,
$flags, $offset)) {
switch (preg_last_error()) {
case PREG_INTERNAL_ERROR:
$error = 'Internal PCRE error.';
break;
case PREG_BACKTRACK_LIMIT_ERROR:
$error = 'pcre.backtrack_limit reached.';
break;
case PREG_RECURSION_LIMIT_ERROR:
$error = 'pcre.recursion_limit reached.';
break;
case PREG_BAD_UTF8_ERROR:
$error = 'Malformed UTF-8 data.';
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
$error = 'Offset doesn\'t correspond to the
begin of a valid UTF-8 code point.';
break;
default:
$error = 'Error.';
}
throw new ParseException($error);
}
return $ret;
}
}
Compat/src/Yaml/Unescaper.php000064400000010515151162104040012112
0ustar00<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml;
/**
* Unescaper encapsulates unescaping rules for single and double-quoted
* YAML strings.
*
* @author Matthew Lewinski <matthew@lewinski.org>
*
* @internal
*/
class Unescaper
{
/**
* Parser and Inline assume UTF-8 encoding, so escaped Unicode
characters
* must be converted to that encoding.
*
* @deprecated since version 2.5, to be removed in 3.0
*
* @internal
*/
const ENCODING = 'UTF-8';
/**
* Regex fragment that matches an escaped character in a double quoted
string.
*/
const REGEX_ESCAPED_CHARACTER =
'\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';
/**
* Unescapes a single quoted string.
*
* @param string $value A single quoted string
*
* @return string The unescaped string
*/
public function unescapeSingleQuotedString($value)
{
return str_replace('\'\'', '\'',
$value);
}
/**
* Unescapes a double quoted string.
*
* @param string $value A double quoted string
*
* @return string The unescaped string
*/
public function unescapeDoubleQuotedString($value)
{
$self = $this;
$callback = function ($match) use ($self) {
return $self->unescapeCharacter($match[0]);
};
// evaluate the string
return
preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u',
$callback, $value);
}
/**
* Unescapes a character that was found in a double-quoted string.
*
* @param string $value An escaped character
*
* @return string The unescaped character
*
* @internal This method is public to be usable as callback. It should
not
* be used in user code. Should be changed in 3.0.
*/
public function unescapeCharacter($value)
{
switch ($value[1]) {
case '0':
return "\x0";
case 'a':
return "\x7";
case 'b':
return "\x8";
case 't':
return "\t";
case "\t":
return "\t";
case 'n':
return "\n";
case 'v':
return "\xB";
case 'f':
return "\xC";
case 'r':
return "\r";
case 'e':
return "\x1B";
case ' ':
return ' ';
case '"':
return '"';
case '/':
return '/';
case '\\':
return '\\';
case 'N':
// U+0085 NEXT LINE
return "\xC2\x85";
case '_':
// U+00A0 NO-BREAK SPACE
return "\xC2\xA0";
case 'L':
// U+2028 LINE SEPARATOR
return "\xE2\x80\xA8";
case 'P':
// U+2029 PARAGRAPH SEPARATOR
return "\xE2\x80\xA9";
case 'x':
return self::utf8chr(hexdec(substr($value, 2, 2)));
case 'u':
return self::utf8chr(hexdec(substr($value, 2, 4)));
case 'U':
return self::utf8chr(hexdec(substr($value, 2, 8)));
default:
@trigger_error('Not escaping a backslash in a
double-quoted string is deprecated since Symfony 2.8 and will throw a
ParseException in 3.0.', E_USER_DEPRECATED);
return $value;
}
}
/**
* Get the UTF-8 character for the given code point.
*
* @param int $c The unicode code point
*
* @return string The corresponding UTF-8 character
*/
private static function utf8chr($c)
{
if (0x80 > $c %= 0x200000) {
return chr($c);
}
if (0x800 > $c) {
return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
}
if (0x10000 > $c) {
return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6
& 0x3F).chr(0x80 | $c & 0x3F);
}
return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 &
0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
}
}
Compat/src/Yaml/Yaml.php000064400000005137151162104040011073
0ustar00<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml;
use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;
/**
* Yaml offers convenience methods to load and dump YAML.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Yaml
{
/**
* Parses YAML into a PHP value.
*
* Usage:
* <code>
* $array = Yaml::parse(file_get_contents('config.yml'));
* print_r($array);
* </code>
*
* As this method accepts both plain strings and file names as an
input,
* you must validate the input before calling this method. Passing a
file
* as an input is a deprecated feature and will be removed in 3.0.
*
* Note: the ability to pass file names to the Yaml::parse method is
deprecated since version 2.2 and will be removed in 3.0. Pass the YAML
contents of the file instead.
*
* @param string $input Path to a YAML file or a
string containing YAML
* @param bool $exceptionOnInvalidType True if an exception must be
thrown on invalid types false otherwise
* @param bool $objectSupport True if object support is
enabled, false otherwise
* @param bool $objectForMap True if maps should return a
stdClass instead of array()
*
* @return mixed The YAML converted to a PHP value
*
* @throws ParseException If the YAML is not valid
*/
public static function parse($input, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
{
// if input is a file, process it
$file = '';
if (false === strpos($input, "\n") &&
is_file($input)) {
@trigger_error('The ability to pass file names to the
'.__METHOD__.' method is deprecated since version 2.2 and will be
removed in 3.0. Pass the YAML contents of the file instead.',
E_USER_DEPRECATED);
if (false === is_readable($input)) {
throw new ParseException(sprintf('Unable to parse
"%s" as the file is not readable.', $input));
}
$file = $input;
$input = file_get_contents($file);
}
$yaml = new Parser();
try {
return $yaml->parse($input, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
} catch (ParseException $e) {
if ($file) {
$e->setParsedFile($file);
}
throw $e;
}
}
}
composer.json000064400000002304151162104040007257 0ustar00{
"name": "rockettheme/toolbox",
"type": "library",
"description": "RocketTheme Toolbox Library",
"keywords": ["rockettheme", "php"],
"homepage": "http://www.rockettheme.com",
"license": "MIT",
"require": {
"php": ">=5.4.0",
"ext-json": "*",
"pimple/pimple": "~3.0",
"symfony/yaml": ">2.5",
"symfony/event-dispatcher": ">2.5"
},
"require-dev": {
"phpunit/phpunit": "~6"
},
"autoload": {
"psr-4": {
"RocketTheme\\Toolbox\\ArrayTraits\\":
"ArrayTraits/src",
"RocketTheme\\Toolbox\\Blueprints\\":
"Blueprints/src",
"RocketTheme\\Toolbox\\Compat\\":
"Compat/src",
"RocketTheme\\Toolbox\\DI\\": "DI/src",
"RocketTheme\\Toolbox\\Event\\":
"Event/src",
"RocketTheme\\Toolbox\\File\\": "File/src",
"RocketTheme\\Toolbox\\ResourceLocator\\":
"ResourceLocator/src",
"RocketTheme\\Toolbox\\Session\\":
"Session/src",
"RocketTheme\\Toolbox\\StreamWrapper\\":
"StreamWrapper/src"
}
},
"scripts": {
"test": "vendor/bin/phpunit run unit",
"test-windows": "vendor\\bin\\phpunit run unit"
}
}
DI/src/Container.php000064400000000376151162104040010262 0ustar00<?php
namespace RocketTheme\Toolbox\DI;
use Pimple\Container as BaseContainer;
/**
* Implements Dependency Injection Container.
*
* @package RocketTheme\Toolbox\DI
* @author RocketTheme
* @license MIT
*/
class Container extends BaseContainer
{
}
DI/src/ServiceProviderInterface.php000064400000000466151162104040013274
0ustar00<?php
namespace RocketTheme\Toolbox\DI;
use \Pimple\ServiceProviderInterface as BaseServiceProviderInterface;
/**
* Defines ServiceProviderInterface.
*
* @package RocketTheme\Toolbox\DI
* @author RocketTheme
* @license MIT
*/
interface ServiceProviderInterface extends BaseServiceProviderInterface
{
}
Event/src/Event.php000064400000001026151162104040010177 0ustar00<?php
namespace RocketTheme\Toolbox\Event;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Constructor;
use RocketTheme\Toolbox\ArrayTraits\Export;
use Symfony\Component\EventDispatcher\Event as BaseEvent;
/**
* Implements Symfony Event interface.
*
* @package RocketTheme\Toolbox\Event
* @author RocketTheme
* @license MIT
*/
class Event extends BaseEvent implements \ArrayAccess
{
use ArrayAccess, Constructor, Export;
/**
* @var array
*/
protected $items = [];
}
Event/src/EventDispatcher.php000064400000001247151162104040012213
0ustar00<?php
namespace RocketTheme\Toolbox\Event;
use Symfony\Component\EventDispatcher\Event as BaseEvent;
use Symfony\Component\EventDispatcher\EventDispatcher as
BaseEventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Implements Symfony EventDispatcher interface.
*
* @package RocketTheme\Toolbox\Event
* @author RocketTheme
* @license MIT
*/
class EventDispatcher extends BaseEventDispatcher implements
EventDispatcherInterface
{
public function dispatch($eventName, BaseEvent $event = null)
{
if (null === $event) {
$event = new Event();
}
return parent::dispatch($eventName, $event);
}
}
Event/src/EventSubscriberInterface.php000064400000000526151162104040014050
0ustar00<?php
namespace RocketTheme\Toolbox\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface as
BaseEventSubscriberInterface;
/**
* Defines EventSubscriberInterface.
*
* @package RocketTheme\Toolbox\Event
* @author RocketTheme
* @license MIT
*/
interface EventSubscriberInterface extends BaseEventSubscriberInterface
{
}
File/src/File.php000064400000025051151162104040007577 0ustar00<?php
namespace RocketTheme\Toolbox\File;
/**
* Implements Universal File Reader.
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
class File implements FileInterface
{
/**
* @var string
*/
protected $filename;
/**
* @var resource
*/
protected $handle;
/**
* @var bool|null
*/
protected $locked;
/**
* @var string
*/
protected $extension;
/**
* @var string Raw file contents.
*/
protected $raw;
/**
* @var array Parsed file contents.
*/
protected $content;
/**
* @var array
*/
protected $settings = [];
/**
* @var array|File[]
*/
static protected $instances = [];
/**
* Get file instance.
*
* @param string $filename
* @return static
*/
public static function instance($filename)
{
if (!\is_string($filename) && $filename) {
throw new \InvalidArgumentException('Filename should be
non-empty string');
}
if (!isset(static::$instances[$filename])) {
static::$instances[$filename] = new static;
static::$instances[$filename]->init($filename);
}
return static::$instances[$filename];
}
/**
* Set/get settings.
*
* @param array $settings
* @return array
*/
public function settings(array $settings = null)
{
if ($settings !== null) {
$this->settings = $settings;
}
return $this->settings;
}
/**
* Get setting.
*
* @param string $setting
* @param mixed $default
* @return mixed
*/
public function setting($setting, $default = null)
{
return isset($this->settings[$setting]) ?
$this->settings[$setting] : $default;
}
/**
* Prevent constructor from being used.
*/
protected function __construct()
{
}
/**
* Prevent cloning.
*/
protected function __clone()
{
//Me not like clones! Me smash clones!
}
/**
* Set filename.
*
* @param $filename
*/
protected function init($filename)
{
$this->filename = $filename;
}
/**
* Free the file instance.
*/
public function free()
{
if ($this->locked) {
$this->unlock();
}
$this->content = null;
$this->raw = null;
unset(static::$instances[$this->filename]);
}
/**
* Get/set the file location.
*
* @param string $var
* @return string
*/
public function filename($var = null)
{
if ($var !== null) {
$this->filename = $var;
}
return $this->filename;
}
/**
* Return basename of the file.
*
* @return string
*/
public function basename()
{
return basename($this->filename, $this->extension);
}
/**
* Check if file exits.
*
* @return bool
*/
public function exists()
{
return is_file($this->filename);
}
/**
* Return file modification time.
*
* @return int|bool Timestamp or false if file doesn't exist.
*/
public function modified()
{
return is_file($this->filename) ? filemtime($this->filename)
: false;
}
/**
* Lock file for writing. You need to manually unlock().
*
* @param bool $block For non-blocking lock, set the parameter to
false.
* @return bool
* @throws \RuntimeException
*/
public function lock($block = true)
{
if (!$this->handle) {
if (!$this->mkdir(\dirname($this->filename))) {
throw new \RuntimeException('Creating directory failed
for ' . $this->filename);
}
$this->handle = @fopen($this->filename, 'cb+');
if (!$this->handle) {
$error = error_get_last();
throw new \RuntimeException("Opening file for writing
failed on error {$error['message']}");
}
}
$lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB;
// Some filesystems do not support file locks, only fail if another
process holds the lock.
$this->locked = flock($this->handle, $lock, $wouldblock) ||
!$wouldblock;
return $this->locked;
}
/**
* Returns true if file has been locked for writing.
*
* @return bool|null True = locked, false = failed, null = not locked.
*/
public function locked()
{
return $this->locked;
}
/**
* Unlock file.
*
* @return bool
*/
public function unlock()
{
if (!$this->handle) {
return false;
}
if ($this->locked) {
flock($this->handle, LOCK_UN);
$this->locked = null;
}
fclose($this->handle);
$this->handle = null;
return true;
}
/**
* Check if file can be written.
*
* @return bool
*/
public function writable()
{
return file_exists($this->filename) ?
is_writable($this->filename) && is_file($this->filename) :
$this->writableDir(\dirname($this->filename));
}
/**
* (Re)Load a file and return RAW file contents.
*
* @return string
*/
public function load()
{
$this->raw = $this->exists() ? (string)
file_get_contents($this->filename) : '';
$this->content = null;
return $this->raw;
}
/**
* Get/set raw file contents.
*
* @param string $var
* @return string
*/
public function raw($var = null)
{
if ($var !== null) {
$this->raw = (string) $var;
$this->content = null;
}
if (!\is_string($this->raw)) {
$this->raw = $this->load();
}
return $this->raw;
}
/**
* Get/set parsed file contents.
*
* @param mixed $var
* @return string|array
* @throws \RuntimeException
*/
public function content($var = null)
{
if ($var !== null) {
$this->content = $this->check($var);
// Update RAW, too.
$this->raw = $this->encode($this->content);
} elseif ($this->content === null) {
// Decode RAW file.
try {
$this->content = $this->decode($this->raw());
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Failed to read
%s: %s', $this->filename, $e->getMessage()), 500, $e);
}
}
return $this->content;
}
/**
* Save file.
*
* @param mixed $data Optional data to be saved, usually array.
* @throws \RuntimeException
*/
public function save($data = null)
{
if ($data !== null) {
$this->content($data);
}
$filename = $this->filename;
$dir = \dirname($filename);
if (!$this->mkdir($dir)) {
throw new \RuntimeException('Creating directory failed for
' . $filename);
}
try {
if ($this->handle) {
$tmp = true;
// As we are using non-truncating locking, make sure that
the file is empty before writing.
if (@ftruncate($this->handle, 0) === false ||
@fwrite($this->handle, $this->raw()) === false) {
// Writing file failed, throw an error.
$tmp = false;
}
} else {
// Create file with a temporary name and rename it to make
the save action atomic.
$tmp = $this->tempname($filename);
if (file_put_contents($tmp, $this->raw()) === false) {
$tmp = false;
} elseif (@rename($tmp, $filename) === false) {
@unlink($tmp);
$tmp = false;
}
}
} catch (\Exception $e) {
$tmp = false;
}
if ($tmp === false) {
throw new \RuntimeException('Failed to save file ' .
$filename);
}
// Touch the directory as well, thus marking it modified.
@touch($dir);
}
/**
* Rename file in the filesystem if it exists.
*
* @param $filename
* @return bool
*/
public function rename($filename)
{
if ($this->exists() && !@rename($this->filename,
$filename)) {
return false;
}
unset(static::$instances[$this->filename]);
static::$instances[$filename] = $this;
$this->filename = $filename;
return true;
}
/**
* Delete file from filesystem.
*
* @return bool
*/
public function delete()
{
return unlink($this->filename);
}
/**
* Check contents and make sure it is in correct format.
*
* Override in derived class.
*
* @param string $var
* @return string
*/
protected function check($var)
{
return (string) $var;
}
/**
* Encode contents into RAW string.
*
* Override in derived class.
*
* @param string $var
* @return string
*/
protected function encode($var)
{
return (string) $var;
}
/**
* Decode RAW string into contents.
*
* Override in derived class.
*
* @param string $var
* @return string mixed
*/
protected function decode($var)
{
return (string) $var;
}
/**
* @param string $dir
*/
private function mkdir($dir)
{
// Silence error for open_basedir; should fail in mkdir instead.
if (@is_dir($dir)) {
return true;
}
$success = @mkdir($dir, 0777, true);
if (!$success) {
// Take yet another look, make sure that the folder
doesn't exist.
clearstatcache(true, $dir);
if (!@is_dir($dir)) {
return false;
}
}
return true;
}
/**
* @param string $dir
* @return bool
* @internal
*/
protected function writableDir($dir)
{
if ($dir && !file_exists($dir)) {
return $this->writableDir(\dirname($dir));
}
return $dir && is_dir($dir) && is_writable($dir);
}
/**
* @param string $filename
* @param int $length
* @return string
*/
protected function tempname($filename, $length = 5)
{
do {
$test = $filename .
substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'),
0, $length);
} while (file_exists($test));
return $test;
}
}
File/src/FileInterface.php000064400000003602151162104040011416
0ustar00<?php
namespace RocketTheme\Toolbox\File;
/**
* Defines FileInterface.
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
interface FileInterface
{
/**
* Get file instance.
*
* @param string $filename
* @return static
*/
public static function instance($filename);
/**
* Free the file instance.
*/
public function free();
/**
* Check if file exits.
*
* @return bool
*/
public function exists();
/**
* Return file modification time.
*
* @return int Timestamp
*/
public function modified();
/**
* Lock file for writing.
*
* @param bool $block For non-blocking lock, set the parameter to
false.
* @return bool
*/
public function lock($block = true);
/**
* Returns true if file has been locked for writing.
*
* @return bool|null True = locked, false = failed, null = not locked.
*/
public function locked();
/**
* Unlock file.
*
* @return bool
*/
public function unlock();
/**
* Check if file can be written.
*
* @return bool
*/
public function writable();
/**
* (Re)Load a file and return its contents.
*
* @return string
*/
public function load();
/**
* Get/set raw file contents.
*
* @param string $var
* @return string
*/
public function raw($var = null);
/**
* Get/set parsed file contents.
*
* @param string $var
* @return string
*/
public function content($var = null);
/**
* Save file.
*
* @param string $data Optional data to be saved.
* @throws \RuntimeException
*/
public function save($data = null);
/**
* Delete file from filesystem.
*
* @return bool
*/
public function delete();
}
File/src/IniFile.php000064400000003200151162104040010227 0ustar00<?php
namespace RocketTheme\Toolbox\File;
/**
* Implements INI File reader.
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
class IniFile extends File
{
/**
* @var string
*/
protected $extension = '.ini';
/**
* @var array|File[]
*/
static protected $instances = [];
/**
* Check contents and make sure it is in correct format.
*
* @param array $var
* @return array
* @throws \RuntimeException
*/
protected function check($var)
{
if (!\is_array($var)) {
throw new \RuntimeException('Provided data is not an
array');
}
return $var;
}
/**
* Encode configuration object into RAW string (INI).
*
* @param array $var
* @return string
* @throws \RuntimeException
*/
protected function encode($var)
{
$string = '';
foreach ($var as $key => $value) {
$string .= $key . '="' . preg_replace(
['/"/', '/\\\/',
"/\t/", "/\n/", "/\r/"],
['\"', '\\\\',
'\t', '\n', '\r'],
$value
) . "\"\n";
}
return $string;
}
/**
* Decode INI file into contents.
*
* @param string $var
* @return array
* @throws \RuntimeException
*/
protected function decode($var)
{
$decoded = file_exists($this->filename) ?
@parse_ini_file($this->filename) : [];
if ($decoded === false) {
throw new \RuntimeException("Decoding file
'{$this->filename}' failed'");
}
return $decoded;
}
}
File/src/JsonFile.php000064400000002047151162104040010431 0ustar00<?php
namespace RocketTheme\Toolbox\File;
/**
* Implements Json File reader.
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
class JsonFile extends File
{
/**
* @var string
*/
protected $extension = '.json';
/**
* @var array|File[]
*/
static protected $instances = [];
/**
* Check contents and make sure it is in correct format.
*
* @param array $var
* @return array
*/
protected function check($var)
{
return (array) $var;
}
/**
* Encode contents into RAW string.
*
* @param string $var
* @param int $options
* @return string
*/
protected function encode($var, $options = 0)
{
return (string) json_encode($var, $options);
}
/**
* Decode RAW string into contents.
*
* @param string $var
* @param bool $assoc
* @return array mixed
*/
protected function decode($var, $assoc = false)
{
return (array) json_decode($var, $assoc);
}
}
File/src/LogFile.php000064400000002734151162104040010244 0ustar00<?php
namespace RocketTheme\Toolbox\File;
/**
* Implements Log File reader.
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
class LogFile extends File
{
/**
* @var array|File[]
*/
static protected $instances = [];
/**
* Constructor.
*/
protected function __construct()
{
parent::__construct();
$this->extension = '.log';
}
/**
* Check contents and make sure it is in correct format.
*
* @param array $var
* @return array
*/
protected function check($var)
{
return (array) $var;
}
/**
* Encode contents into RAW string (unsupported).
*
* @param string $var
* @return string|void
* @throws \BadMethodCallException
*/
protected function encode($var)
{
throw new \BadMethodCallException('Saving log file is
forbidden.');
}
/**
* Decode RAW string into contents.
*
* @param string $var
* @return array mixed
*/
protected function decode($var)
{
$lines = (array) preg_split('#(\r\n|\n|\r)#', $var);
$results = array();
foreach ($lines as $line) {
preg_match('#^\[(.*)\] (.*) @ (.*) @@ (.*)$#',
$line, $matches);
if ($matches) {
$results[] = ['date' => $matches[1],
'message' => $matches[2], 'url' => $matches[3],
'file' => $matches[4]];
}
}
return $results;
}
}
File/src/MarkdownFile.php000064400000010435151162104040011302
0ustar00<?php
namespace RocketTheme\Toolbox\File;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml as YamlParser;
use RocketTheme\Toolbox\Compat\Yaml\Yaml as FallbackYamlParser;
/**
* Implements Markdown File reader.
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
class MarkdownFile extends File
{
/**
* @var string
*/
protected $extension = '.md';
/**
* @var array|File[]
*/
static protected $instances = [];
/**
* Get/set file header.
*
* @param array $var
*
* @return array
*/
public function header(array $var = null)
{
$content = $this->content();
if ($var !== null) {
$content['header'] = $var;
$this->content($content);
}
return $content['header'];
}
/**
* Get/set markdown content.
*
* @param string $var
*
* @return string
*/
public function markdown($var = null)
{
$content = $this->content();
if ($var !== null) {
$content['markdown'] = (string) $var;
$this->content($content);
}
return $content['markdown'];
}
/**
* Get/set frontmatter content.
*
* @param string $var
*
* @return string
*/
public function frontmatter($var = null)
{
$content = $this->content();
if ($var !== null) {
$content['frontmatter'] = (string) $var;
$this->content($content);
}
return $content['frontmatter'];
}
/**
* Check contents and make sure it is in correct format.
*
* @param array $var
* @return array
*/
protected function check($var)
{
$var = (array) $var;
if (!isset($var['header']) ||
!\is_array($var['header'])) {
$var['header'] = array();
}
if (!isset($var['markdown']) ||
!\is_string($var['markdown'])) {
$var['markdown'] = '';
}
return $var;
}
/**
* Encode contents into RAW string.
*
* @param array $var
* @return string
*/
protected function encode($var)
{
// Create Markdown file with YAML header.
$o = (!empty($var['header']) ? "---\n" .
trim(YamlParser::dump($var['header'], 20)) .
"\n---\n\n" : '') . $var['markdown'];
// Normalize line endings to Unix style.
$o = preg_replace("/(\r\n|\r)/", "\n", $o);
return $o;
}
/**
* Decode RAW string into contents.
*
* @param string $var
* @return array mixed
*/
protected function decode($var)
{
$content = [
'header' => false,
'frontmatter' => ''
];
$frontmatter_regex = "/^---\n(.+?)\n---\n{0,}(.*)$/uis";
// Normalize line endings to Unix style.
$var = preg_replace("/(\r\n|\r)/", "\n", $var);
// Parse header.
preg_match($frontmatter_regex, ltrim($var), $m);
if(!empty($m)) {
// Normalize frontmatter.
$content['frontmatter'] = $frontmatter =
preg_replace("/\n\t/", "\n ", $m[1]);
// Try native PECL YAML PHP extension first if available.
if (\function_exists('yaml_parse') &&
$this->setting('native')) {
// Safely decode YAML.
$saved = @ini_get('yaml.decode_php');
@ini_set('yaml.decode_php', 0);
$content['header'] =
@yaml_parse("---\n" . $frontmatter . "\n...");
@ini_set('yaml.decode_php', $saved);
}
if ($content['header'] === false) {
// YAML hasn't been parsed yet (error or extension
isn't available). Fall back to Symfony parser.
try {
$content['header'] = (array)
YamlParser::parse($frontmatter);
} catch (ParseException $e) {
if (!$this->setting('compat', true)) {
throw $e;
}
$content['header'] = (array)
FallbackYamlParser::parse($frontmatter);
}
}
$content['markdown'] = $m[2];
} else {
$content['header'] = [];
$content['markdown'] = $var;
}
return $content;
}
}
File/src/MoFile.php000064400000007137151162104040010100 0ustar00<?php
namespace RocketTheme\Toolbox\File;
/**
* Implements Gettext Mo File reader (readonly).
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
class MoFile extends File
{
/**
* @var string
*/
protected $extension = '.mo';
protected $pos = 0;
protected $str;
protected $len;
protected $endian;
/**
* @var array|File[]
*/
static protected $instances = [];
/**
* File can never be written.
*
* @return bool
*/
public function writable()
{
return false;
}
/**
* Prevent saving file.
*
* @throws \BadMethodCallException
*/
public function save($data = null)
{
throw new \BadMethodCallException('save() not supported for
.mo files.');
}
/**
* Prevent deleting file from filesystem.
*
* @return bool
*/
public function delete()
{
return false;
}
/**
* @param $var
* @return array
* @throws \RuntimeException
*/
public function decode($var)
{
$this->endian = 'V';
$this->str = $var;
$this->len = \strlen($var);
$magic = $this->readInt() & 0xffffffff;
if ($magic === 0x950412de) {
// Low endian.
$this->endian = 'V';
} elseif ($magic === 0xde120495) {
// Big endian.
$this->endian = 'N';
} else {
throw new \RuntimeException('Not a Gettext file
(.mo).');
}
// Skip revision number.
$rev = $this->readInt();
// Total count.
$total = $this->readInt();
// Offset of original table.
$originals = $this->readInt();
// Offset of translation table.
$translations = $this->readInt();
// Each table consists of string length and offset of the string.
$this->seek($originals);
$table_originals = $this->readIntArray($total * 2);
$this->seek($translations);
$table_translations = $this->readIntArray($total * 2);
$items = [];
for ($i = 0; $i < $total; $i++) {
$this->seek($table_originals[$i * 2 + 2]);
// TODO: Original string can have context concatenated on it.
We do not yet support that.
$original = $this->read($table_originals[$i * 2 + 1]);
if ($original) {
$this->seek($table_translations[$i * 2 + 2]);
// TODO: Plural forms are stored by letting the plural of
the original string follow the singular of the original string, separated
through a NUL byte.
$translated = $this->read($table_translations[$i * 2 +
1]);
$items[$original] = $translated;
}
}
return $items;
}
/**
* @return int
*/
protected function readInt()
{
$read = $this->read(4);
if ($read === false) {
return false;
}
$read = unpack($this->endian, $read);
return array_shift($read);
}
/**
* @param $count
* @return array
*/
protected function readIntArray($count)
{
return unpack($this->endian . $count, $this->read(4 *
$count));
}
/**
* @param $bytes
* @return string
*/
private function read($bytes)
{
$data = substr($this->str, $this->pos, $bytes);
$this->seek($this->pos + $bytes);
return $data;
}
/**
* @param $pos
* @return mixed
*/
private function seek($pos)
{
$this->pos = $pos < $this->len ? $pos : $this->len;
return $this->pos;
}
}
File/src/PhpFile.php000064400000005030151162104040010242 0ustar00<?php
namespace RocketTheme\Toolbox\File;
/**
* Implements PHP File reader.
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
class PhpFile extends File
{
/**
* @var string
*/
protected $extension = '.php';
/**
* @var array|File[]
*/
static protected $instances = [];
/**
* Saves PHP file and invalidates opcache.
*
* @param mixed $data Optional data to be saved, usually array.
* @throws \RuntimeException
*/
public function save($data = null)
{
parent::save($data);
// Invalidate configuration file from the opcache.
if (\function_exists('opcache_invalidate')) {
// PHP 5.5.5+
@opcache_invalidate($this->filename, true);
} elseif (\function_exists('apc_invalidate')) {
// APC
@apc_invalidate($this->filename);
}
}
/**
* Check contents and make sure it is in correct format.
*
* @param array|object $var
* @return array
* @throws \RuntimeException
*/
protected function check($var)
{
if (!(\is_array($var) || \is_object($var))) {
throw new \RuntimeException('Provided data is not an
array');
}
return $var;
}
/**
* Encode configuration object into RAW string (PHP class).
*
* @param array $var
* @return string
* @throws \RuntimeException
*/
protected function encode($var)
{
// Build the object variables string
return "<?php\nreturn {$this->encodeArray((array)
$var)};\n";
}
/**
* Method to get an array as an exported string.
*
* @param array $a The array to get as a string.
* @param int $level Used internally to indent rows.
*
* @return string
*/
protected function encodeArray(array $a, $level = 0)
{
$r = [];
foreach ($a as $k => $v) {
if (\is_array($v) || \is_object($v)) {
$r[] = var_export($k, true) . ' => ' .
$this->encodeArray((array) $v, $level + 1);
} else {
$r[] = var_export($k, true) . ' => ' .
var_export($v, true);
}
}
$space = str_repeat(' ', $level);
return "[\n {$space}" . implode(",\n
{$space}", $r) . "\n{$space}]";
}
/**
* Decode PHP file into contents.
*
* @param string $var
* @return array
*/
protected function decode($var)
{
return (array) include $this->filename;
}
}
File/src/YamlFile.php000064400000006213151162104040010421 0ustar00<?php
namespace RocketTheme\Toolbox\File;
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;
/**
* Implements YAML File reader.
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
class YamlFile extends File
{
/**
* @var array|File[]
*/
static protected $instances = [];
static protected $globalSettings = [
'compat' => true,
'native' => true
];
/**
* Set/get settings.
*
* @param array $settings
* @return array
*/
public static function globalSettings(array $settings = null)
{
if ($settings !== null) {
static::$globalSettings = $settings;
}
return static::$globalSettings;
}
/**
* Constructor.
*/
protected function __construct()
{
parent::__construct();
$this->extension = '.yaml';
}
/**
* Set/get settings.
*
* @param array $settings
* @return array
*/
public function settings(array $settings = null)
{
if ($settings !== null) {
$this->settings = $settings;
}
return $this->settings + static::$globalSettings;
}
/**
* Get setting.
*
* @param string $setting
* @param mixed $default
* @return mixed
*/
public function setting($setting, $default = null)
{
$value = parent::setting($setting);
if (null === $value) {
$value = isset(static::$globalSettings[$setting]) ?
static::$globalSettings[$setting] : $default;
}
return $value;
}
/**
* Check contents and make sure it is in correct format.
*
* @param array $var
* @return array
*/
protected function check($var)
{
return (array) $var;
}
/**
* Encode contents into RAW string.
*
* @param array $var
* @return string
* @throws DumpException
*/
protected function encode($var)
{
return (string) YamlParser::dump($var,
$this->setting('inline', 5),
$this->setting('indent', 2), true, false);
}
/**
* Decode RAW string into contents.
*
* @param string $var
* @return array mixed
* @throws ParseException
*/
protected function decode($var)
{
// Try native PECL YAML PHP extension first if available.
if (\function_exists('yaml_parse') &&
$this->setting('native', true)) {
// Safely decode YAML.
$saved = @ini_get('yaml.decode_php');
@ini_set('yaml.decode_php', 0);
$data = @yaml_parse($var);
@ini_set('yaml.decode_php', $saved);
if ($data !== false) {
return (array) $data;
}
}
try {
return (array) YamlParser::parse($var);
} catch (ParseException $e) {
if ($this->setting('compat', true)) {
return (array) FallbackYamlParser::parse($var);
}
throw $e;
}
}
}
ResourceLocator/src/RecursiveUniformResourceIterator.php000064400000002315151162104040017703
0ustar00<?php
namespace RocketTheme\Toolbox\ResourceLocator;
/**
* Implements recursive iterator over filesystem.
*
* @package RocketTheme\Toolbox\ResourceLocator
* @author RocketTheme
* @license MIT
*/
class RecursiveUniformResourceIterator extends UniformResourceIterator
implements \SeekableIterator, \RecursiveIterator
{
protected $subPath;
public function getChildren()
{
$subPath = $this->getSubPathName();
return (new static($this->getUrl(), $this->flags,
$this->locator))->setSubPath($subPath);
}
public function hasChildren($allow_links = null)
{
$allow_links = (bool) ($allow_links !== null ? $allow_links :
$this->flags & \FilesystemIterator::FOLLOW_SYMLINKS);
return $this->iterator && $this->isDir() &&
!$this->isDot() && ($allow_links || !$this->isLink());
}
public function getSubPath()
{
return $this->subPath;
}
public function getSubPathName()
{
return ($this->subPath ? $this->subPath . '/' :
'') . $this->getFilename();
}
/**
* @param $path
* @return $this
* @internal
*/
public function setSubPath($path)
{
$this->subPath = $path;
return $this;
}
}
ResourceLocator/src/ResourceLocatorInterface.php000064400000001647151162104040016115
0ustar00<?php
namespace RocketTheme\Toolbox\ResourceLocator;
/**
* Defines ResourceLocatorInterface.
*
* @package RocketTheme\Toolbox\ResourceLocator
* @author RocketTheme
* @license MIT
*/
interface ResourceLocatorInterface
{
/**
* Alias for findResource()
*
* @param $uri
* @return string|bool
*/
public function __invoke($uri);
/**
* Returns true if uri is resolvable by using locator.
*
* @param string $uri
* @return bool
*/
public function isStream($uri);
/**
* @param string $uri
* @param bool $absolute
* @param bool $first
* @return string|bool
*/
public function findResource($uri, $absolute = true, $first = false);
/**
* @param string $uri
* @param bool $absolute
* @param bool $all
* @return array
*/
public function findResources($uri, $absolute = true, $all = false);
}
ResourceLocator/src/UniformResourceIterator.php000064400000011551151162104040016015
0ustar00<?php
namespace RocketTheme\Toolbox\ResourceLocator;
use FilesystemIterator;
/**
* Implements FilesystemIterator for uniform resource locator.
*
* @package RocketTheme\Toolbox\ResourceLocator
* @author RocketTheme
* @license MIT
*/
class UniformResourceIterator extends FilesystemIterator
{
/**
* @var FilesystemIterator
*/
protected $iterator;
/**
* @var array
*/
protected $found;
/**
* @var array
*/
protected $stack;
/**
* @var string
*/
protected $path;
/**
* @var int
*/
protected $flags;
/**
* @var UniformResourceLocator
*/
protected $locator;
public function __construct($path, $flags = null,
UniformResourceLocator $locator = null)
{
if (!$locator) {
throw new \BadMethodCallException('Use
$locator->getIterator() instead');
}
$this->path = $path;
$this->setFlags($flags);
$this->locator = $locator;
$this->rewind();
}
public function current()
{
if ($this->flags & static::CURRENT_AS_SELF) {
return $this;
}
return $this->iterator->current();
}
public function key()
{
return $this->iterator->key();
}
public function next()
{
do {
$found = $this->findNext();
} while ($found && !empty($this->found[$found]));
if ($found) {
// Mark the file as found.
$this->found[$found] = true;
}
}
public function valid()
{
return $this->iterator &&
$this->iterator->valid();
}
public function rewind()
{
$this->found = [];
$this->stack =
$this->locator->findResources($this->path);
$this->next();
}
public function getUrl()
{
$path = $this->path . (substr($this->path, -1, 1) ===
'/' ? '' : '/');
return $path . $this->iterator->getFilename();
}
public function seek($position)
{
throw new \RuntimeException('Seek not implemented');
}
public function getATime()
{
return $this->iterator->getATime();
}
public function getBasename($suffix = null)
{
return $this->iterator->getBasename($suffix);
}
public function getCTime()
{
return $this->iterator->getCTime();
}
public function getExtension()
{
return $this->iterator->getExtension();
}
public function getFilename()
{
return $this->iterator->getFilename();
}
public function getGroup()
{
return $this->iterator->getGroup();
}
public function getInode()
{
return $this->iterator->getInode();
}
public function getMTime()
{
return $this->iterator->getMTime();
}
public function getOwner()
{
return $this->iterator->getOwner();
}
public function getPath()
{
return $this->iterator->getPath();
}
public function getPathname()
{
return $this->iterator->getPathname();
}
public function getPerms()
{
return $this->iterator->getPerms();
}
public function getSize()
{
return $this->iterator->getSize();
}
public function getType()
{
return $this->iterator->getType();
}
public function isDir()
{
return $this->iterator->isDir();
}
public function isDot()
{
return $this->iterator->isDot();
}
public function isExecutable()
{
return $this->iterator->isExecutable();
}
public function isFile()
{
return $this->iterator->isFile();
}
public function isLink()
{
return $this->iterator->isLink();
}
public function isReadable()
{
return $this->iterator->isReadable();
}
public function isWritable()
{
return $this->iterator->isWritable();
}
public function __toString()
{
return $this->iterator->__toString();
}
public function getFlags()
{
return $this->flags;
}
public function setFlags($flags = null)
{
$this->flags = $flags === null ? static::KEY_AS_PATHNAME |
static::CURRENT_AS_SELF | static::SKIP_DOTS : $flags;
if ($this->iterator) {
$this->iterator->setFlags($this->flags);
}
}
protected function findNext()
{
if ($this->iterator) {
$this->iterator->next();
}
if (!$this->valid()) {
do {
// Move to the next iterator if it exists.
$path = array_shift($this->stack);
if (!isset($path)) {
return null;
}
$this->iterator = new \FilesystemIterator($path,
$this->getFlags());
} while (!$this->iterator->valid());
}
return $this->getFilename();
}
}
ResourceLocator/src/UniformResourceLocator.php000064400000034512151162104040015631
0ustar00<?php
namespace RocketTheme\Toolbox\ResourceLocator;
/**
* Implements Uniform Resource Location.
*
* @package RocketTheme\Toolbox\ResourceLocator
* @author RocketTheme
* @license MIT
*
* @link
http://webmozarts.com/2013/06/19/the-power-of-uniform-resource-location-in-php/
*/
class UniformResourceLocator implements ResourceLocatorInterface
{
/**
* @var string Base URL for all the streams.
*/
public $base;
/**
* @var array[]
*/
protected $schemes = [];
/**
* @var array
*/
protected $cache = [];
public function __construct($base = null)
{
// Normalize base path.
$this->base = rtrim(str_replace('\\', '/',
$base ?: getcwd()), '/');
}
/**
* Return iterator for the resource URI.
*
* @param string $uri
* @param int $flags See constants from FilesystemIterator
class.
* @return UniformResourceIterator
*/
public function getIterator($uri, $flags = null)
{
return new UniformResourceIterator($uri, $flags, $this);
}
/**
* Return recursive iterator for the resource URI.
*
* @param string $uri
* @param int $flags See constants from FilesystemIterator
class.
* @return RecursiveUniformResourceIterator
*/
public function getRecursiveIterator($uri, $flags = null)
{
return new RecursiveUniformResourceIterator($uri, $flags, $this);
}
/**
* Reset locator by removing all the schemes.
*
* @return $this
*/
public function reset()
{
$this->schemes = [];
$this->cache = [];
return $this;
}
/**
* Reset a locator scheme
*
* @param string $scheme The scheme to reset
*
* @return $this
*/
public function resetScheme($scheme)
{
$this->schemes[$scheme] = [];
$this->cache = [];
return $this;
}
/**
* Add new paths to the scheme.
*
* @param string $scheme
* @param string $prefix
* @param string|array $paths
* @param bool|string $override True to add path as override, string
* @param bool $force True to add paths even if them do not exist.
* @throws \BadMethodCallException
*/
public function addPath($scheme, $prefix, $paths, $override = false,
$force = false)
{
$list = [];
foreach((array) $paths as $path) {
if (\is_array($path)) {
// Support stream lookup in ['theme',
'path/to'] format.
if (\count($path) !== 2 || !\is_string($path[0]) ||
!\is_string($path[1])) {
throw new \BadMethodCallException('Invalid stream
path given.');
}
$list[] = $path;
} elseif (false !== strpos($path, '://')) {
// Support stream lookup in 'theme://path/to'
format.
$stream = explode('://', $path, 2);
$stream[1] = trim($stream[1], '/');
$list[] = $stream;
} else {
// Normalize path.
$path = rtrim(str_replace('\\', '/',
$path), '/');
if ($force ||
@file_exists("{$this->base}/{$path}") || @file_exists($path))
{
// Support for absolute and relative paths.
$list[] = $path;
}
}
}
if (isset($this->schemes[$scheme][$prefix])) {
$paths = $this->schemes[$scheme][$prefix];
if (!$override || $override == 1) {
$list = $override ? array_merge($paths, $list) :
array_merge($list, $paths);
} else {
$location = array_search($override, $paths, true) ?:
\count($paths);
array_splice($paths, $location, 0, $list);
$list = $paths;
}
}
$this->schemes[$scheme][$prefix] = $list;
// Sort in reverse order to get longer prefixes to be matched
first.
krsort($this->schemes[$scheme]);
$this->cache = [];
}
/**
* Return base directory.
*
* @return string
*/
public function getBase()
{
return $this->base;
}
/**
* Return true if scheme has been defined.
*
* @param string $name
* @return bool
*/
public function schemeExists($name)
{
return isset($this->schemes[$name]);
}
/**
* Return defined schemes.
*
* @return array
*/
public function getSchemes()
{
return array_keys($this->schemes);
}
/**
* Return all scheme lookup paths.
*
* @param string $scheme
* @return array
*/
public function getPaths($scheme = null)
{
return !$scheme ? $this->schemes :
(isset($this->schemes[$scheme]) ? $this->schemes[$scheme] : []);
}
/**
* @param string $uri
* @return string|bool
* @throws \BadMethodCallException
*/
public function __invoke($uri)
{
if (!\is_string($uri)) {
throw new \BadMethodCallException('Invalid parameter
$uri.');
}
return $this->findCached($uri, false, true, false);
}
/**
* Returns true if uri is resolvable by using locator.
*
* @param string $uri
* @return bool
*/
public function isStream($uri)
{
try {
list ($scheme,) = $this->normalize($uri, true, true);
} catch (\Exception $e) {
return false;
}
return $this->schemeExists($scheme);
}
/**
* Returns the canonicalized URI on success. The resulting path will
have no '/./' or '/../' components.
* Trailing delimiter `/` is kept.
*
* By default (if $throwException parameter is not set to true) returns
false on failure.
*
* @param string $uri
* @param bool $throwException
* @param bool $splitStream
* @return string|array|bool
* @throws \BadMethodCallException
*/
public function normalize($uri, $throwException = false, $splitStream =
false)
{
if (!\is_string($uri)) {
if ($throwException) {
throw new \BadMethodCallException('Invalid parameter
$uri.');
}
return false;
}
$uri = preg_replace('|\\\|u', '/', $uri);
$segments = explode('://', $uri, 2);
$path = array_pop($segments);
$scheme = array_pop($segments) ?: 'file';
if ($path) {
$path = preg_replace('|\\\|u', '/', $path);
$parts = explode('/', $path);
$list = [];
foreach ($parts as $i => $part) {
if ($part === '..') {
$part = array_pop($list);
if ($part === null || $part === '' || (!$list
&& strpos($part, ':'))) {
if ($throwException) {
throw new \BadMethodCallException('Invalid
parameter $uri.');
}
return false;
}
} elseif (($i && $part === '') || $part
=== '.') {
continue;
} else {
$list[] = $part;
}
}
if (($l = end($parts)) === '' || $l === '.'
|| $l === '..') {
$list[] = '';
}
$path = implode('/', $list);
}
return $splitStream ? [$scheme, $path] : ($scheme !==
'file' ? "{$scheme}://{$path}" : $path);
}
/**
* Find highest priority instance from a resource.
*
* @param string $uri Input URI to be searched.
* @param bool $absolute Whether to return absolute path.
* @param bool $first Whether to return first path even if it
doesn't exist.
* @throws \BadMethodCallException
* @return string|bool
*/
public function findResource($uri, $absolute = true, $first = false)
{
if (!\is_string($uri)) {
throw new \BadMethodCallException('Invalid parameter
$uri.');
}
return $this->findCached($uri, false, $absolute, $first);
}
/**
* Find all instances from a resource.
*
* @param string $uri Input URI to be searched.
* @param bool $absolute Whether to return absolute path.
* @param bool $all Whether to return all paths even if they
don't exist.
* @throws \BadMethodCallException
* @return array
*/
public function findResources($uri, $absolute = true, $all = false)
{
if (!\is_string($uri)) {
throw new \BadMethodCallException('Invalid parameter
$uri.');
}
return $this->findCached($uri, true, $absolute, $all);
}
/**
* Find all instances from a list of resources.
*
* @param array $uris Input URIs to be searched.
* @param bool $absolute Whether to return absolute path.
* @param bool $all Whether to return all paths even if they
don't exist.
* @throws \BadMethodCallException
* @return array
*/
public function mergeResources(array $uris, $absolute = true, $all =
false)
{
$uris = array_unique($uris);
$lists = [[]];
foreach ($uris as $uri) {
$lists[] = $this->findResources($uri, $absolute, $all);
}
// TODO: In PHP 5.6+ use array_merge(...$list);
return call_user_func_array('array_merge', $lists);
}
/**
* Pre-fill cache by a stream.
*
* @param string $uri
* @return $this
*/
public function fillCache($uri)
{
$cacheKey = $uri . '@cache';
if (!isset($this->cache[$cacheKey])) {
$this->cache[$cacheKey] = true;
$iterator = new
\RecursiveIteratorIterator($this->getRecursiveIterator($uri),
\RecursiveIteratorIterator::SELF_FIRST);
/** @var UniformResourceIterator $uri */
foreach ($iterator as $item) {
$key = $item->getUrl() . '@010';
$this->cache[$key] = $item->getPathname();
}
}
return $this;
}
/**
* Reset locator cache.
*
* @param string $uri
* @return $this
*/
public function clearCache($uri = null)
{
if ($uri) {
$this->clearCached($uri, true, true, true);
$this->clearCached($uri, true, true, false);
$this->clearCached($uri, true, false, true);
$this->clearCached($uri, true, false, false);
$this->clearCached($uri, false, true, true);
$this->clearCached($uri, false, true, false);
$this->clearCached($uri, false, false, true);
$this->clearCached($uri, false, false, false);
} else {
$this->cache = [];
}
return $this;
}
/**
* @param string $uri
* @param bool $array
* @param bool $absolute
* @param bool $all
* @return array|string|bool
* @throws \BadMethodCallException
*/
protected function findCached($uri, $array, $absolute, $all)
{
// Local caching: make sure that the function gets only called at
once for each file.
$key = $uri .'@'. (int) $array . (int) $absolute . (int)
$all;
if (!isset($this->cache[$key])) {
try {
list ($scheme, $file) = $this->normalize($uri, true,
true);
if (!$file && $scheme === 'file') {
$file = $this->base;
}
$this->cache[$key] = $this->find($scheme, $file,
$array, $absolute, $all);
} catch (\BadMethodCallException $e) {
$this->cache[$key] = $array ? [] : false;
}
}
return $this->cache[$key];
}
protected function clearCached($uri, $array, $absolute, $all)
{
// Local caching: make sure that the function gets only called at
once for each file.
$key = $uri .'@'. (int) $array . (int) $absolute . (int)
$all;
unset($this->cache[$key]);
}
/**
* @param string $scheme
* @param string $file
* @param bool $array
* @param bool $absolute
* @param bool $all
*
* @throws \InvalidArgumentException
* @return array|string|bool
* @internal
*/
protected function find($scheme, $file, $array, $absolute, $all)
{
if (!isset($this->schemes[$scheme])) {
throw new \InvalidArgumentException("Invalid resource
{$scheme}://");
}
$results = $array ? [] : false;
foreach ($this->schemes[$scheme] as $prefix => $paths) {
if ($prefix && strpos($file, $prefix) !== 0) {
continue;
}
// Remove prefix from filename.
$filename = '/' . trim(substr($file,
\strlen($prefix)), '\/');
foreach ($paths as $path) {
if (\is_array($path)) {
// Handle scheme lookup.
$relPath = trim($path[1] . $filename, '/');
$found = $this->find($path[0], $relPath, $array,
$absolute, $all);
if ($found) {
if (!$array) {
return $found;
}
$results = array_merge($results, $found);
}
} else {
// TODO: We could provide some extra information about
the path to remove preg_match().
// Check absolute paths for both unix and windows
if (!$path || !preg_match('`^/|\w+:`',
$path)) {
// Handle relative path lookup.
$relPath = trim($path . $filename, '/');
$fullPath = $this->base . '/' .
$relPath;
} else {
// Handle absolute path lookup.
$fullPath = rtrim($path . $filename,
'/');
if (!$absolute) {
throw new
\RuntimeException("UniformResourceLocator: Absolute stream path with
relative lookup not allowed ({$prefix})", 500);
}
}
if ($all || file_exists($fullPath)) {
$current = $absolute ? $fullPath : $relPath;
if (!$array) {
return $current;
}
$results[] = $current;
}
}
}
}
return $results;
}
}
Session/src/Message.php000064400000003573151162104040011055
0ustar00<?php
namespace RocketTheme\Toolbox\Session;
/**
* Implements session messages.
*
* @package RocketTheme\Toolbox\Session
* @author RocketTheme
* @license MIT
*/
class Message
{
/**
* @var array|string[]
*/
protected $messages = [];
/**
* Add message to the queue.
*
* @param string $message
* @param string $scope
* @return $this
*/
public function add($message, $scope = 'default')
{
$key = md5($scope.'~'.$message);
$item = ['message' => $message, 'scope'
=> $scope];
// don't add duplicates
if (!array_key_exists($key, $this->messages)) {
$this->messages[$key] = $item;
}
return $this;
}
/**
* Clear message queue.
*
* @param string $scope
* @return $this
*/
public function clear($scope = null)
{
if ($scope === null) {
$this->messages = array();
} else {
foreach ($this->messages as $key => $message) {
if ($message['scope'] === $scope) {
unset($this->messages[$key]);
}
}
}
return $this;
}
/**
* Fetch all messages.
*
* @param string $scope
* @return array
*/
public function all($scope = null)
{
if ($scope === null) {
return array_values($this->messages);
}
$messages = array();
foreach ($this->messages as $message) {
if ($message['scope'] === $scope) {
$messages[] = $message;
}
}
return $messages;
}
/**
* Fetch and clear message queue.
*
* @param string $scope
* @return array
*/
public function fetch($scope = null)
{
$messages = $this->all($scope);
$this->clear($scope);
return $messages;
}
}
Session/src/Session.php000064400000012476151162104040011116
0ustar00<?php
namespace RocketTheme\Toolbox\Session;
/**
* Implements Session handling.
*
* @package RocketTheme\Toolbox\Session
* @author RocketTheme
* @license MIT
*/
class Session implements \IteratorAggregate
{
/**
* @var bool
*/
protected $started = false;
/**
* @var Session
*/
protected static $instance;
/**
* @param int $lifetime Defaults to 1800 seconds.
* @param string $path Cookie path.
* @param string $domain Optional, domain for the session
* @throws \RuntimeException
*/
public function __construct($lifetime, $path, $domain = null)
{
// Session is a singleton.
if (null !== self::$instance) {
throw new \RuntimeException('Session has already been
initialized.', 500);
}
// Destroy any existing sessions started with session.auto_start
if ($this->isSessionStarted()) {
session_unset();
session_destroy();
}
// Disable transparent sid support
ini_set('session.use_trans_sid', 0);
// Only allow cookies
ini_set('session.use_cookies', 1);
session_name('msF9kJcW');
session_set_cookie_params($lifetime, $path, $domain);
register_shutdown_function([$this, 'close']);
session_cache_limiter('nocache');
self::$instance = $this;
}
/**
* Get current session instance.
*
* @return Session
* @throws \RuntimeException
*/
public function instance()
{
if (null === self::$instance) {
throw new \RuntimeException("Session hasn't been
initialized.", 500);
}
return self::$instance;
}
/**
* Starts the session storage
*
* @return $this
* @throws \RuntimeException
*/
public function start()
{
// Protection against invalid session cookie names throwing
exception: http://php.net/manual/en/function.session-id.php#116836
if (isset($_COOKIE[session_name()]) &&
!preg_match('/^[-,a-zA-Z0-9]{1,128}$/',
$_COOKIE[session_name()])) {
unset($_COOKIE[session_name()]);
}
if (!session_start()) {
throw new \RuntimeException('Failed to start
session.', 500);
}
$this->started = true;
return $this;
}
/**
* Get session ID
*
* @return string|null Session ID
*/
public function getId()
{
return session_id();
}
/**
* Set session Id
*
* @param string $id Session ID
*
* @return $this
*/
public function setId($id)
{
session_id($id);
return $this;
}
/**
* Get session name
*
* @return string|null
*/
public function getName()
{
return session_name();
}
/**
* Set session name
*
* @param string $name
*
* @return $this
*/
public function setName($name)
{
session_name($name);
return $this;
}
/**
* Invalidates the current session.
*
* @return $this
*/
public function invalidate()
{
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params['path'], $params['domain'],
$params['secure'], $params['httponly']
);
session_unset();
session_destroy();
$this->started = false;
return $this;
}
/**
* Force the session to be saved and closed
*
* @return $this
*/
public function close()
{
if ($this->started) {
session_write_close();
}
$this->started = false;
return $this;
}
/**
* Checks if an attribute is defined.
*
* @param string $name The attribute name
*
* @return bool True if the attribute is defined, false otherwise
*/
public function __isset($name)
{
return isset($_SESSION[$name]);
}
/**
* Returns an attribute.
*
* @param string $name The attribute name
*
* @return mixed
*/
public function __get($name)
{
return isset($_SESSION[$name]) ? $_SESSION[$name] : null;
}
/**
* Sets an attribute.
*
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
$_SESSION[$name] = $value;
}
/**
* Removes an attribute.
*
* @param string $name
*/
public function __unset($name)
{
unset($_SESSION[$name]);
}
/**
* Returns attributes.
*
* @return array Attributes
*/
public function all()
{
return $_SESSION;
}
/**
* Retrieve an external iterator
*
* @return \ArrayIterator Return an ArrayIterator of $_SESSION
*/
public function getIterator()
{
return new \ArrayIterator($_SESSION);
}
/**
* Checks if the session was started.
*
* @return Boolean
*/
public function started()
{
return $this->started;
}
/**
* http://php.net/manual/en/function.session-status.php#113468
* Check if session is started nicely.
* @return bool
*/
protected function isSessionStarted()
{
return php_sapi_name() !== 'cli' ? session_id() !==
'' : false;
}
}
StreamWrapper/src/ReadOnlyStream.php000064400000005175151162104060013535
0ustar00<?php
namespace RocketTheme\Toolbox\StreamWrapper;
use RocketTheme\Toolbox\ResourceLocator\ResourceLocatorInterface;
/**
* Implements Read Only Streams.
*
* @package RocketTheme\Toolbox\StreamWrapper
* @author RocketTheme
* @license MIT
*/
class ReadOnlyStream extends Stream implements StreamInterface
{
/**
* @var ResourceLocatorInterface
*/
protected static $locator;
public function stream_open($uri, $mode, $options, &$opened_url)
{
if (!\in_array($mode, ['r', 'rb',
'rt'], true)) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error(sprintf('stream_open() write modes not
allowed for %s', $uri), E_USER_WARNING);
}
return false;
}
$path = $this->getPath($uri);
if (!$path) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error(sprintf('stream_open(): path for %s does
not exist', $uri), E_USER_WARNING);
}
return false;
}
$this->uri = $uri;
$this->handle = ($options & STREAM_REPORT_ERRORS) ?
fopen($path, $mode) : @fopen($path, $mode);
return (bool) $this->handle;
}
public function stream_lock($operation)
{
// Disallow exclusive lock or non-blocking lock requests
if (!\in_array($operation, [LOCK_SH, LOCK_UN, LOCK_SH | LOCK_NB],
true)) {
trigger_error(
sprintf('stream_lock() exclusive lock operations not
allowed for %s', $this->uri),
E_USER_WARNING
);
return false;
}
return flock($this->handle, $operation);
}
public function stream_metadata($uri, $option, $value)
{
if ($option !== STREAM_META_TOUCH) {
throw new
\BadMethodCallException(sprintf('stream_metadata() not allowed for
%s', $uri));
}
return parent::stream_metadata($uri, $option, $value);
}
public function stream_write($data)
{
throw new \BadMethodCallException(sprintf('stream_write() not
allowed for %s', $this->uri));
}
public function unlink($uri)
{
throw new \BadMethodCallException(sprintf('unlink() not
allowed for %s', $uri));
}
public function rename($from_uri, $to_uri)
{
throw new \BadMethodCallException(sprintf('rename() not
allowed for %s', $from_uri));
}
public function mkdir($uri, $mode, $options)
{
throw new \BadMethodCallException(sprintf('mkdir() not allowed
for %s', $uri));
}
public function rmdir($uri, $options)
{
throw new \BadMethodCallException(sprintf('rmdir() not allowed
for %s', $uri));
}
}
StreamWrapper/src/Stream.php000064400000017057151162104060012101
0ustar00<?php
namespace RocketTheme\Toolbox\StreamWrapper;
use RocketTheme\Toolbox\ResourceLocator\ResourceLocatorInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* Implements Read/Write Streams.
*
* @package RocketTheme\Toolbox\StreamWrapper
* @author RocketTheme
* @license MIT
*/
class Stream implements StreamInterface
{
/**
* @var string
*/
protected $uri;
/**
* A generic resource handle.
*
* @var Resource
*/
protected $handle = null;
/**
* @var ResourceLocatorInterface|UniformResourceLocator
*/
protected static $locator;
/**
* @param ResourceLocatorInterface $locator
*/
public static function setLocator(ResourceLocatorInterface $locator)
{
static::$locator = $locator;
}
public function stream_open($uri, $mode, $options, &$opened_url)
{
$path = $this->getPath($uri, $mode);
if (!$path) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error(sprintf('stream_open(): path for %s does
not exist', $uri), E_USER_WARNING);
}
return false;
}
$this->uri = $uri;
$this->handle = ($options & STREAM_REPORT_ERRORS) ?
fopen($path, $mode) : @fopen($path, $mode);
if (static::$locator instanceof UniformResourceLocator &&
!\in_array($mode, ['r', 'rb', 'rt'], true)) {
static::$locator->clearCache($this->uri);
}
return (bool) $this->handle;
}
public function stream_close()
{
return fclose($this->handle);
}
public function stream_lock($operation)
{
if (\in_array($operation, [LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB],
true)) {
return flock($this->handle, $operation);
}
return false;
}
public function stream_metadata($uri, $option, $value)
{
$path = $this->findPath($uri);
if ($path) {
switch ($option) {
case STREAM_META_TOUCH:
list($time, $atime) = $value;
return touch($path, $time, $atime);
case STREAM_META_OWNER_NAME:
case STREAM_META_OWNER:
return chown($path, $value);
case STREAM_META_GROUP_NAME:
case STREAM_META_GROUP:
return chgrp($path, $value);
case STREAM_META_ACCESS:
return chmod($path, $value);
}
}
return false;
}
public function stream_read($count)
{
return fread($this->handle, $count);
}
public function stream_write($data)
{
return fwrite($this->handle, $data);
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_seek($offset, $whence)
{
// fseek returns 0 on success and -1 on a failure.
return !fseek($this->handle, $offset, $whence);
}
public function stream_flush()
{
return fflush($this->handle);
}
public function stream_tell()
{
return ftell($this->handle);
}
public function stream_stat()
{
return fstat($this->handle);
}
/**
* @return bool
*/
public function stream_set_option($option, $arg1, $arg2)
{
switch ((int)$option) {
case STREAM_OPTION_BLOCKING:
return stream_set_blocking($this->handle, (int)$arg1);
case STREAM_OPTION_READ_TIMEOUT:
return stream_set_timeout($this->handle, (int)$arg1,
(int)$arg2);
case STREAM_OPTION_WRITE_BUFFER:
return stream_set_write_buffer($this->handle,
(int)$arg2);
default:
return false;
}
}
/**
* @param string $uri
* @return bool
*/
public function unlink($uri)
{
$path = $this->getPath($uri);
if (!$path) {
return false;
}
return unlink($path);
}
public function rename($fromUri, $toUri)
{
$fromPath = $this->getPath($fromUri);
$toPath = $this->getPath($toUri, 'w');
if (!$fromPath || !$toPath) {
return false;
}
if (static::$locator instanceof UniformResourceLocator) {
static::$locator->clearCache($fromUri);
static::$locator->clearCache($toUri);
}
return rename($fromPath, $toPath);
}
public function mkdir($uri, $mode, $options)
{
$recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
$path = $this->getPath($uri, $recursive ? 'd' :
'w');
if (!$path) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error(sprintf('mkdir(): Could not create
directory for %s', $uri), E_USER_WARNING);
}
return false;
}
if (static::$locator instanceof UniformResourceLocator) {
static::$locator->clearCache($uri);
}
return ($options & STREAM_REPORT_ERRORS) ? mkdir($path, $mode,
$recursive) : @mkdir($path, $mode, $recursive);
}
public function rmdir($uri, $options)
{
$path = $this->getPath($uri);
if (!$path) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error(sprintf('rmdir(): Directory not found
for %s', $uri), E_USER_WARNING);
}
return false;
}
if (static::$locator instanceof UniformResourceLocator) {
static::$locator->clearCache($uri);
}
return ($options & STREAM_REPORT_ERRORS) ? rmdir($path) :
@rmdir($path);
}
public function url_stat($uri, $flags)
{
$path = $this->getPath($uri);
if (!$path) {
return false;
}
// Suppress warnings if requested or if the file or directory does
not
// exist. This is consistent with PHPs plain filesystem stream
wrapper.
return ($flags & STREAM_URL_STAT_QUIET || !file_exists($path))
? @stat($path) : stat($path);
}
public function dir_opendir($uri, $options)
{
$path = $this->getPath($uri);
if (!$path) {
return false;
}
$this->uri = $uri;
$this->handle = opendir($path);
return (bool) $this->handle;
}
public function dir_readdir()
{
return readdir($this->handle);
}
public function dir_rewinddir()
{
rewinddir($this->handle);
return true;
}
public function dir_closedir()
{
closedir($this->handle);
return true;
}
protected function getPath($uri, $mode = null)
{
if ($mode === null) {
$mode = 'r';
}
$path = $this->findPath($uri);
if ($path && file_exists($path)) {
return $path;
}
if (strpos($mode[0], 'r') === 0) {
return false;
}
// We are either opening a file or creating directory.
list($scheme, $target) = explode('://', $uri, 2);
if ($target === '') {
return false;
}
$target = explode('/', $target);
$filename = [];
do {
$filename[] = array_pop($target);
$path = $this->findPath($scheme . '://' .
implode('/', $target));
} while ($target && !$path);
if (!$path) {
return false;
}
return $path . '/' . implode('/',
array_reverse($filename));
}
protected function findPath($uri)
{
return static::$locator &&
static::$locator->isStream($uri) ?
static::$locator->findResource($uri) : false;
}
}
StreamWrapper/src/StreamBuilder.php000064400000004175151162104060013405
0ustar00<?php
namespace RocketTheme\Toolbox\StreamWrapper;
/**
* Class StreamBuilder
* @package RocketTheme\Toolbox\StreamWrapper
*/
class StreamBuilder
{
/**
* @var array
*/
protected $items = [];
/**
* StreamBuilder constructor.
* @param StreamInterface[] $items
* @throws \InvalidArgumentException
*/
public function __construct(array $items = [])
{
foreach ($items as $scheme => $handler) {
$this->add($scheme, $handler);
}
}
/**
* @param string $scheme
* @param StreamInterface $handler
* @return $this
* @throws \InvalidArgumentException
*/
public function add($scheme, $handler)
{
if (isset($this->items[$scheme])) {
if ($handler === $this->items[$scheme]) {
return $this;
}
throw new \InvalidArgumentException("Stream
'{$scheme}' has already been initialized.");
}
if (!is_subclass_of($handler,
'RocketTheme\Toolbox\StreamWrapper\StreamInterface')) {
throw new \InvalidArgumentException("Stream
'{$scheme}' has unknown or invalid type.");
}
if (!@stream_wrapper_register($scheme, $handler)) {
throw new \InvalidArgumentException("Stream
'{$scheme}' could not be initialized.");
}
$this->items[$scheme] = $handler;
return $this;
}
/**
* @param string $scheme
* @return $this
*/
public function remove($scheme)
{
if (isset($this->items[$scheme])) {
stream_wrapper_unregister($scheme);
unset($this->items[$scheme]);
}
return $this;
}
/**
* @return array
*/
public function getStreams()
{
return $this->items;
}
/**
* @param string $scheme
* @return bool
*/
public function isStream($scheme)
{
return isset($this->items[$scheme]);
}
/**
* @param string $scheme
* @return StreamInterface|null
*/
public function getStreamType($scheme)
{
return isset($this->items[$scheme]) ? $this->items[$scheme] :
null;
}
}
StreamWrapper/src/StreamInterface.php000064400000020334151162104060013712
0ustar00<?php
namespace RocketTheme\Toolbox\StreamWrapper;
/**
* Defines Generic PHP stream wrapper interface.
*
* @package RocketTheme\Toolbox\StreamWrapper
* @author RocketTheme
* @license MIT
*
* @see http://www.php.net/manual/class.streamwrapper.php
*/
interface StreamInterface
{
/**
* Support for fopen(), file_get_contents(), file_put_contents() etc.
*
* @param string $uri A string containing the URI to the file to
open.
* @param string $mode The file mode ("r",
"wb" etc.).
* @param int $options A bit mask of STREAM_USE_PATH and
STREAM_REPORT_ERRORS.
* @param string $opened_url A string containing the path actually
opened.
*
* @return bool Returns TRUE if file was opened successfully.
* @see http://php.net/manual/streamwrapper.stream-open.php
*/
public function stream_open($uri, $mode, $options, &$opened_url);
/**
* Support for fclose().
*
* @return bool TRUE if stream was successfully closed.
* @see http://php.net/manual/streamwrapper.stream-close.php
*/
public function stream_close();
/**
* Support for flock().
*
* @param $operation
* One of the following:
* - LOCK_SH to acquire a shared lock (reader).
* - LOCK_EX to acquire an exclusive lock (writer).
* - LOCK_UN to release a lock (shared or exclusive).
* - LOCK_NB if you don't want flock() to block while locking
(not
* supported on Windows).
*
* @return bool Always returns TRUE at the present time.
* @see http://php.net/manual/streamwrapper.stream-lock.php
*/
public function stream_lock($operation);
/**
* Support for touch(), chmod(), chown(), chgrp().
*
* @param $path
* The file path or URL to set metadata. Note that in the case of a
URL, it must be a :// delimited URL.
* Other URL forms are not supported.
*
* @param $option
* One of:
* - STREAM_META_TOUCH The method was called in response to
touch()
* - STREAM_META_OWNER_NAME The method was called in response to
chown() with string parameter
* - STREAM_META_OWNER The method was called in response to
chown()
* - STREAM_META_GROUP_NAME The method was called in response to
chgrp()
* - STREAM_META_GROUP The method was called in response to
chgrp()
* - STREAM_META_ACCESS The method was called in response to
chmod()
*
* @param $value
* If option is
* - STREAM_META_TOUCH: Array consisting of two arguments
of the touch() function.
* - STREAM_META_OWNER_NAME or
* STREAM_META_GROUP_NAME: The name of the owner user/group as
string.
* - STREAM_META_OWNER or
* STREAM_META_GROUP: The value owner user/group argument
as integer.
* - STREAM_META_ACCESS: The argument of the chmod() as
integer.
*
* @return bool
* @see http://php.net/manual/en/streamwrapper.stream-metadata.php
*/
public function stream_metadata($path, $option, $value);
/**
* Support for fread(), file_get_contents() etc.
*
* @param $count
* Maximum number of bytes to be read.
*
* @return string|bool The string that was read, or FALSE in case of an
error.
* @see http://php.net/manual/streamwrapper.stream-read.php
*/
public function stream_read($count);
/**
* Support for fwrite(), file_put_contents() etc.
*
* @param $data
* The string to be written.
*
* @return int The number of bytes written (integer).
* @see http://php.net/manual/streamwrapper.stream-write.php
*/
public function stream_write($data);
/**
* Support for feof().
*
* @return bool TRUE if end-of-file has been reached.
* @see http://php.net/manual/streamwrapper.stream-eof.php
*/
public function stream_eof();
/**
* Support for fseek().
*
* @param $offset
* The byte offset to got to.
* @param $whence
* SEEK_SET, SEEK_CUR, or SEEK_END.
*
* @return bool TRUE on success.
* @see http://php.net/manual/streamwrapper.stream-seek.php
*/
public function stream_seek($offset, $whence);
/**
* Support for fflush().
*
* @return bool TRUE if data was successfully stored (or there was no
data to store).
* @see http://php.net/manual/streamwrapper.stream-flush.php
*/
public function stream_flush();
/**
* Support for ftell().
*
* @return int The current offset in bytes from the beginning of file.
* @see http://php.net/manual/streamwrapper.stream-tell.php
*/
public function stream_tell();
/**
* Support for fstat().
*
* @return array An array with file status, or FALSE in case of an
error - see fstat()
* @see http://php.net/manual/streamwrapper.stream-stat.php
*/
public function stream_stat();
/**
* Support for stream_set_option
* - stream_set_blocking()
* - stream_set_timeout()
* - stream_set_write_buffer()
*
* @param int $option
* @param int $arg1
* @param int $arg2
* @return bool
*
* @see http://php.net/manual/streamwrapper.stream-set-option.php
*/
public function stream_set_option($option, $arg1, $arg2);
/**
* Support for unlink().
*
* @param $uri
* A string containing the URI to the resource to delete.
*
* @return
* TRUE if resource was successfully deleted.
* @see http://php.net/manual/streamwrapper.unlink.php
*/
public function unlink($uri);
/**
* Support for rename().
*
* @param $from_uri ,
* The URI to the file to rename.
* @param $to_uri
* The new URI for file.
*
* @return bool TRUE if file was successfully renamed.
* @see http://php.net/manual/streamwrapper.rename.php
*/
public function rename($from_uri, $to_uri);
/**
* Support for mkdir().
*
* @param $uri
* A string containing the URI to the directory to create.
* @param $mode
* Permission flags - see mkdir().
* @param $options
* A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
*
* @return bool TRUE if directory was successfully created.
* @see http://php.net/manual/streamwrapper.mkdir.php
*/
public function mkdir($uri, $mode, $options);
/**
* Support for rmdir().
*
* @param $uri
* A string containing the URI to the directory to delete.
* @param $options
* A bit mask of STREAM_REPORT_ERRORS.
*
* @return
* TRUE if directory was successfully removed.
*
* @see http://php.net/manual/streamwrapper.rmdir.php
*/
public function rmdir($uri, $options);
/**
* Support for stat().
*
* @param $uri
* A string containing the URI to get information about.
* @param $flags
* A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
*
* @return array An array with file status, or FALSE in case of an
error - see fstat()
* @see http://php.net/manual/streamwrapper.url-stat.php
*/
public function url_stat($uri, $flags);
/**
* Support for opendir().
*
* @param $uri
* A string containing the URI to the directory to open.
* @param $options
* Unknown (parameter is not documented in PHP Manual).
*
* @return bool TRUE on success.
* @see http://php.net/manual/streamwrapper.dir-opendir.php
*/
public function dir_opendir($uri, $options);
/**
* Support for readdir().
*
* @return string The next filename, or FALSE if there are no more
files in the directory.
* @see http://php.net/manual/streamwrapper.dir-readdir.php
*/
public function dir_readdir();
/**
* Support for rewinddir().
*
* @return bool TRUE on success.
* @see http://php.net/manual/streamwrapper.dir-rewinddir.php
*/
public function dir_rewinddir();
/**
* Support for closedir().
*
* @return bool TRUE on success.
* @see http://php.net/manual/streamwrapper.dir-closedir.php
*/
public function dir_closedir();
}