Ground work for allowing multiple configurations

This commit is contained in:
Matias Griese
2014-09-03 09:57:46 +03:00
parent b507815eef
commit affa768efb
57 changed files with 1873 additions and 550 deletions

View File

@@ -3,7 +3,12 @@
use Symfony\Component\Console\Application;
require_once(__DIR__ . '/../vendor/autoload.php');
$autoload = __DIR__ . '/../vendor/autoload.php';
if (!is_file($autoload)) {
exit('Please run: composer install -o');
}
require_once $autoload;
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');

View File

@@ -18,8 +18,15 @@
"ircmaxell/password-compat": "1.0.*",
"mrclay/minify": "~2.2",
"ornicar/php-user-agent": "1.0.*",
"pimple/pimple": "~3.0"
"pimple/pimple": "~3.0",
"rockettheme/toolbox": "dev-develop"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/rockettheme/toolbox"
}
],
"autoload": {
"psr-4": {
"Grav\\": "system/src/"

View File

@@ -0,0 +1,7 @@
title: File Streams
validation: loose
form:
fields:
schemes.xxx:
type: array

View File

@@ -1,10 +1,4 @@
schemes:
plugin:
type: ReadOnlyStream
paths:
- user/plugins
- system/plugins
user:
type: ReadOnlyStream
paths:
@@ -15,21 +9,6 @@ schemes:
# paths:
# - assets
# cache:
# type: ReadOnlyStream
# paths:
# - cache
# log:
# type: ReadOnlyStream
# paths:
# - logs
page:
type: ReadOnlyStream
paths:
- user/pages
account:
type: ReadOnlyStream
paths:
@@ -39,3 +18,21 @@ schemes:
type: ReadOnlyStream
paths:
- user/data
page:
type: ReadOnlyStream
prefixes:
'/':
- user/pages
image:
prefixes:
'/':
- user/images
- system/images
theme:
prefixes:
'/':
- user/themes
- system/themes

View File

@@ -6,10 +6,10 @@ define('GRAV_VERSION', '0.8.1');
define('DS', '/');
// Directories and Paths
if (!defined('ROOT_DIR')) {
define('ROOT_DIR', getcwd() .'/');
if (!defined('GRAV_ROOT')) {
define('GRAV_ROOT', getcwd());
}
define('GRAV_ROOT', ROOT_DIR);
define('ROOT_DIR', GRAV_ROOT . '/');
define('USER_PATH', 'user/');
define('USER_DIR', ROOT_DIR . USER_PATH);
define('SYSTEM_DIR', ROOT_DIR .'system/');

View File

@@ -1,259 +0,0 @@
<?php
namespace Grav\Common;
use Grav\Common\Data\Blueprints;
use Grav\Common\Data\Data;
use Grav\Common\Filesystem\File;
use Grav\Common\Filesystem\Folder;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
* @license MIT
*/
class Config extends Data
{
/**
* @var string Configuration location in the disk.
*/
public $filename;
/**
* @var string MD5 from the files.
*/
public $key;
/**
* @var array Configuration file list.
*/
public $files = array();
/**
* @var bool Flag to tell if configuration needs to be saved.
*/
public $updated = false;
public $issues = array();
/**
* Constructor.
*/
public function __construct($filename)
{
$this->filename = realpath(dirname($filename)) . '/' . basename($filename);
$this->reload(false);
}
/**
* Force reload of the configuration from the disk.
*
* @param bool $force
* @return $this
*/
public function reload($force = true)
{
// Build file map.
$files = $this->build();
$key = md5(serialize($files) . GRAV_VERSION);
if ($force || $key != $this->key) {
// First take non-blocking lock to the file.
File\Config::instance($this->filename)->lock(false);
// Reset configuration.
$this->items = array();
$this->files = array();
$this->init($files);
$this->key = $key;
}
return $this;
}
/**
* Save configuration into file.
*
* Note: Only saves the file if updated flag is set!
*
* @return $this
* @throws \RuntimeException
*/
public function save()
{
// If configuration was updated, store it as cached version.
try {
$file = File\Config::instance($this->filename);
// Only save configuration file if it wasn't locked. Also invalidate opcache after saving.
// This prevents us from saving the file multiple times in a row and gives faster recovery.
if ($file->locked() !== false) {
$file->save($this);
$file->unlock();
}
$this->updated = false;
} catch (\Exception $e) {
$this->issues[] = 'Writing configuration into cache failed.';
//throw new \RuntimeException('Writing configuration into cache failed.', 500, $e);
}
return $this;
}
/**
* Load configuration.
*
* @param Grav $grav
* @return \Grav\Common\Config
*/
public static function instance(Grav $grav)
{
$filename = $grav['config_path'];
// Load cached version if available..
if (file_exists($filename)) {
require_once $filename;
if (class_exists('\Grav\Config')) {
$instance = new \Grav\Config($filename);
}
}
// Or initialize new configuration object..
if (!isset($instance)) {
$instance = new static($filename);
}
// If configuration was updated, store it as cached version.
if ($instance->updated) {
$instance->save();
}
// If not set, add manually current base url.
if (empty($instance->items['system']['base_url_absolute'])) {
$instance->items['system']['base_url_absolute'] = $grav['uri']->rootUrl(true);
}
if (empty($instance->items['system']['base_url_relative'])) {
$instance->items['system']['base_url_relative'] = $grav['uri']->rootUrl(false);
}
return $instance;
}
/**
* Convert configuration into an array.
*
* @return array
*/
public function toArray()
{
return array('key' => $this->key, 'files' => $this->files, 'items' => $this->items);
}
/**
* Initialize object by loading all the configuration files.
*
* @param array $files
*/
protected function init(array $files)
{
$this->updated = true;
// Combine all configuration files into one larger lookup table (only keys matter).
$allFiles = $files['user'] + $files['plugins'] + $files['system'];
// Then sort the files to have all parent nodes first.
// This is to make sure that child nodes override parents content.
uksort(
$allFiles,
function($a, $b) {
$diff = substr_count($a, '/') - substr_count($b, '/');
return $diff ? $diff : strcmp($a, $b);
}
);
$systemBlueprints = new Blueprints(SYSTEM_DIR . 'blueprints');
$pluginBlueprints = new Blueprints(USER_DIR);
$items = array();
foreach ($allFiles as $name => $dummy) {
$lookup = array(
'system' => SYSTEM_DIR . 'config/' . $name . YAML_EXT,
'plugins' => USER_DIR . $name . '/' . basename($name) . YAML_EXT,
'user' => USER_DIR . 'config/' . $name . YAML_EXT,
);
if (strpos($name, 'plugins/') === 0) {
$blueprint = $pluginBlueprints->get("{$name}/blueprints");
} else {
$blueprint = $systemBlueprints->get($name);
}
$data = new Data(array(), $blueprint);
foreach ($lookup as $key => $path) {
if (is_file($path)) {
$data->merge(File\Yaml::instance($path)->content());
}
}
// $data->validate();
// $data->filter();
// Find the current sub-tree location.
$current = &$items;
$parts = explode('/', $name);
foreach ($parts as $part) {
if (!isset($current[$part])) {
$current[$part] = array();
}
$current = &$current[$part];
}
// Handle both updated and deleted configuration files.
$current = $data->toArray();
}
$this->items = $items;
$this->files = $files;
}
/**
* Build a list of configuration files with their timestamps. Used for loading settings and caching them.
*
* @return array
* @internal
*/
protected function build()
{
// Find all plugins with default configuration options.
$plugins = array();
$iterator = new \DirectoryIterator(PLUGINS_DIR);
/** @var \DirectoryIterator $plugin */
foreach ($iterator as $plugin) {
$name = $plugin->getBasename();
$file = $plugin->getPathname() . DS . $name . YAML_EXT;
if (!is_file($file)) {
continue;
}
$modified = filemtime($file);
$plugins["plugins/{$name}"] = $modified;
}
// Find all system and user configuration files.
$options = array(
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => array('key' => '|\.yaml$|'),
'key' => 'SubPathname',
'value' => 'MTime'
);
$system = Folder::all(SYSTEM_DIR . 'config', $options);
$user = Folder::all(USER_DIR . 'config', $options);
return array('system' => $system, 'plugins' => $plugins, 'user' => $user);
}
}

View File

@@ -0,0 +1,207 @@
<?php
namespace Grav\Common\Config;
use Grav\Common\File\CompiledYaml;
use Grav\Common\Grav;
use Grav\Common\GravTrait;
use Grav\Component\Blueprints\Blueprints as BaseBlueprints;
use Grav\Component\Filesystem\File\Php;
use Grav\Component\Filesystem\Folder;
use Grav\Component\Filesystem\ResourceLocator;
/**
* The Blueprints class contains configuration rules.
*
* @author RocketTheme
* @license MIT
*/
class Blueprints extends BaseBlueprints
{
protected $grav;
protected $files = [];
public function __construct(array $serialized = null, Grav $grav = null)
{
parent::__construct($serialized);
$this->grav = $grav ?: Grav::instance();
}
public function init()
{
/** @var ResourceLocator $locator */
$locator = $this->grav['locator'];
$blueprints = $locator->findResources('blueprints:///config');
$plugins = $locator->findResources('plugin:///');
$blueprintFiles = $this->getBlueprintFiles($blueprints, $plugins);
$this->loadCompiledBlueprints($plugins + $blueprints, $blueprintFiles);
}
protected function loadCompiledBlueprints($blueprints, $blueprintFiles)
{
$checksum = md5(serialize($blueprints));
$filename = CACHE_DIR . 'compiled/blueprints/' . $checksum .'.php';
$checksum .= ':'.md5(serialize($blueprintFiles));
$class = get_class($this);
$file = Php::instance($filename);
if ($file->exists()) {
$cache = $file->exists() ? $file->content() : null;
} else {
$cache = null;
}
// Load real file if cache isn't up to date (or is invalid).
if (
!is_array($cache)
|| empty($cache['checksum'])
|| empty($cache['$class'])
|| $cache['checksum'] != $checksum
|| $cache['@class'] != $class
) {
// Attempt to lock the file for writing.
$file->lock(false);
// Load blueprints.
$this->blueprints = new Blueprints();
foreach ($blueprintFiles as $key => $files) {
$this->loadBlueprints($key);
}
$cache = [
'@class' => $class,
'checksum' => $checksum,
'files' => $blueprintFiles,
'data' => $this->blueprints->toArray()
];
// If compiled file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$file->save($cache);
$file->unlock();
}
} else {
$this->blueprints = new Blueprints($cache['data']);
}
}
/**
* Load global blueprints.
*
* @param string $key
* @param array $files
*/
public function loadBlueprints($key, array $files = null)
{
if (is_null($files)) {
$files = $this->files[$key];
}
foreach ($files as $name => $item) {
$file = CompiledYaml::instance($item['file']);
$this->blueprints->embed($name, $file->content(), '/');
}
}
/**
* Get all blueprint files (including plugins).
*
* @param array $blueprints
* @param array $plugins
* @return array
*/
protected function getBlueprintFiles(array $blueprints, array $plugins)
{
$list = [];
foreach (array_reverse($plugins) as $folder) {
$list += $this->detectPlugins($folder, true);
}
foreach (array_reverse($blueprints) as $folder) {
$list += $this->detectConfig($folder, true);
}
return $list;
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @param bool $blueprints
* @return array
* @internal
*/
protected function detectPlugins($lookup = SYSTEM_DIR, $blueprints = false)
{
$find = $blueprints ? 'blueprints.yaml' : '.yaml';
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
$path = trim(Folder::getRelativePath($lookup), '/');
if (isset($this->{$location}[$path])) {
return [$path => $this->{$location}[$path]];
}
$list = [];
if (is_dir($lookup)) {
$iterator = new \DirectoryIterator($lookup);
/** @var \DirectoryIterator $directory */
foreach ($iterator as $directory) {
if (!$directory->isDir() || $directory->isDot()) {
continue;
}
$name = $directory->getBasename();
$filename = "{$path}/{$name}/" . ($find && $find[0] != '.' ? $find : $name . $find);
if (is_file($filename)) {
$list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)];
}
}
}
$this->{$location}[$path] = $list;
return [$path => $list];
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @param bool $blueprints
* @return array
* @internal
*/
protected function detectConfig($lookup = SYSTEM_DIR, $blueprints = false)
{
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
$path = trim(Folder::getRelativePath($lookup), '/');
if (isset($this->{$location}[$path])) {
return [$path => $this->{$location}[$path]];
}
if (is_dir($lookup)) {
// Find all system and user configuration files.
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => [
'key' => '|\.yaml$|',
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
}],
'key' => 'SubPathname'
];
$list = Folder::all($lookup, $options);
} else {
$list = [];
}
$this->{$location}[$path] = $list;
return [$path => $list];
}
}

View File

@@ -0,0 +1,414 @@
<?php
namespace Grav\Common\Config;
use Grav\Common\File\CompiledYaml;
use Grav\Common\Grav;
use Grav\Common\GravTrait;
use Grav\Common\Uri;
use Grav\Component\Blueprints\Blueprints;
use Grav\Component\Data\Data;
use Grav\Component\Filesystem\File\Php;
use Grav\Component\Filesystem\Folder;
use Grav\Component\Filesystem\ResourceLocator;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
* @license MIT
*/
class Config extends Data
{
protected $grav;
protected $default = [
'streams' => [
'schemes' => [
'blueprints' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'/' => ['user/blueprints', 'system/blueprints'],
]
],
'config' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'/' => ['user/config', 'system/config'],
]
],
'plugin' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['user/plugins'],
]
],
'cache' => [
'type' => 'Stream',
'prefixes' => [
'' => ['cache']
]
],
'logs' => [
'type' => 'Stream',
'prefixes' => [
'' => ['logs']
]
]
],
],
];
protected $blueprintFiles = [];
protected $configFiles = [];
protected $checksum;
protected $timestamp;
public function __construct(array $items = array(), Grav $grav = null)
{
$this->grav = $grav ?: Grav::instance();
if (isset($items['@class']) && $items['@class'] = get_class($this)) {
// Loading pre-compiled configuration.
$this->timestamp = (int) $items['timestamp'];
$this->checksum = (string) $items['checksum'];
$this->items = (array) $items['data'];
} else {
parent::__construct($items + $this->default);
}
}
public function init()
{
$checksum = $this->checksum();
if ($checksum == $this->checksum) {
return;
}
$this->checksum = $checksum;
/** @var Uri $uri */
$uri = $this->grav['uri'];
// If not set, add manually current base url.
$this->def('system.base_url_absolute', $uri->rootUrl(true));
$this->def('system.base_url_relative', $uri->rootUrl(false));
/** @var ResourceLocator $locator */
$locator = $this->grav['locator'];
$configs = $locator->findResources('config:///');
$blueprints = $locator->findResources('blueprints:///config');
$plugins = $locator->findResources('plugin:///');
$this->loadCompiledBlueprints($blueprints, $plugins, 'master');
$this->loadCompiledConfig($configs, $plugins, 'master');
}
public function checksum()
{
$checkBlueprints = $this->get('system.cache.check.blueprints', true);
$checkConfig = $this->get('system.cache.check.config', true);
$checkSystem = $this->get('system.cache.check.system', true);
if (!$checkBlueprints && !$checkConfig && !$checkSystem) {
return false;
}
/** @var ResourceLocator $locator */
$locator = $this->grav['locator'];
$configs = $locator->findResources('config:///');
$blueprints = $locator->findResources('blueprints:///config');
$plugins = $locator->findResources('plugin:///');
// Generate checksum according to the configuration settings.
if (!$checkConfig) {
// Just check changes in system.yaml files and ignore all the other files.
$cc = $checkSystem ? $this->detectFile($configs, 'system') : [];
} else {
// Check changes in all configuration files.
$cc = $this->getConfigFiles($configs, $plugins);
}
$cb = $checkBlueprints ? $this->getBlueprintFiles($blueprints, $plugins) : [];
return md5(serialize([$cc, $cb]));
}
protected function loadCompiledBlueprints($blueprints, $plugins, $filename = null)
{
$checksum = md5(serialize($blueprints));
$filename = $filename
? CACHE_DIR . 'compiled/blueprints/' . $filename .'.php'
: CACHE_DIR . 'compiled/blueprints/' . $checksum .'.php';
$file = Php::instance($filename);
if ($file->exists()) {
$cache = $file->exists() ? $file->content() : null;
} else {
$cache = null;
}
$blueprintFiles = $this->getBlueprintFiles($blueprints, $plugins);
$checksum .= ':'.md5(serialize($blueprintFiles));
$class = get_class($this);
// Load real file if cache isn't up to date (or is invalid).
if (
!is_array($cache)
|| empty($cache['checksum'])
|| empty($cache['$class'])
|| $cache['checksum'] != $checksum
|| $cache['@class'] != $class
) {
// Attempt to lock the file for writing.
$file->lock(false);
// Load blueprints.
$this->blueprints = new Blueprints();
foreach ($blueprintFiles as $key => $files) {
$this->loadBlueprints($key);
}
$cache = [
'@class' => $class,
'checksum' => $checksum,
'files' => $blueprintFiles,
'data' => $this->blueprints->toArray()
];
// If compiled file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$file->save($cache);
$file->unlock();
}
} else {
$this->blueprints = new Blueprints($cache['data']);
}
}
protected function loadCompiledConfig($configs, $plugins, $filename = null)
{
$checksum = md5(serialize($configs));
$filename = $filename
? CACHE_DIR . 'compiled/config/' . $filename .'.php'
: CACHE_DIR . 'compiled/config/' . $checksum .'.php';
$file = Php::instance($filename);
if ($file->exists()) {
$cache = $file->exists() ? $file->content() : null;
} else {
$cache = null;
}
$configFiles = $this->getConfigFiles($configs, $plugins);
$checksum .= ':'.md5(serialize($configFiles));
print_r($configFiles);
echo $cache['checksum'].' '.$checksum;
$class = get_class($this);
// Load real file if cache isn't up to date (or is invalid).
if (
!is_array($cache)
|| $cache['checksum'] != $checksum
|| $cache['@class'] != $class
) {
// Attempt to lock the file for writing.
$file->lock(false);
// Load configuration.
foreach ($configFiles as $key => $files) {
$this->loadConfig($key);
}
$cache = [
'@class' => $class,
'timestamp' => time(),
'checksum' => $this->checksum,
'data' => $this->toArray()
];
// If compiled file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$file->save($cache);
$file->unlock();
}
}
$this->items = $cache['data'];
}
/**
* Load global blueprints.
*
* @param string $key
* @param array $files
*/
public function loadBlueprints($key, array $files = null)
{
if (is_null($files)) {
$files = $this->blueprintFiles[$key];
}
foreach ($files as $name => $item) {
$file = CompiledYaml::instance($item['file']);
$this->blueprints->embed($name, $file->content(), '/');
}
}
/**
* Load global configuration.
*
* @param string $key
* @param array $files
*/
public function loadConfig($key, array $files = null)
{
if (is_null($files)) {
$files = $this->configFiles[$key];
}
foreach ($files as $name => $item) {
$file = CompiledYaml::instance($item['file']);
$this->join($name, $file->content(), '/');
}
}
/**
* Get all blueprint files (including plugins).
*
* @param array $blueprints
* @param array $plugins
* @return array
*/
protected function getBlueprintFiles(array $blueprints, array $plugins)
{
$list = [];
foreach (array_reverse($plugins) as $folder) {
$list += $this->detectPlugins($folder, true);
}
foreach (array_reverse($blueprints) as $folder) {
$list += $this->detectConfig($folder, true);
}
return $list;
}
/**
* Get all configuration files.
*
* @param array $configs
* @param array $plugins
* @return array
*/
protected function getConfigFiles(array $configs, array $plugins)
{
$list = [];
foreach (array_reverse($plugins) as $folder) {
$list += $this->detectPlugins($folder);
}
foreach (array_reverse($configs) as $folder) {
$list += $this->detectConfig($folder);
}
return $list;
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @param bool $blueprints
* @return array
* @internal
*/
protected function detectPlugins($lookup = SYSTEM_DIR, $blueprints = false)
{
$find = $blueprints ? 'blueprints.yaml' : '.yaml';
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
$path = trim(Folder::getRelativePath($lookup), '/');
if (isset($this->{$location}[$path])) {
return [$path => $this->{$location}[$path]];
}
$list = [];
if (is_dir($lookup)) {
$iterator = new \DirectoryIterator($lookup);
/** @var \DirectoryIterator $directory */
foreach ($iterator as $directory) {
if (!$directory->isDir() || $directory->isDot()) {
continue;
}
$name = $directory->getBasename();
$filename = "{$path}/{$name}/" . ($find && $find[0] != '.' ? $find : $name . $find);
if (is_file($filename)) {
$list["plugins/{$name}"] = ['file' => $filename, 'modified' => filemtime($filename)];
}
}
}
$this->{$location}[$path] = $list;
return [$path => $list];
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @param bool $blueprints
* @return array
* @internal
*/
protected function detectConfig($lookup = SYSTEM_DIR, $blueprints = false)
{
$location = $blueprints ? 'blueprintFiles' : 'configFiles';
$path = trim(Folder::getRelativePath($lookup), '/');
if (isset($this->{$location}[$path])) {
return [$path => $this->{$location}[$path]];
}
if (is_dir($lookup)) {
// Find all system and user configuration files.
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => [
'key' => '|\.yaml$|',
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
}],
'key' => 'SubPathname'
];
$list = Folder::all($lookup, $options);
} else {
$list = [];
}
$this->{$location}[$path] = $list;
return [$path => $list];
}
/**
* Detects all instances of the file and returns last modification time.
*
* @param string $lookups Locations to look up from.
* @param string $name
* @return array
* @internal
*/
protected function detectFile(array $lookups, $name)
{
$list = [];
$filename = "{$name}.yaml";
foreach ($lookups as $lookup) {
$path = trim(Folder::getRelativePath($lookup), '/');
if (is_file("{$lookup}/{$filename}")) {
$modified = filemtime("{$lookup}/{$filename}");
} else {
$modified = 0;
}
$list[$path] = [$name => ['file' => "{$path}/{$filename}", 'modified' => $modified]];
}
return $list;
}
}

View File

@@ -0,0 +1,328 @@
<?php
namespace Grav\Common;
use Grav\Component\Data\Blueprints;
use Grav\Component\Data\Data;
use Grav\Component\Filesystem\File;
use Grav\Component\Filesystem\Folder;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
* @license MIT
*/
class Config extends Data
{
/**
* @var string Configuration location in the disk.
*/
public $filename;
/**
* @var string Path to YAML configuration files.
*/
public $path;
/**
* @var string MD5 from the files.
*/
public $key;
/**
* @var array Configuration file list.
*/
public $files = array();
/**
* @var bool Flag to tell if configuration needs to be saved.
*/
public $updated = false;
/**
* Gets configuration instance.
*
* @param string $filename
* @param string $path
* @return static
*/
public static function instance($filename, $path = SYSTEM_DIR)
{
// Load cached version if available..
if (file_exists($filename)) {
$data = require_once $filename;
if (is_array($data) && isset($data['@class']) && $data['@class'] == __CLASS__) {
$instance = new static($filename, $path, $data);
}
}
// Or initialize new configuration object..
if (!isset($instance)) {
$instance = new static($filename, $path);
}
// If configuration was updated, store it as cached version.
/** @var Config $instance */
if ($instance->updated) {
$instance->save();
}
return $instance;
}
/**
* Constructor.
* @param string $filename
* @param string $path
* @param array $data
*/
public function __construct($filename, $path, array $data = null)
{
$this->filename = (string) $filename;
$this->path = (string) $path;
if ($data) {
$this->key = $data['key'];
$this->files = $data['files'];
$this->items = $data['items'];
}
$this->reload(false);
print_r($this->getBlueprintFiles());
print_r($this->getConfigFiles());
}
/**
* Force reload of the configuration from the disk.
*
* @param bool $force
* @return $this
*/
public function reload($force = true)
{
// Build file map.
$files = $this->build();
$key = $this->getKey($files);
if ($force || $key != $this->key) {
// First take non-blocking lock to the file.
File\Php::instance($this->filename)->lock(false);
// Reset configuration.
$this->items = array();
$this->files = array();
$this->init($files);
$this->key = $key;
}
return $this;
}
/**
* Save configuration into file.
*
* Note: Only saves the file if updated flag is set!
*
* @return $this
* @throws \RuntimeException
*/
public function save()
{
// If configuration was updated, store it as cached version.
try {
$file = File\Php::instance($this->filename);
// Only save configuration file if it was successfully locked to prevent multiple saves.
if ($file->locked() !== false) {
$file->save($this->toArray());
$file->unlock();
}
$this->updated = false;
} catch (\Exception $e) {
// TODO: do not require saving to succeed, but display some kind of error anyway.
throw new \RuntimeException('Writing configuration to cache folder failed.', 500, $e);
}
return $this;
}
/**
* Convert configuration into an array.
*
* @return array
*/
public function toArray()
{
return [
'@class' => get_class($this),
'key' => $this->key,
'files' => $this->files,
'items' => $this->items
];
}
/**
* @param $files
* @return string
*/
protected function getKey(&$files)
{
return md5(serialize($files) . GRAV_VERSION);
}
/**
* Initialize object by loading all the configuration files.
*
* @param array $files
*/
protected function init(array $files)
{
$this->updated = true;
// Then sort the files to have all parent nodes first.
// This is to make sure that child nodes override parents content.
uksort(
$files,
function($a, $b) {
$diff = substr_count($a, '/') - substr_count($b, '/');
return $diff ? $diff : strcmp($a, $b);
}
);
$blueprints = new Blueprints($this->path . '/blueprints/config');
$items = array();
foreach ($files as $name => $dummy) {
$lookup = $this->path . '/config/' . $name . '.yaml';
$blueprint = $blueprints->get($name);
$data = new Data(array(), $blueprint);
if (is_file($lookup)) {
$data->merge(File\Yaml::instance($lookup)->content());
}
// Find the current sub-tree location.
$current = &$items;
$parts = explode('/', $name);
foreach ($parts as $part) {
if (!isset($current[$part])) {
$current[$part] = array();
}
$current = &$current[$part];
}
// Handle both updated and deleted configuration files.
$current = $data->toArray();
}
$this->items = $items;
$this->files = $files;
}
/**
* Build a list of configuration files with their timestamps. Used for loading settings and caching them.
*
* @return array
* @internal
*/
protected function build()
{
// Find all system and user configuration files.
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => ['key' => '|\.yaml$|'],
'key' => 'SubPathname',
'value' => 'MTime'
];
return Folder::all($this->path . '/config', $options);
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @param string $find Filename or extension to find.
* @return array
*/
protected function detectPlugins($lookup = SYSTEM_DIR, $find = '.yaml')
{
if (!is_dir($lookup)) {
return [];
}
$list = [];
$iterator = new \DirectoryIterator($lookup);
$path = trim(Folder::getRelativePath($lookup), '/');
/** @var \DirectoryIterator $directory */
foreach ($iterator as $directory) {
if (!$directory->isDir() || $directory->isDot()) {
continue;
}
$name = $directory->getBasename();
$filename = "{$path}/{$name}/" . ($find && $find[0] != '.' ? $find : $name . $find);
if (is_file($filename)) {
$list["plugins/{$name}"] = ['file' => $filename, 'mtime' => filemtime($filename)];
}
}
return [$path => $list];
}
/**
* Detects all plugins with a configuration file and returns last modification time.
*
* @param string $lookup Location to look up from.
* @return array
*/
protected function detectConfig($lookup = SYSTEM_DIR)
{
if (!is_dir($lookup)) {
return [];
}
$path = trim(Folder::getRelativePath($lookup), '/');
// Find all system and user configuration files.
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => [
'key' => '|\.yaml$|',
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
return ['file' => "{$path}/{$file->getSubPathname()}", 'mtime' => $file->getMTime()];
}],
'key' => 'SubPathname'
];
$list = Folder::all($lookup, $options);
return [$path => $list];
}
public function getBlueprintFiles()
{
$list = [];
$list += $this->detectPlugins(SYSTEM_DIR . 'plugins', 'blueprints.yaml');
$list += $this->detectConfig(SYSTEM_DIR . 'blueprints/config');
$list += $this->detectPlugins(PLUGINS_DIR, 'blueprints.yaml');
$list += $this->detectConfig(USER_DIR . 'blueprints/config');
return $list;
}
public function getConfigFiles()
{
$list = [];
$list += $this->detectPlugins(SYSTEM_DIR . 'plugins');
$list += $this->detectConfig(SYSTEM_DIR . 'config');
$list += $this->detectPlugins(PLUGINS_DIR);
$list += $this->detectConfig(USER_DIR . 'config');
return $list;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Grav\Common\File;
use Grav\Component\Filesystem\File\Php;
trait CompiledFile
{
/**
* Get/set parsed file contents.
*
* @param mixed $var
* @return string
*/
public function content($var = null)
{
// If nothing has been loaded, attempt to get pre-compiled version of the file first.
if ($var === null && $this->raw === null && $this->content === null) {
$key = md5($this->filename);
$file = Php::instance(CACHE_DIR . "/compiled/files/{$key}{$this->extension}.php");
$modified = $this->modified();
$class = get_class($this);
if ($file->exists()) {
$cache = $file->exists() ? $file->content() : null;
} else {
$cache = null;
}
// Load real file if cache isn't up to date (or is invalid).
if (
!is_array($cache)
|| $cache['modified'] != $modified
|| $cache['filename'] != $this->filename
|| $cache['@class'] != $class
) {
// Attempt to lock the file for writing.
$file->lock(false);
// Decode RAW file into compiled array.
$data = $this->decode($this->raw());
$cache = [
'@class' => $class,
'filename' => $this->filename,
'modified' => $modified,
'data' => $data
];
// If compiled file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$file->save($cache);
$file->unlock();
}
}
$this->content = $cache['data'];
}
return parent::content($var);
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Grav\Common\File;
use Grav\Component\Filesystem\File\Yaml;
class CompiledYaml extends Yaml
{
use CompiledFile;
}

View File

@@ -1,6 +1,9 @@
<?php
namespace Grav\Common;
use Grav\Common\Config\Blueprints;
use Grav\Common\Config\Config;
use Grav\Common\Config\ConfigServiceProvider;
use Grav\Common\Service\StreamsServiceProvider;
use Grav\Component\DI\Container;
use Grav\Component\EventDispatcher\Event;
@@ -50,8 +53,6 @@ class Grav extends Container
{
$container = new static($values);
$container['config_path'] = CACHE_DIR . 'config.php';
$container['grav'] = $container;
$container['events'] = function ($c) {
@@ -60,9 +61,6 @@ class Grav extends Container
$container['uri'] = function ($c) {
return new Uri($c);
};
$container['config'] = function ($c) {
return Config::instance($c);
};
$container['cache'] = function ($c) {
return new Cache($c);
};
@@ -106,6 +104,7 @@ class Grav extends Container
};
$container->register(new StreamsServiceProvider);
$container->register(new ConfigServiceProvider);
return $container;
}
@@ -115,8 +114,8 @@ class Grav extends Container
// Use output buffering to prevent headers from being sent too early.
ob_start();
// Initialize stream wrappers.
$this['locator'];
// Initialize configuration.
$this['config']->init();
$this['plugins']->init();

View File

@@ -2,11 +2,11 @@
namespace Grav\Common\Page;
use Grav\Common\Config;
use Grav\Common\Data\Blueprint;
use Grav\Common\Data\Data;
use Grav\Common\Filesystem\File\Yaml;
use Grav\Common\Grav;
use Grav\Common\GravTrait;
use Grav\Component\Data\Blueprint;
use Grav\Component\Data\Data;
use Grav\Component\Filesystem\File\Yaml;
use Gregwar\Image\Image as ImageFile;
/**

View File

@@ -6,12 +6,12 @@ use Grav\Common\GravTrait;
use Grav\Common\Utils;
use Grav\Common\Cache;
use Grav\Common\Twig;
use Grav\Common\Filesystem\File;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Data;
use Grav\Common\Uri;
use Grav\Common\Grav;
use Grav\Common\Taxonomy;
use Grav\Component\Data\Blueprint;
use Grav\Component\Filesystem\File;
use Grav\Component\Filesystem\Folder;
use Grav\Component\EventDispatcher\Event;
use Symfony\Component\Yaml\Yaml;
@@ -463,7 +463,7 @@ class Page
/**
* Get blueprints for the page.
*
* @return Data\Blueprint
* @return Blueprint
*/
public function blueprints()
{

View File

@@ -1,13 +1,14 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Grav;
use Grav\Common\Config;
use Grav\Common\Data;
use Grav\Common\Utils;
use Grav\Common\Cache;
use Grav\Common\Taxonomy;
use Grav\Component\Data\Blueprint;
use Grav\Component\Data\Blueprints;
use Grav\Component\Filesystem\Folder;
use Grav\Component\EventDispatcher\Event;
/**
@@ -44,7 +45,7 @@ class Pages
protected $sort;
/**
* @var Data\Blueprints
* @var Blueprints
*/
protected $blueprints;
@@ -252,12 +253,12 @@ class Pages
* Get a blueprint for a page type.
*
* @param string $type
* @return Data\Blueprint
* @return Blueprint
*/
public function blueprints($type)
{
if (!isset($this->blueprints)) {
$this->blueprints = new Data\Blueprints('theme://blueprints/');
$this->blueprints = new Blueprints('theme://blueprints/');
}
try {
@@ -311,7 +312,7 @@ class Pages
*/
static public function types()
{
$blueprints = new Data\Blueprints('theme://blueprints/');
$blueprints = new Blueprints('theme://blueprints/');
return $blueprints->types();
}

View File

@@ -1,6 +1,7 @@
<?php
namespace Grav\Common;
use Grav\Common\Config\Config;
use Grav\Component\EventDispatcher\EventDispatcher;
use Grav\Component\EventDispatcher\EventSubscriberInterface;

View File

@@ -1,7 +1,9 @@
<?php
namespace Grav\Common;
use Grav\Common\Filesystem\File;
use Grav\Component\Data\Blueprints;
use Grav\Component\Data\Data;
use Grav\Component\Filesystem\File;
use Grav\Component\EventDispatcher\EventDispatcher;
use Grav\Component\EventDispatcher\EventSubscriberInterface;
@@ -103,13 +105,13 @@ class Plugins extends Iterator
static public function get($name)
{
$blueprints = new Data\Blueprints("plugin://{$name}");
$blueprints = new Blueprints("plugin://{$name}");
$blueprint = $blueprints->get('blueprints');
$blueprint->name = $name;
// Load default configuration.
$file = File\Yaml::instance("plugin://{$name}/{$name}.yaml");
$obj = new Data\Data($file->content(), $blueprint);
$obj = new Data($file->content(), $blueprint);
// Override with user configuration.
$file = File\Yaml::instance("user://config/plugins/{$name}.yaml");

View File

@@ -0,0 +1,51 @@
<?php
namespace Grav\Common\Config;
use Grav\Common\Grav;
use Grav\Component\Blueprints\Blueprints;
use Grav\Component\Filesystem\Folder;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
* @license MIT
*/
class ConfigServiceProvider implements ServiceProviderInterface
{
public function register(Container $container)
{
$self = $this;
$container['blueprints'] = function ($c) use ($self) {
return $self->loadMasterBlueprints($c);
};
$container['config'] = function ($c) use ($self) {
return $self->loadMasterConfig($c);
};
}
public function loadMasterConfig(Container $container)
{
$file = CACHE_DIR . 'compiled/config/master.php';
$data = is_file($file) ? (array) include $file : [];
if (!$data) {
$file = GRAV_ROOT . '/config.php';
$data = is_file($file) ? (array) include $file : [];
}
return new Config($data, $container);
}
public function loadMasterBlueprints(Container $container)
{
$file = CACHE_DIR . 'compiled/blueprints/master.php';
$data = is_file($file) ? (array) include $file : [];
return new Blueprints($data, $container);
}
}

View File

@@ -1,7 +1,8 @@
<?php
namespace Grav\Common;
use Grav\Common\Filesystem\File\Yaml;
use Grav\Common\Config\Config;
use Grav\Component\Filesystem\File\Yaml;
use Grav\Component\Filesystem\ResourceLocator;
class Theme extends Plugin
@@ -23,9 +24,7 @@ class Theme extends Plugin
}
public function configure() {
$themeConfig = Yaml::instance(THEMES_DIR . "{$this->name}/{$this->name}.yaml")->content();
$this->config->merge(['themes' => [$this->name => $themeConfig]]);
$this->loadConfiguration();
/** @var ResourceLocator $locator */
$locator = $this->grav['locator'];
@@ -61,4 +60,11 @@ class Theme extends Plugin
}
}
protected function loadConfiguration()
{
$themeConfig = Yaml::instance(THEMES_DIR . "{$this->name}/{$this->name}.yaml")->content();
$this->config->merge(['themes' => [$this->name => $themeConfig]]);
}
}

View File

@@ -1,7 +1,9 @@
<?php
namespace Grav\Common;
use Grav\Common\Filesystem\File;
use Grav\Component\Data\Blueprints;
use Grav\Component\Data\Data;
use Grav\Component\Filesystem\File;
/**
* The Themes object holds an array of all the theme objects that Grav knows about.
@@ -24,7 +26,7 @@ class Themes
/**
* Return list of all theme data with their blueprints.
*
* @return array|Data\Data[]
* @return array|Data[]
*/
public function all()
{
@@ -50,7 +52,7 @@ class Themes
* Get theme or throw exception if it cannot be found.
*
* @param string $name
* @return Data\Data
* @return Data
* @throws \RuntimeException
*/
public function get($name)
@@ -59,7 +61,7 @@ class Themes
throw new \RuntimeException('Theme name not provided.');
}
$blueprints = new Data\Blueprints("theme://{$name}");
$blueprints = new Blueprints("theme://{$name}");
$blueprint = $blueprints->get('blueprints');
$blueprint->name = $name;
@@ -72,7 +74,7 @@ class Themes
// Load default configuration.
$file = File\Yaml::instance("theme://{$name}.yaml");
$obj = new Data\Data($file->content(), $blueprint);
$obj = new Data($file->content(), $blueprint);
// Override with user configuration.
$file = File\Yaml::instance("user://config/themes/{$name}.yaml");
@@ -86,28 +88,32 @@ class Themes
public function load($name = null)
{
$grav = $this->grav;
/** @var Config $config */
$config = $this->grav['config'];
$config = $grav['config'];
if (!$name) {
$name = $config->get('system.pages.theme');
}
$file = THEMES_DIR . "{$name}/{$name}.php";
$path = THEMES_DIR . $name;
$file = "{$path}/{$name}.php";
if (file_exists($file)) {
$class = require_once $file;
// Local variables available in the file: $grav, $config, $name, $path, $file
$class = include $file;
if (!is_object($class)) {
$className = '\\Grav\\Theme\\' . ucfirst($name);
if (class_exists($className)) {
$class = new $className($this->grav, $config, $name);
$class = new $className($grav, $config, $name);
}
}
}
if (empty($class)) {
$class = new Theme($this->grav, $config, $name);
$class = new Theme($grav, $config, $name);
}
return $class;

View File

@@ -1,7 +1,7 @@
<?php
namespace Grav\Common\User;
use Grav\Common\Data\Data;
use Grav\Component\Data\Data;
/**
* User object

View File

@@ -12,7 +12,7 @@ use Symfony\Component\Yaml\Yaml;
trait Export
{
/**
* Convert blueprints into an array.
* Convert object into an array.
*
* @return array
*/
@@ -22,22 +22,22 @@ trait Export
}
/**
* Convert blueprints into YAML string.
* Convert object into YAML string.
*
* @return string
*/
public function toYaml()
{
return Yaml::dump($this->items);
return Yaml::dump($this->toArray());
}
/**
* Convert blueprints into JSON string.
* Convert object into JSON string.
*
* @return string
*/
public function toJson()
{
return json_encode($this->items);
return json_encode($this->toArray());
}
}

View File

@@ -0,0 +1,296 @@
<?php
namespace Grav\Component\Blueprints;
/**
* Blueprints define a data structure.
*
* @author RocketTheme
* @license MIT
*/
class Blueprints
{
protected $items = array();
protected $rules = array();
protected $nested = array();
protected $filter = ['validation' => 1];
public function __construct(array $serialized = null) {
if ($serialized) {
$this->items = $serialized['items'];
$this->rules = $serialized['rules'];
$this->nested = $serialized['nested'];
$this->filter = $serialized['filter'];
}
}
/**
* 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 != '.' ? strtr($name, $separator, '.') : $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 != '.' ? strtr($name, $separator, '.') : $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);
}
/**
* Convert object into an array.
*
* @return array
*/
public function toArray()
{
return ['items' => $this->items, 'rules' => $this->rules, 'nested' => $this->nested, 'filter' => $this->filter];
}
/**
* Embed an array to the blueprint.
*
* @param $name
* @param array $value
* @param string $separator
*/
public function embed($name, array $value, $separator = '.')
{
if (isset($value['rules'])) {
$this->rules = array_merge($this->rules, $value['rules']);
}
if (!isset($value['form']['fields']) || !is_array($value['form']['fields'])) {
return;
}
$prefix = $name ? ($separator != '.' ? strtr($name, $separator, '.') : $name) . '.' : '';
$params = array_intersect_key($this->filter, $value);
$this->parseFormFields($value['form']['fields'], $params, $prefix);
}
/**
* 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->getProperty($name, $separator);
return $this->mergeArrays($data1, $data2, $nested);
}
/**
* @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 (!$rule && array_key_exists($key, $data1) && is_array($field) && is_array($val)) {
// Array has been defined in blueprints.
$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
* @param array $params
* @param string $prefix
* @internal
*/
protected function parseFormFields(array &$fields, $params, $prefix)
{
// Go though all the fields in current level.
foreach ($fields as $key => &$field) {
// Set name from the array key.
$field['name'] = $prefix . $key;
$field += $params;
if (isset($field['fields'])) {
// Recursively get all the nested fields.
$newParams = array_intersect_key($this->filter, $field);
$this->parseFormFields($field['fields'], $newParams, $prefix);
} else {
// Add rule.
$this->items[$prefix . $key] = &$field;
$this->addProperty($prefix . $key);
foreach ($field as $name => $value) {
// TODO: Support nested blueprints.
/*
if (isset($this->context) && $name == '@import') {
$values = (array) $value;
if (!isset($field['fields'])) {
$field['fields'] = array();
}
foreach ($values as $bname) {
$b = $this->context->get($bname);
$field['fields'] = array_merge($field['fields'], $b->fields());
}
}
// Support for callable data values.
else
*/
if (substr($name, 0, 6) == '@data-') {
$property = substr($name, 6);
if (is_array($value)) {
$func = array_shift($value);
} else {
$func = $value;
$value = array();
}
list($o, $f) = preg_split('/::/', $func);
if (!$f && function_exists($o)) {
$data = call_user_func_array($o, $value);
} elseif ($f && method_exists($o, $f)) {
$data = call_user_func_array(array($o, $f), $value);
}
// If function returns a value,
if (isset($data)) {
if (isset($field[$property]) && is_array($field[$property]) && is_array($data)) {
// Combine field and @data-field together.
$field[$property] += $data;
} else {
// Or create/replace field with @data-field.
$field[$property] = $data;
}
}
}
}
// Initialize predefined validation rule.
if (isset($field['validate']['rule'])) {
$field['validate'] += $this->getRule($field['validate']['rule']);
}
}
}
}
/**
* Get property from the definition.
*
* @param string $path Comma separated path to the property.
* @param string $separator
* @return array
* @internal
*/
protected function getProperty($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])) {
return [];
}
$nested = $nested[$part];
}
return isset($nested[$item]) ? $nested[$item] : [];
}
/**
* 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])) {
$nested[$part] = array();
}
$nested = &$nested[$part];
}
if (!isset($nested[$item])) {
$nested[$item] = $path;
}
}
/**
* @param $rule
* @return array
* @internal
*/
protected function getRule($rule)
{
if (isset($this->rules[$rule]) && is_array($this->rules[$rule])) {
return $this->rules[$rule];
}
return array();
}
}

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Data;
namespace Grav\Component\Data;
use Grav\Component\ArrayTraits\Export;
@@ -14,25 +14,35 @@ class Blueprint
use Export;
public $name;
public $initialized = false;
protected $items;
protected $context;
protected $fields;
protected $rules = array();
protected $nested = array();
protected $filter = ['validation' => 1];
/**
* @param string $name
* @param array $data
* @param Blueprints $context
*/
public function __construct($name, array $data, Blueprints $context)
public function __construct($name, array $data = array(), Blueprints $context = null)
{
$this->name = $name;
$this->items = $data;
$this->context = $context;
}
/**
* 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.
*
@@ -103,8 +113,7 @@ class Blueprint
public function fields()
{
if (!isset($this->fields)) {
$this->fields = isset($this->items['form']['fields']) ? $this->items['form']['fields'] : array();
$this->getFields($this->fields);
$this->embed('', $this->items);
}
return $this->fields;
@@ -135,7 +144,7 @@ class Blueprint
* @param array $data2
* @return array
*/
public function mergeData(array $data1, array $data2)
public function mergeData(array $data1, array $data2, $name = null)
{
// Initialize data
$this->fields();
@@ -204,6 +213,35 @@ class Blueprint
$this->items = $blueprints;
}
/**
* Convert object into an array.
*
* @return array
*/
public function getState()
{
return ['name' => $this->name, 'items' => $this->items, 'rules' => $this->rules, 'nested' => $this->nested];
}
/**
* Embed an array to the blueprint.
*
* @param $name
* @param array $value
* @param string $separator
*/
public function embed($name, array $value, $separator = '.')
{
if (!isset($value['form']['fields']) || !is_array($value['form']['fields'])) {
return;
}
// Initialize data
$this->fields();
$prefix = $name ? strtr($name, $separator, '.') . '.' : '';
$params = array_intersect_key($this->filter, $value);
$this->parseFormFields($value['form']['fields'], $params, $prefix);
}
/**
* @param array $data
@@ -319,26 +357,30 @@ class Blueprint
* Gets all field definitions from the blueprints.
*
* @param array $fields
* @param array $params
* @param string $prefix
* @internal
*/
protected function getFields(array &$fields)
protected function parseFormFields(array &$fields, $params, $prefix)
{
// Go though all the fields in current level.
foreach ($fields as $key => &$field) {
// Set name from the array key.
$field['name'] = $key;
$field['name'] = $prefix . $key;
$field += $params;
if (isset($field['fields'])) {
// Recursively get all the nested fields.
$this->getFields($field['fields']);
$newParams = array_intersect_key($this->filter, $field);
$this->parseFormFields($field['fields'], $newParams, $prefix);
} else {
// Add rule.
$this->rules[$key] = &$field;
$this->addProperty($key);
$this->rules[$prefix . $key] = &$field;
$this->addProperty($prefix . $key);
foreach ($field as $name => $value) {
// Support nested blueprints.
if ($name == '@import') {
if ($this->context && $name == '@import') {
$values = (array) $value;
if (!isset($field['fields'])) {
$field['fields'] = array();

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Data;
namespace Grav\Component\Data;
use \Symfony\Component\Yaml\Yaml;

View File

@@ -1,9 +1,11 @@
<?php
namespace Grav\Common\Data;
namespace Grav\Component\Data;
use Grav\Common\Filesystem\FileInterface;
use \Grav\Common\Getters;
use \Grav\Common\Filesystem\File;
use Grav\Component\ArrayTraits\ArrayAccessWithGetters;
use Grav\Component\ArrayTraits\Countable;
use Grav\Component\ArrayTraits\Export;
use Grav\Component\Filesystem\FileInterface;
use Grav\Component\Filesystem\File;
/**
* Recursive data object
@@ -11,8 +13,10 @@ use \Grav\Common\Filesystem\File;
* @author RocketTheme
* @license MIT
*/
class Data extends Getters implements DataInterface
class Data implements DataInterface
{
use ArrayAccessWithGetters, Countable, Export;
protected $gettersVariable = 'items';
protected $items;
@@ -121,13 +125,37 @@ class Data extends Getters implements DataInterface
* @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 def($name, $default = null, $separator = '.')
{
$this->set($name, $this->get($name, $default, $separator), $separator);
}
/**
* Join two values together by using blueprints if available.
*
* @example $data->def('this.is.my.nested.variable', 'default');
*
* @param string $name Dot separated path to the requested value.
* @param mixed $value Value to be joined.
* @param string $separator Separator, defaults to '.'
*/
public function join($name, $value, $separator = '.')
{
$old = $this->get($name, null, $separator);
if ($old === null) {
// Variable does not exist yet: just use the incoming value.
} elseif ($this->blueprints) {
// Blueprints: join values by using blueprints.
$value = $this->blueprints->mergeData($old, $value, $name, $separator);
} else {
// No blueprints: replace existing top level variables with the new ones.
$value =array_merge($old, $value);
}
$this->set($name, $value, $separator);
}
/**
* Merge two sets of data together.
*

View File

@@ -1,8 +1,8 @@
<?php
namespace Grav\Common\Data;
namespace Grav\Component\Data;
use Grav\Common\Filesystem\FileInterface;
use \Grav\Common\Filesystem\File;
use Grav\Component\Filesystem\FileInterface;
use Grav\Component\Filesystem\File;
/**
* Data interface

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Data;
namespace Grav\Component\Data;
/**
* Data validation.

View File

@@ -1,7 +1,7 @@
<?php
namespace Grav\Common\Filesystem\File;
namespace Grav\Component\Filesystem\File;
use Grav\Common\Filesystem\FileInterface;
use Grav\Component\Filesystem\FileInterface;
/**
* General file handling class.

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Filesystem\File;
namespace Grav\Component\Filesystem\File;
/**
* File handling class for JSON.

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Filesystem\File;
namespace Grav\Component\Filesystem\File;
/**
* File handling class for Log files.

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Filesystem\File;
namespace Grav\Component\Filesystem\File;
use \Symfony\Component\Yaml\Yaml as YamlParser;

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Filesystem\File;
namespace Grav\Component\Filesystem\File;
/**
* File handling class.
@@ -7,7 +7,7 @@ namespace Grav\Common\Filesystem\File;
* @author RocketTheme
* @license MIT
*/
class Config extends General
class Php extends General
{
/**
* @var string
@@ -20,7 +20,7 @@ class Config extends General
static protected $instances = array();
/**
* Saves configuration file and invalidates opcache.
* Saves PHP file and invalidates opcache.
*
* @param mixed $data Optional data to be saved, usually array.
* @throws \RuntimeException
@@ -42,14 +42,14 @@ class Config extends General
/**
* Check contents and make sure it is in correct format.
*
* @param \Grav\Common\Config $var
* @return \Grav\Common\Config
* @param array $var
* @return array
* @throws \RuntimeException
*/
protected function check($var)
{
if (!($var instanceof \Grav\Common\Config)) {
throw new \RuntimeException('Provided data is not configuration');
if (!(is_array($var) || is_object($var))) {
throw new \RuntimeException('Provided data is not an array');
}
return $var;
@@ -58,34 +58,14 @@ class Config extends General
/**
* Encode configuration object into RAW string (PHP class).
*
* @param \Grav\Common\Config $var
* @param array $var
* @return string
* @throws \RuntimeException
*/
protected function encode($var)
{
if (!($var instanceof \Grav\Common\Config)) {
throw new \RuntimeException('Provided data is not configuration');
}
// Build the object variables string
$vars = array();
$options = $var->toArray();
foreach ($options as $k => $v) {
if (is_int($v)) {
$vars[] = "\tpublic $" . $k . " = " . $v . ";";
} elseif (is_bool($v)) {
$vars[] = "\tpublic $" . $k . " = " . ($v ? 'true' : 'false') . ";";
} elseif (is_scalar($v)) {
$vars[] = "\tpublic $" . $k . " = '" . addcslashes($v, '\\\'') . "';";
} elseif (is_array($v) || is_object($v)) {
$vars[] = "\tpublic $" . $k . " = " . $this->encodeArray((array) $v) . ";";
}
}
$vars = implode("\n", $vars);
return "<?php\nnamespace Grav;\n\nclass Config extends \\Grav\\Common\\Config {\n {$vars}\n}";
return "<?php\nreturn {$this->encodeArray((array) $var)};\n";
}
/**
@@ -96,12 +76,12 @@ class Config extends General
*
* @return array
*/
protected function encodeArray($a, $level = 1)
protected function encodeArray(array $a, $level = 0)
{
$r = array();
$r = [];
foreach ($a as $k => $v) {
if (is_array($v) || is_object($v)) {
$r[] = '"' . $k . '" => ' . $this->encodeArray((array) $v, $level+1);
$r[] = "'" . $k . "' => " . $this->encodeArray((array) $v, $level + 1);
} elseif (is_int($v)) {
$r[] = "'" . $k . "' => " . $v;
} elseif (is_bool($v)) {
@@ -111,19 +91,19 @@ class Config extends General
}
}
$tabs = str_repeat("\t", $level);
return "array(\n\t{$tabs}" . implode(",\n\t{$tabs}", $r) . "\n{$tabs})";
$space = str_repeat(" ", $level);
return "[\n {$space}" . implode(",\n {$space}", $r) . "\n{$space}]";
}
/**
* Decode RAW string into contents.
* Decode PHP file into contents.
*
* @param string $var
* @return \Grav\Common\Config
* @return array
*/
protected function decode($var)
{
// TODO: improve this one later, works only for single file...
return class_exists('\Grav\Config') ? new \Grav\Config($this->filename) : new Config($this->filename);
$var = (array) include $this->filename;
return $var;
}
}

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Filesystem\File;
namespace Grav\Component\Filesystem\File;
use \Symfony\Component\Yaml\Yaml as YamlParser;
@@ -23,7 +23,7 @@ class Yaml extends General
{
parent::__construct();
$this->extension = YAML_EXT;
$this->extension = '.yaml';
}
/**

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Filesystem;
namespace Grav\Component\Filesystem;
/**
* File interface.

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Filesystem;
namespace Grav\Component\Filesystem;
/**
* Folder helper class.
@@ -86,12 +86,12 @@ abstract class Folder
$filePath = call_user_func($filter, $file);
} else {
$filePath = preg_replace($filter, '', $filePath);
}
}
}
}
if ($fileKey !== null) {
$results[$fileKey] = $filePath;
$results[$fileKey] = $filePath;
} else {
$results[] = $filePath;
}

View File

@@ -14,6 +14,8 @@ class ResourceLocator
*/
protected $schemes = [];
protected $cache = [];
/**
* @param string $scheme
* @param string $prefix
@@ -34,6 +36,8 @@ class ResourceLocator
// Sort in reverse order to get longer prefixes to be matched first.
krsort($this->schemes[$scheme]);
$this->cache = [];
}
/**
@@ -66,14 +70,14 @@ class ResourceLocator
}
/**
* @param string $uri
* @param bool $absolute
* @param bool $array
* Parse resource.
*
* @param $uri
* @return array
* @throws \InvalidArgumentException
* @return array|string|bool
* @internal
*/
protected function find($uri, $array, $absolute)
protected function parseResource($uri)
{
$segments = explode('://', $uri, 2);
$file = array_pop($segments);
@@ -90,6 +94,28 @@ class ResourceLocator
throw new \InvalidArgumentException("Invalid resource {$scheme}://");
}
return [$file, $scheme];
}
/**
* @param string $uri
* @param bool $absolute
* @param bool $array
*
* @throws \InvalidArgumentException
* @return array|string|bool
* @internal
*/
protected function find($uri, $array, $absolute)
{
// Local caching: make sure that the function gets only called at once for each file.
$key = $uri .'@'. (int) $array . (int) $absolute;
if (isset($this->cache[$key])) {
return $this->cache[$key];
}
list ($file, $scheme) = $this->parseResource($uri);
$results = $array ? [] : false;
foreach ($this->schemes[$scheme] as $prefix => $paths) {
if ($prefix && strpos($file, $prefix) !== 0) {
@@ -98,17 +124,19 @@ class ResourceLocator
foreach ($paths as $path) {
$filename = $path . '/' . ltrim(substr($file, strlen($prefix)), '\/');
$lookup = ROOT_DIR . '/' . $filename;
$lookup = GRAV_ROOT . '/' . $filename;
if (file_exists($lookup)) {
if (!$array) {
return $absolute ? $lookup : $filename;
$results = $absolute ? $lookup : $filename;
break;
}
$results[] = $absolute ? $lookup : $filename;
}
}
}
$this->cache[$key] = $results;
return $results;
}
}

View File

@@ -1,7 +1,5 @@
<?php
namespace Grav\Common\Session;
use Grav\Common\Getters;
namespace Grav\Component\Session;
/**
* Session wide messages.

View File

@@ -1,5 +1,5 @@
<?php
namespace Grav\Common\Session;
namespace Grav\Component\Session;
/**
* Session handling.
@@ -22,9 +22,11 @@ class Session implements \IteratorAggregate
/**
* @param int $lifetime Defaults to 1800 seconds.
* @param string $path Cookie path.
* @throws \RuntimeException
*/
public function __construct($lifetime, $path)
{
// Session is a singleton.
if (isset(self::$instance)) {
throw new \RuntimeException("Session has already been initialized.", 500);
}
@@ -73,7 +75,7 @@ class Session implements \IteratorAggregate
public function start()
{
if (!session_start()) {
throw new \RuntimeException('Failed to start session');
throw new \RuntimeException('Failed to start session.', 500);
}
$this->started = true;

2
vendor/autoload.php vendored
View File

@@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderIniteef751e17db86fa31c0c46c61acf76f4::getLoader();
return ComposerAutoloaderInit783fd05b00682a5818a5d9d5601c81fe::getLoader();

View File

@@ -28,21 +28,13 @@ return array(
'FirePHP' => $vendorDir . '/mrclay/minify/min/lib/FirePHP.php',
'Grav\\Common\\Assets' => $baseDir . '/system/src/Grav/Common/Assets.php',
'Grav\\Common\\Cache' => $baseDir . '/system/src/Grav/Common/Cache.php',
'Grav\\Common\\Config' => $baseDir . '/system/src/Grav/Common/Config.php',
'Grav\\Common\\Data\\Blueprint' => $baseDir . '/system/src/Grav/Common/Data/Blueprint.php',
'Grav\\Common\\Data\\Blueprints' => $baseDir . '/system/src/Grav/Common/Data/Blueprints.php',
'Grav\\Common\\Data\\Data' => $baseDir . '/system/src/Grav/Common/Data/Data.php',
'Grav\\Common\\Data\\DataInterface' => $baseDir . '/system/src/Grav/Common/Data/DataInterface.php',
'Grav\\Common\\Data\\Validation' => $baseDir . '/system/src/Grav/Common/Data/Validation.php',
'Grav\\Common\\Config' => $baseDir . '/system/src/Grav/Common/ConfigBak.php',
'Grav\\Common\\Config\\Blueprints' => $baseDir . '/system/src/Grav/Common/Config/Blueprints.php',
'Grav\\Common\\Config\\Config' => $baseDir . '/system/src/Grav/Common/Config/Config.php',
'Grav\\Common\\Config\\ConfigServiceProvider' => $baseDir . '/system/src/Grav/Common/Service/ConfigServiceProvider.php',
'Grav\\Common\\Debugger' => $baseDir . '/system/src/Grav/Common/Debugger.php',
'Grav\\Common\\Filesystem\\FileInterface' => $baseDir . '/system/src/Grav/Common/Filesystem/FileInterface.php',
'Grav\\Common\\Filesystem\\File\\Config' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Config.php',
'Grav\\Common\\Filesystem\\File\\General' => $baseDir . '/system/src/Grav/Common/Filesystem/File/General.php',
'Grav\\Common\\Filesystem\\File\\Json' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Json.php',
'Grav\\Common\\Filesystem\\File\\Log' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Log.php',
'Grav\\Common\\Filesystem\\File\\Markdown' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Markdown.php',
'Grav\\Common\\Filesystem\\File\\Yaml' => $baseDir . '/system/src/Grav/Common/Filesystem/File/Yaml.php',
'Grav\\Common\\Filesystem\\Folder' => $baseDir . '/system/src/Grav/Common/Filesystem/Folder.php',
'Grav\\Common\\File\\CompiledFile' => $baseDir . '/system/src/Grav/Common/File/CompiledFile.php',
'Grav\\Common\\File\\CompiledYaml' => $baseDir . '/system/src/Grav/Common/File/CompiledYaml.php',
'Grav\\Common\\Getters' => $baseDir . '/system/src/Grav/Common/Getters.php',
'Grav\\Common\\Grav' => $baseDir . '/system/src/Grav/Common/Grav.php',
'Grav\\Common\\GravTrait' => $baseDir . '/system/src/Grav/Common/GravTrait.php',
@@ -57,8 +49,6 @@ return array(
'Grav\\Common\\Plugins' => $baseDir . '/system/src/Grav/Common/Plugins.php',
'Grav\\Common\\Registry' => $baseDir . '/system/src/Grav/Common/Registry.php',
'Grav\\Common\\Service\\StreamsServiceProvider' => $baseDir . '/system/src/Grav/Common/Service/StreamsServiceProvider.php',
'Grav\\Common\\Session\\Message' => $baseDir . '/system/src/Grav/Common/Session/Message.php',
'Grav\\Common\\Session\\Session' => $baseDir . '/system/src/Grav/Common/Session/Session.php',
'Grav\\Common\\Taxonomy' => $baseDir . '/system/src/Grav/Common/Taxonomy.php',
'Grav\\Common\\Theme' => $baseDir . '/system/src/Grav/Common/Theme.php',
'Grav\\Common\\Themes' => $baseDir . '/system/src/Grav/Common/Themes.php',
@@ -69,18 +59,38 @@ return array(
'Grav\\Common\\User\\User' => $baseDir . '/system/src/Grav/Common/User/User.php',
'Grav\\Common\\Utils' => $baseDir . '/system/src/Grav/Common/Utils.php',
'Grav\\Component\\ArrayTraits\\ArrayAccess' => $baseDir . '/system/src/Grav/Component/ArrayTraits/ArrayAccess.php',
'Grav\\Component\\ArrayTraits\\ArrayAccessWithGetters' => $baseDir . '/system/src/Grav/Component/ArrayTraits/ArrayAccessWithGetters.php',
'Grav\\Component\\ArrayTraits\\Constructor' => $baseDir . '/system/src/Grav/Component/ArrayTraits/Constructor.php',
'Grav\\Component\\ArrayTraits\\Countable' => $baseDir . '/system/src/Grav/Component/ArrayTraits/Countable.php',
'Grav\\Component\\ArrayTraits\\Getters' => $baseDir . '/system/src/Grav/Component/ArrayTraits/Getters.php',
'Grav\\Component\\ArrayTraits\\Export' => $baseDir . '/system/src/Grav/Component/ArrayTraits/Export.php',
'Grav\\Component\\ArrayTraits\\Iterator' => $baseDir . '/system/src/Grav/Component/ArrayTraits/Iterator.php',
'Grav\\Component\\ArrayTraits\\Serializable' => $baseDir . '/system/src/Grav/Component/ArrayTraits/Serializable.php',
'Grav\\Component\\Blueprints\\Blueprints' => $baseDir . '/system/src/Grav/Component/Blueprints/Blueprints.php',
'Grav\\Component\\Config\\ConfigLoader' => $baseDir . '/system/src/Grav/Component/Config/ConfigLoader.php',
'Grav\\Component\\DI\\Container' => $baseDir . '/system/src/Grav/Component/DI/Container.php',
'Grav\\Component\\DI\\ServiceProviderInterface' => $baseDir . '/system/src/Grav/Component/DI/ServiceProviderInterface.php',
'Grav\\Component\\Data\\Blueprint' => $baseDir . '/system/src/Grav/Component/Data/Blueprint.php',
'Grav\\Component\\Data\\Blueprints' => $baseDir . '/system/src/Grav/Component/Data/Blueprints.php',
'Grav\\Component\\Data\\Data' => $baseDir . '/system/src/Grav/Component/Data/Data.php',
'Grav\\Component\\Data\\DataInterface' => $baseDir . '/system/src/Grav/Component/Data/DataInterface.php',
'Grav\\Component\\Data\\Validation' => $baseDir . '/system/src/Grav/Component/Data/Validation.php',
'Grav\\Component\\EventDispatcher\\Event' => $baseDir . '/system/src/Grav/Component/EventDispatcher/Event.php',
'Grav\\Component\\EventDispatcher\\EventDispatcher' => $baseDir . '/system/src/Grav/Component/EventDispatcher/EventDispatcher.php',
'Grav\\Component\\EventDispatcher\\EventSubscriberInterface' => $baseDir . '/system/src/Grav/Component/EventDispatcher/EventSubscriberInterface.php',
'Grav\\Component\\Filesystem\\FileInterface' => $baseDir . '/system/src/Grav/Component/Filesystem/FileInterface.php',
'Grav\\Component\\Filesystem\\File\\General' => $baseDir . '/system/src/Grav/Component/Filesystem/File/General.php',
'Grav\\Component\\Filesystem\\File\\Json' => $baseDir . '/system/src/Grav/Component/Filesystem/File/Json.php',
'Grav\\Component\\Filesystem\\File\\Log' => $baseDir . '/system/src/Grav/Component/Filesystem/File/Log.php',
'Grav\\Component\\Filesystem\\File\\Markdown' => $baseDir . '/system/src/Grav/Component/Filesystem/File/Markdown.php',
'Grav\\Component\\Filesystem\\File\\Php' => $baseDir . '/system/src/Grav/Component/Filesystem/File/Php.php',
'Grav\\Component\\Filesystem\\File\\Yaml' => $baseDir . '/system/src/Grav/Component/Filesystem/File/Yaml.php',
'Grav\\Component\\Filesystem\\Folder' => $baseDir . '/system/src/Grav/Component/Filesystem/Folder.php',
'Grav\\Component\\Filesystem\\ResourceLocator' => $baseDir . '/system/src/Grav/Component/Filesystem/ResourceLocator.php',
'Grav\\Component\\Filesystem\\StreamWrapper\\ReadOnlyStream' => $baseDir . '/system/src/Grav/Component/Filesystem/StreamWrapper/ReadOnlyStream.php',
'Grav\\Component\\Filesystem\\StreamWrapper\\Stream' => $baseDir . '/system/src/Grav/Component/Filesystem/StreamWrapper/Stream.php',
'Grav\\Component\\Filesystem\\StreamWrapper\\StreamInterface' => $baseDir . '/system/src/Grav/Component/Filesystem/StreamWrapper/StreamInterface.php',
'Grav\\Component\\Session\\Message' => $baseDir . '/system/src/Grav/Component/Session/Message.php',
'Grav\\Component\\Session\\Session' => $baseDir . '/system/src/Grav/Component/Session/Session.php',
'Grav\\Console\\BackupCommand' => $baseDir . '/system/src/Grav/Console/BackupCommand.php',
'Grav\\Console\\CleanCommand' => $baseDir . '/system/src/Grav/Console/CleanCommand.php',
'Grav\\Console\\ClearCacheCommand' => $baseDir . '/system/src/Grav/Console/ClearCacheCommand.php',
@@ -157,6 +167,31 @@ return array(
'Pimple\\Tests\\Fixtures\\NonInvokable' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php',
'Pimple\\Tests\\Fixtures\\PimpleServiceProvider' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php',
'Pimple\\Tests\\Fixtures\\Service' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php',
'RocketTheme\\Toolbox\\ArrayTraits\\ArrayAccess' => $vendorDir . '/rockettheme/toolbox/ArrayTraits/src/ArrayAccess.php',
'RocketTheme\\Toolbox\\ArrayTraits\\ArrayAccessWithGetters' => $vendorDir . '/rockettheme/toolbox/ArrayTraits/src/ArrayAccessWithGetters.php',
'RocketTheme\\Toolbox\\ArrayTraits\\Constructor' => $vendorDir . '/rockettheme/toolbox/ArrayTraits/src/Constructor.php',
'RocketTheme\\Toolbox\\ArrayTraits\\Countable' => $vendorDir . '/rockettheme/toolbox/ArrayTraits/src/Countable.php',
'RocketTheme\\Toolbox\\ArrayTraits\\Export' => $vendorDir . '/rockettheme/toolbox/ArrayTraits/src/Export.php',
'RocketTheme\\Toolbox\\ArrayTraits\\ExportInterface' => $vendorDir . '/rockettheme/toolbox/ArrayTraits/src/ExportInterface.php',
'RocketTheme\\Toolbox\\ArrayTraits\\Iterator' => $vendorDir . '/rockettheme/toolbox/ArrayTraits/src/Iterator.php',
'RocketTheme\\Toolbox\\ArrayTraits\\Serializable' => $vendorDir . '/rockettheme/toolbox/ArrayTraits/src/Serializable.php',
'RocketTheme\\Toolbox\\DI\\Container' => $vendorDir . '/rockettheme/toolbox/DI/src/Container.php',
'RocketTheme\\Toolbox\\DI\\ServiceProviderInterface' => $vendorDir . '/rockettheme/toolbox/DI/src/ServiceProviderInterface.php',
'RocketTheme\\Toolbox\\Event\\Event' => $vendorDir . '/rockettheme/toolbox/Event/src/Event.php',
'RocketTheme\\Toolbox\\Event\\EventDispatcher' => $vendorDir . '/rockettheme/toolbox/Event/src/EventDispatcher.php',
'RocketTheme\\Toolbox\\Event\\EventSubscriberInterface' => $vendorDir . '/rockettheme/toolbox/Event/src/EventSubscriberInterface.php',
'RocketTheme\\Toolbox\\File\\File' => $vendorDir . '/rockettheme/toolbox/File/src/File.php',
'RocketTheme\\Toolbox\\File\\FileInterface' => $vendorDir . '/rockettheme/toolbox/File/src/FileInterface.php',
'RocketTheme\\Toolbox\\File\\JsonFile' => $vendorDir . '/rockettheme/toolbox/File/src/JsonFile.php',
'RocketTheme\\Toolbox\\File\\LogFile' => $vendorDir . '/rockettheme/toolbox/File/src/LogFile.php',
'RocketTheme\\Toolbox\\File\\MarkdownFile' => $vendorDir . '/rockettheme/toolbox/File/src/MarkdownFile.php',
'RocketTheme\\Toolbox\\File\\PhpFile' => $vendorDir . '/rockettheme/toolbox/File/src/PhpFile.php',
'RocketTheme\\Toolbox\\File\\YamlFile' => $vendorDir . '/rockettheme/toolbox/File/src/YamlFile.php',
'RocketTheme\\Toolbox\\Session\\Message' => $vendorDir . '/rockettheme/toolbox/Session/src/Message.php',
'RocketTheme\\Toolbox\\Session\\Session' => $vendorDir . '/rockettheme/toolbox/Session/src/Session.php',
'RocketTheme\\Toolbox\\StreamWrapper\\ReadOnlyStream' => $vendorDir . '/rockettheme/toolbox/StreamWrapper/src/ReadOnlyStream.php',
'RocketTheme\\Toolbox\\StreamWrapper\\Stream' => $vendorDir . '/rockettheme/toolbox/StreamWrapper/src/Stream.php',
'RocketTheme\\Toolbox\\StreamWrapper\\StreamInterface' => $vendorDir . '/rockettheme/toolbox/StreamWrapper/src/StreamInterface.php',
'Symfony\\Component\\Console\\Application' => $vendorDir . '/symfony/console/Symfony/Component/Console/Application.php',
'Symfony\\Component\\Console\\Command\\Command' => $vendorDir . '/symfony/console/Symfony/Component/Console/Command/Command.php',
'Symfony\\Component\\Console\\Command\\HelpCommand' => $vendorDir . '/symfony/console/Symfony/Component/Console/Command/HelpCommand.php',

View File

@@ -6,9 +6,9 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
$vendorDir . '/ircmaxell/password-compat/lib/password.php',
$vendorDir . '/ornicar/php-user-agent/lib/phpUserAgent.php',
$vendorDir . '/ornicar/php-user-agent/lib/phpUserAgentStringParser.php',
$vendorDir . '/tracy/tracy/src/shortcuts.php',
$vendorDir . '/ircmaxell/password-compat/lib/password.php',
$baseDir . '/system/defines.php',
);

View File

@@ -15,5 +15,6 @@ return array(
'Parsedown' => array($vendorDir . '/erusev/parsedown'),
'Gregwar\\Image' => array($vendorDir . '/gregwar/image'),
'Gregwar\\Cache' => array($vendorDir . '/gregwar/cache'),
'Grav\\' => array($baseDir . '/system/src'),
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib'),
);

View File

@@ -6,5 +6,11 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Grav\\' => array($baseDir . '/system/src'),
'RocketTheme\\Toolbox\\StreamWrapper\\' => array($vendorDir . '/rockettheme/toolbox/StreamWrapper/src'),
'RocketTheme\\Toolbox\\Session\\' => array($vendorDir . '/rockettheme/toolbox/Session/src'),
'RocketTheme\\Toolbox\\ResourceLocation\\' => array($vendorDir . '/rockettheme/toolbox/ResourceLocation/src'),
'RocketTheme\\Toolbox\\File\\' => array($vendorDir . '/rockettheme/toolbox/File/src'),
'RocketTheme\\Toolbox\\Event\\' => array($vendorDir . '/rockettheme/toolbox/Event/src'),
'RocketTheme\\Toolbox\\DI\\' => array($vendorDir . '/rockettheme/toolbox/DI/src'),
'RocketTheme\\Toolbox\\ArrayTraits\\' => array($vendorDir . '/rockettheme/toolbox/ArrayTraits/src'),
);

View File

@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderIniteef751e17db86fa31c0c46c61acf76f4
class ComposerAutoloaderInit783fd05b00682a5818a5d9d5601c81fe
{
private static $loader;
@@ -19,9 +19,9 @@ class ComposerAutoloaderIniteef751e17db86fa31c0c46c61acf76f4
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderIniteef751e17db86fa31c0c46c61acf76f4', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit783fd05b00682a5818a5d9d5601c81fe', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderIniteef751e17db86fa31c0c46c61acf76f4', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit783fd05b00682a5818a5d9d5601c81fe', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
@@ -42,14 +42,14 @@ class ComposerAutoloaderIniteef751e17db86fa31c0c46c61acf76f4
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $file) {
composerRequireeef751e17db86fa31c0c46c61acf76f4($file);
composerRequire783fd05b00682a5818a5d9d5601c81fe($file);
}
return $loader;
}
}
function composerRequireeef751e17db86fa31c0c46c61acf76f4($file)
function composerRequire783fd05b00682a5818a5d9d5601c81fe($file)
{
require $file;
}

View File

@@ -99,52 +99,6 @@
"parser"
]
},
{
"name": "erusev/parsedown-extra",
"version": "0.2.0",
"version_normalized": "0.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown-extra.git",
"reference": "7578fe28ce42e7a1fff4ba2aada3807c4c03d04b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/7578fe28ce42e7a1fff4ba2aada3807c4c03d04b",
"reference": "7578fe28ce42e7a1fff4ba2aada3807c4c03d04b",
"shasum": ""
},
"require": {
"erusev/parsedown": "~1.0"
},
"time": "2014-08-16 11:20:35",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"ParsedownExtra": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "An extension of Parsedown that adds support for Markdown Extra.",
"homepage": "https://github.com/erusev/parsedown-extra",
"keywords": [
"markdown",
"markdown extra",
"parsedown",
"parser"
]
},
{
"name": "doctrine/cache",
"version": "v1.3.0",
@@ -518,62 +472,6 @@
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com"
},
{
"name": "tracy/tracy",
"version": "v2.2.2",
"version_normalized": "2.2.2.0",
"source": {
"type": "git",
"url": "https://github.com/nette/tracy.git",
"reference": "f5a2647c9d0174d218d626eab3952ea3a523c6e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/tracy/zipball/f5a2647c9d0174d218d626eab3952ea3a523c6e7",
"reference": "f5a2647c9d0174d218d626eab3952ea3a523c6e7",
"shasum": ""
},
"require": {
"php": ">=5.3.1"
},
"require-dev": {
"nette/tester": "~1.0"
},
"time": "2014-06-24 01:18:03",
"type": "library",
"installation-source": "dist",
"autoload": {
"classmap": [
"src/Tracy"
],
"files": [
"src/shortcuts.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
"GPL-2.0",
"GPL-3.0"
],
"authors": [
{
"name": "David Grudl",
"homepage": "http://davidgrudl.com"
},
{
"name": "Nette Community",
"homepage": "http://nette.org/contributors"
}
],
"description": "Tracy: useful PHP debugger",
"homepage": "http://tracy.nette.org",
"keywords": [
"debug",
"debugger",
"nette"
]
},
{
"name": "gregwar/cache",
"version": "v1.0.7",
@@ -704,5 +602,160 @@
"hashing",
"password"
]
},
{
"name": "erusev/parsedown-extra",
"version": "0.2.1",
"version_normalized": "0.2.1.0",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown-extra.git",
"reference": "424e63fef5299f2a5a0464cd22a666b7a7b48657"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown-extra/zipball/424e63fef5299f2a5a0464cd22a666b7a7b48657",
"reference": "424e63fef5299f2a5a0464cd22a666b7a7b48657",
"shasum": ""
},
"require": {
"erusev/parsedown": "~1.0"
},
"time": "2014-08-25 10:49:57",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"ParsedownExtra": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "An extension of Parsedown that adds support for Markdown Extra.",
"homepage": "https://github.com/erusev/parsedown-extra",
"keywords": [
"markdown",
"markdown extra",
"parsedown",
"parser"
]
},
{
"name": "tracy/tracy",
"version": "v2.2.3",
"version_normalized": "2.2.3.0",
"source": {
"type": "git",
"url": "https://github.com/nette/tracy.git",
"reference": "97889d2b8cfb7607cc370ca0ddb97c6f5b43deb9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/tracy/zipball/97889d2b8cfb7607cc370ca0ddb97c6f5b43deb9",
"reference": "97889d2b8cfb7607cc370ca0ddb97c6f5b43deb9",
"shasum": ""
},
"require": {
"php": ">=5.3.1"
},
"require-dev": {
"nette/tester": "~1.0"
},
"time": "2014-08-24 23:36:30",
"type": "library",
"installation-source": "dist",
"autoload": {
"classmap": [
"src/Tracy"
],
"files": [
"src/shortcuts.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
"GPL-2.0",
"GPL-3.0"
],
"authors": [
{
"name": "David Grudl",
"homepage": "http://davidgrudl.com"
},
{
"name": "Nette Community",
"homepage": "http://nette.org/contributors"
}
],
"description": "Tracy: useful PHP debugger",
"homepage": "http://tracy.nette.org",
"keywords": [
"debug",
"debugger",
"nette"
]
},
{
"name": "rockettheme/toolbox",
"version": "dev-develop",
"version_normalized": "dev-develop",
"source": {
"type": "git",
"url": "https://github.com/rockettheme/toolbox.git",
"reference": "5f26caed7527e1b30ac9f8200af79017c109a79c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rockettheme/toolbox/zipball/5f26caed7527e1b30ac9f8200af79017c109a79c",
"reference": "5f26caed7527e1b30ac9f8200af79017c109a79c",
"shasum": ""
},
"require": {
"ircmaxell/password-compat": "1.0.*",
"php": ">=5.4.0",
"pimple/pimple": "~3.0",
"symfony/event-dispatcher": "~2.5",
"symfony/yaml": "~2.5"
},
"require-dev": {
"phpunit/phpunit": "4.0.*"
},
"time": "2014-08-28 14:25:14",
"type": "library",
"installation-source": "source",
"autoload": {
"psr-4": {
"RocketTheme\\Toolbox\\ArrayTraits\\": "ArrayTraits/src",
"RocketTheme\\Toolbox\\DI\\": "DI/src",
"RocketTheme\\Toolbox\\Event\\": "Event/src",
"RocketTheme\\Toolbox\\File\\": "File/src",
"RocketTheme\\Toolbox\\ResourceLocation\\": "ResourceLocation/src",
"RocketTheme\\Toolbox\\Session\\": "Session/src",
"RocketTheme\\Toolbox\\StreamWrapper\\": "StreamWrapper/src"
}
},
"license": [
"MIT"
],
"description": "RocketTheme Toolbox Library",
"homepage": "http://www.rockettheme.com",
"keywords": [
"php",
"rockettheme"
],
"support": {
"source": "https://github.com/rockettheme/toolbox/tree/develop",
"issues": "https://github.com/rockettheme/toolbox/issues"
}
}
]

View File

@@ -166,7 +166,12 @@ class ParsedownExtra extends Parsedown
{
$DOMDocument = new DOMDocument;
$DOMDocument->loadXML($Block['element']);
$DOMDocument->loadXML($Block['element'], LIBXML_NOERROR | LIBXML_NOWARNING);
if ($DOMDocument->documentElement === null)
{
return $Block;
}
$result = $DOMDocument->documentElement->getAttribute('markdown');

View File

@@ -103,6 +103,9 @@ class Debugger
/** @var string name of the directory where errors should be logged */
public static $logDirectory;
/** @var int log bluescreen in production mode for this error severity */
public static $logSeverity = 0;
/** @var string|array email(s) to which send error notifications */
public static $email;
@@ -475,6 +478,12 @@ class Debugger
} elseif (($severity & error_reporting()) !== $severity) {
return FALSE; // calls normal error handler to fill-in error_get_last()
} elseif (($severity & self::$logSeverity) === $severity) {
$e = new ErrorException($message, 0, $severity, $file, $line);
$e->context = $context;
self::log($e, self::ERROR);
return NULL;
} elseif (!self::$productionMode && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity))) {
$e = new ErrorException($message, 0, $severity, $file, $line);
$e->context = $context;

View File

@@ -79,6 +79,7 @@ class Helpers
}
/** @internal */
public static function fixStack($exception)
{
if (function_exists('xdebug_get_function_stack')) {
@@ -104,11 +105,7 @@ class Helpers
}
/**
* Returns correctly UTF-8 encoded string.
* @param string byte stream to fix
* @return string
*/
/** @internal */
public static function fixEncoding($s)
{
if (PHP_VERSION_ID < 50400) {

View File

@@ -72,6 +72,7 @@ class Logger
* @param string
* @param string
* @return void
* @internal
*/
public static function defaultMailer($message, $email)
{

View File

@@ -42,6 +42,7 @@ class OutputDebugger
}
/** @internal */
public function handler($s, $phase)
{
$trace = debug_backtrace(FALSE);
@@ -62,7 +63,6 @@ class OutputDebugger
{
$res = '<style>code, pre {white-space:nowrap} a {text-decoration:none} pre {color:gray;display:inline} big {color:red}</style><code>';
foreach ($this->list as $item) {
list($file, $line, $s) = $item;
$res .= Helpers::editorLink($item[0], $item[1]) . ' '
. str_replace(self::BOM, '<big>BOM</big>', Dumper::toHtml($item[2])) . "<br>\n";
}

View File

@@ -14,6 +14,7 @@ html {
background: white;
color: #333;
position: absolute;
z-index: 20000;
left: 0;
top: 0;
width: 100%;
@@ -54,7 +55,6 @@ html {
position: absolute;
right: .5em;
top: .5em;
z-index: 20000;
text-decoration: none;
background: #CD1818;
color: white !important;

View File

@@ -117,7 +117,7 @@ $counter = 0;
<i>inner-code</i><?php if (isset($row['line'])) echo ':', $row['line'] ?>
<?php endif ?>
<?php if (isset($row['file']) && is_file($row['file'])): ?><a href="#tracyBsSrc<?php echo "$level-$key" ?>" class="tracy-toggle tracy-collapsed">source</a>&nbsp; <?php endif ?>
<?php if (isset($row['file']) && is_file($row['file'])): ?><a href="#tracyBsSrc<?php echo "$level-$key" ?>" class="tracy-toggle<?php if ($expanded !== $key) echo ' tracy-collapsed' ?>">source</a>&nbsp; <?php endif ?>
<?php if (isset($row['object'])) echo "<a href='#tracyBsObj$level-$key' class='tracy-toggle tracy-collapsed'>" ?>
<?php if (isset($row['class'])) echo htmlspecialchars($row['class'] . $row['type']) ?>
@@ -128,11 +128,11 @@ $counter = 0;
</p>
<?php if (isset($row['file']) && is_file($row['file'])): ?>
<div <?php if ($expanded !== $key) echo 'class="tracy-collapsed"'; ?> id="tracyBsSrc<?php echo "$level-$key" ?>"><?php echo self::highlightFile($row['file'], $row['line']) ?></div>
<div <?php if ($expanded !== $key) echo 'class="tracy-collapsed"' ?> id="tracyBsSrc<?php echo "$level-$key" ?>"><?php echo self::highlightFile($row['file'], $row['line']) ?></div>
<?php endif ?>
<?php if (isset($row['object'])): ?>
<div class="tracy-collapsed outer" id="tracyBsObj<?php echo "$level-$key" ?>"><?php echo Dumper::toHtml($row['object']); ?></div>
<div class="tracy-collapsed outer" id="tracyBsObj<?php echo "$level-$key" ?>"><?php echo Dumper::toHtml($row['object']) ?></div>
<?php endif ?>
<?php if (!empty($row['args'])): ?>