Merge branch 'release/0.9.0'

This commit is contained in:
Djamil Legato
2014-08-25 10:30:24 -07:00
200 changed files with 18734 additions and 1319 deletions

2
.gitignore vendored
View File

@@ -7,6 +7,8 @@ composer.lock
# Grav Specific
cache/*
!cache/.*
assets/*
!assets/.*
logs/*
!logs/.*
images/*

View File

@@ -1,26 +1,38 @@
<IfModule mod_rewrite.c>
Options -Multiviews
RewriteEngine On
# access site
##
# If you are getting 404 errors on subpages, you may have to uncomment the RewriteBase entry
# You should change the '/' to your appropriate subfolder. For example if you have
# your Grav install at the root of your site '/' should work, else it might be something
# along the lines of: RewriteBase /<your_sub_folder>
##
# RewriteBase /
# Access site
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
RewriteRule .* index.php [L]
# block various user files from being accessed directly
# Block various user files from being accessed directly
RewriteRule ^user/accounts/(.*)$ error [R=301,L]
RewriteRule ^user/config/(.*)$ error [R=301,L]
RewriteRule ^user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ error [R=301,L]
# block cache
# Block cache/
RewriteRule ^cache/(.*) error [R=301,L]
# block bin
# Block bin/
RewriteRule ^bin/(.*)$ error [R=301,L]
# block system
# Block system/
RewriteRule ^system/(.*)$ error [R=301,L]
# block vendor
# Block vendor/
RewriteRule ^vendor/(.*)$ error [R=301,L]
</IfModule>

View File

@@ -34,6 +34,19 @@ You can download a **ready-built** package from the [Downloads page on http://ge
Check out the [install procedures](http://learn.getgrav.org/basics/installation) for more information.
# Contributing
We appreciate any contribution to Grav, whether it is related to bugs, grammar, or simply a suggestion or improvement.
However, we ask that any contribution follow our simple guidelines in order to be properly received.
All our projects follow the [GitFlow branching model][gitflow-model], from development to release. If you are not familiar with it, there are several guides and tutorials to make you understand what it is about.
You will probably want to get started by installing [this very good collection of git extensions][gitflow-extensions].
What you mainly want to know is that:
- All the main activity happens in the `develop` branch. Any pull request should be addressed only to that branch. We will not consider pull requests made to the `master`.
- It's very well appreciated, and highly suggested, to start a new feature whenever you want to make changes or add functionalities. It will make it much easier for us to just checkout your feature branch and test it, before merging it into `develop`
# Getting Started
* [What is Grav?](http://learn.getgrav.org/basics/what-is-grav)
@@ -51,3 +64,7 @@ Check out the [install procedures](http://learn.getgrav.org/basics/installation)
# License
See [LICENSE](LICENSE)
[gitflow-model]: http://nvie.com/posts/a-successful-git-branching-model/
[gitflow-extensions]: https://github.com/nvie/gitflow

View File

@@ -1 +1 @@
0.8.0
0.9.0

0
assets/.gitkeep Normal file
View File

View File

@@ -1,9 +1,13 @@
#!/usr/bin/env php
<?php
if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
}
use Symfony\Component\Console\Application;
require_once(__DIR__ . '/../system/autoload.php');
require_once(__DIR__ . '/../vendor/autoload.php');
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');

View File

@@ -1,25 +1,32 @@
{
"name": "rhuk/grav",
"name": "getgrav/grav",
"type": "library",
"description": "Grav is a powerful flat CMS influenced by Pico, Stacey, Kirby and others...",
"keywords": ["cms"],
"description": "Modern, Crazy Fast, Ridiculously Easy and Amazingly Powerful Flat-File CMS",
"keywords": ["cms","flat-file cms","flat cms","flatfile cms","php"],
"homepage": "http://getgrav.org",
"license": "MIT",
"authors": [
{
"name": "Andy Miller",
"email": "rhuk@getgrav.org"
}
],
"require": {
"php": ">=5.3.10",
"twig/twig": "1.16.*@dev",
"erusev/parsedown": "dev-master",
"symfony/yaml": "2.5.*@dev",
"symfony/console": "2.5.*@dev",
"doctrine/cache": "1.4.*@dev",
"tracy/tracy": "dev-master",
"gregwar/image": "dev-master",
"ircmaxell/password-compat": "1.0.*"
"php": ">=5.4.0",
"twig/twig": "~1.16",
"erusev/parsedown-extra": "dev-master",
"symfony/yaml": "~2.5",
"symfony/console": "~2.5",
"symfony/event-dispatcher": "~2.5",
"doctrine/cache": "~1.3",
"tracy/tracy": "~2.2",
"gregwar/image": "~2.0",
"ircmaxell/password-compat": "1.0.*",
"mrclay/minify": "~2.2",
"donatj/phpuseragentparser": "dev-master",
"pimple/pimple": "~3.0"
},
"autoload": {
"psr-4": {
"Grav\\": "system/src/"
},
"files": ["system/defines.php"]
},
"archive": {
"exclude": ["VERSION"]
}
}

View File

@@ -1,46 +1,37 @@
<?php
namespace Grav\Common;
namespace Grav;
if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
throw new \RuntimeException(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
exit(sprintf('You are running PHP %s, but Grav needs at least <strong>PHP %s</strong> to run.', $ver, $req));
}
$autoload = __DIR__ . '/vendor/autoload.php';
if (!is_file($autoload)) {
exit('Please run: <i>composer install -o</i>');
}
use Tracy\Debugger;
use Grav\Common\Grav;
use Grav\Common\Debugger;
// Register system libraries to the auto-loader.
$loader = require_once __DIR__ . '/system/autoload.php';
// Register the auto-loader.
$loader = require_once $autoload;
if (!ini_get('date.timezone')) {
date_default_timezone_set('UTC');
}
// Use output buffering to prevent headers from being sent too early.
ob_start();
// Start the timer and enable debugger in production mode as we do not have system configuration yet.
// Debugger catches all errors and logs them, for example if the script doesn't have write permissions.
Debugger::timer();
Debugger::enable(Debugger::DEVELOPMENT, is_dir(LOG_DIR) ? LOG_DIR : null);
$grav = new Grav;
$grav = Grav::instance(
[
'loader' => $loader,
'debugger' => new Debugger(Debugger::PRODUCTION)
]
);
try {
// Register all the Grav bits into registry.
$registry = Registry::instance();
$registry->store('autoloader', $loader);
$registry->store('Grav', $grav);
$registry->store('Uri', new Uri);
$registry->store('Config', Config::instance(CACHE_DIR . 'config.php'));
$registry->store('Cache', new Cache);
$registry->store('Twig', new Twig);
$registry->store('Pages', new Page\Pages);
$registry->store('Taxonomy', new Taxonomy);
$grav['debugger']->init();
$grav->process();
} catch (\Exception $e) {
$grav->fireEvent('onFatalException', $e);
$grav->fireEvent('onFatalException');
throw $e;
}
ob_end_flush();

View File

@@ -1,8 +0,0 @@
<?php
require_once(__DIR__ . '/../system/defines.php');
// Use composer auto-loader and just add our namespace into it.
$loader = require_once(__DIR__ . '/../vendor/autoload.php');
$loader->addPsr4('Grav\\', LIB_DIR . 'Grav');
return $loader;

View File

@@ -1,3 +1,12 @@
defaults:
type: file
thumb: media/thumb.png
mime: application/octet-stream
image:
filters:
default:
- enableProgressive
jpg:
type: image
thumb: media/thumb-jpg.png

View File

@@ -0,0 +1,36 @@
schemes:
plugin:
type: ReadOnlyStream
paths:
- user/plugins
- system/plugins
# asset:
# type: ReadOnlyStream
# paths:
# - assets
# cache:
# type: ReadOnlyStream
# paths:
# - cache
# log:
# type: ReadOnlyStream
# paths:
# - logs
page:
type: ReadOnlyStream
paths:
- user/pages
account:
type: ReadOnlyStream
paths:
- user/accounts
data:
type: ReadOnlyStream
paths:
- user/data

View File

@@ -3,6 +3,7 @@ home:
pages:
theme: antimatter # Default theme (defaults to "antimatter" theme)
markdown_extra: false # Enable support for Markdown Extra support (GFM by default)
order:
by: defaults # Order pages by "default", "alpha" or "date"
dir: asc # Default ordering direction, "asc" or "desc"
@@ -15,7 +16,7 @@ pages:
markdown: true # Process Markdown
twig: false # Process Twig
events:
page: false # Enable page level events
page: true # Enable page level events
twig: true # Enable twig level events
cache:
@@ -26,14 +27,23 @@ cache:
prefix: 'g' # Cache prefix string (prevents cache conflicts)
twig:
cache: false # Set to true to enable twig caching
debug: true # Enable Twig debug
auto_reload: true # Refresh cache on changes
autoescape: false # Autoescape Twig vars
cache: true # Set to true to enable twig caching
debug: false # Enable Twig debug
auto_reload: true # Refresh cache on changes
autoescape: false # Autoescape Twig vars
assets: # Configuration for Assets Manager (JS, CSS)
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file
css_minify: true # Minify the CSS during pipelining
css_rewrite: true # Rewrite any CSS relative URLs during pipelining
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
js_minify: true # Minify the JS during pipelining
debugger:
enabled: true # Enable Grav debugger
max_depth: 10 # How many nested levels to display for objects or arrays
enabled: false # Enable Grav debugger and following settings
mode: detect # Mode tracy Debugger should be set to when enabled: detect|development|production
strict: false # Throw fatal error also on PHP warnings and notices
max_depth: 10 # How many nested levels to display for objects or arrays
log:
enabled: true # Enable logging
timing: false # Enable timing logging
enabled: true # Enable logging
timing: false # Enable timing logging

View File

@@ -2,7 +2,7 @@
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '0.8.0');
define('GRAV_VERSION', '0.9.0');
define('DS', '/');
// Directories and Paths
@@ -12,6 +12,7 @@ if (!defined('ROOT_DIR')) {
define('USER_PATH', 'user/');
define('USER_DIR', ROOT_DIR . USER_PATH);
define('SYSTEM_DIR', ROOT_DIR .'system/');
define('ASSETS_DIR', ROOT_DIR . 'assets/');
define('CACHE_DIR', ROOT_DIR .'cache/');
define('IMAGES_DIR', ROOT_DIR . 'images/');
define('LOG_DIR', ROOT_DIR .'logs/');
@@ -20,7 +21,6 @@ define('LIB_DIR', SYSTEM_DIR .'src/');
define('ACCOUNTS_DIR', USER_DIR .'accounts/');
define('DATA_DIR', USER_DIR .'data/');
define('PAGES_DIR', USER_DIR .'pages/');
define('BLOCKS_DIR', USER_DIR .'blocks/');
define('PLUGINS_DIR', USER_DIR .'plugins/');
define('THEMES_DIR', USER_DIR .'themes/');

View File

@@ -0,0 +1,36 @@
<?php
namespace Grav\Common;
/**
* Simple wrapper for the very simple parse_user_agent() function
*/
class Browser {
protected $useragent;
public function __construct()
{
$this->useragent = parse_user_agent();
}
public function getBrowser()
{
return strtolower($this->useragent['browser']);
}
public function getPlatform()
{
return strtolower($this->useragent['platform']);
}
public function getLongVersion()
{
return $this->useragent['version'];
}
public function getVersion()
{
$version = explode('.', $this->getLongVersion());
return intval($version[0]);
}
}

View File

@@ -0,0 +1,693 @@
<?php
namespace Grav\Common;
use Closure;
use Exception;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RegexIterator;
define('CSS_ASSET', true);
define('JS_ASSET', false);
/**
* Handles Asset management (CSS & JS) and also pipelining (combining into a single file for each asset)
*
* Based on stolz/assets (https://github.com/Stolz/Assets) package modified for use with Grav
*
* @author RocketTheme
* @license MIT
*/
class Assets
{
use GravTrait;
/** @const Regex to match CSS and JavaScript files */
const DEFAULT_REGEX = '/.\.(css|js)$/i';
/** @const Regex to match CSS files */
const CSS_REGEX = '/.\.css$/i';
/** @const Regex to match JavaScript files */
const JS_REGEX = '/.\.js$/i';
/** @const Regex to match CSS urls */
const CSS_URL_REGEX = '{url\([\'\"]?((?!http|//).*?)[\'\"]?\)}';
/** @const Regex to match CSS sourcemap comments */
const CSS_SOURCEMAP_REGEX = '{\/\*# (.*) \*\/}';
/** @const Regex to match CSS import content */
const CSS_IMPORT_REGEX = '{@import(.*);}';
/**
* Closure used by the pipeline to fetch assets.
*
* Useful when file_get_contents() function is not available in your PHP
* instalation or when you want to apply any kind of preprocessing to
* your assets before they get pipelined.
*
* The closure will receive as the only parameter a string with the path/URL of the asset and
* it should return the content of the asset file as a string.
* @var Closure
*/
protected $fetch_command;
// Configuration toggles to enable/disable the pipelining feature
protected $css_pipeline = false;
protected $js_pipeline = false;
// The asset holding arrays
protected $collections = array();
protected $css = array();
protected $js = array();
// Some configuration variables
protected $config;
protected $base_url;
// Default values for pipeline settings
protected $css_minify = true;
protected $css_rewrite = true;
protected $js_minify = true;
// Arrays to hold assets that should NOT be pipelined
protected $css_no_pipeline = array();
protected $js_no_pipeline = array();
public function __construct(array $options = array())
{
// Forward config options
if($options)
$this->config((array)$options);
}
/**
* Initialization called in the Grav lifecycle to initialize the Assets with appropriate configuration
*/
public function init()
{
/** @var Config $config */
$config = self::$grav['config'];
$base_url = trim($config->get('system.base_url_relative'));
$theme = trim($config->get('system.pages.theme'));
$asset_config = (array)$config->get('system.assets');
$this->config($asset_config);
$this->base_url = $base_url . '/';
}
/**
* Set up configuration options.
*
* All the class properties except 'js' and 'css' are accepted here.
* Also, an extra option 'autoload' may be passed containing an array of
* assets and/or collections that will be automatically added on startup.
*
* @param array $options Configurable options.
* @return $this
* @throws \Exception
*/
public function config(array $config)
{
// Set pipeline modes
if(isset($config['css_pipeline']))
$this->css_pipeline = $config['css_pipeline'];
if(isset($config['js_pipeline']))
$this->js_pipeline = $config['js_pipeline'];
// Pipeline requires public dir
if(($this->js_pipeline || $this->css_pipeline) && ! is_dir(ASSETS_DIR))
throw new \Exception('Assets: Public dir not found');
// Set custom pipeline fetch command
if(isset($config['fetch_command']) and ($config['fetch_command'] instanceof Closure))
$this->fetch_command = $config['fetch_command'];
// Set CSS Minify state
if(isset($config['css_minify']))
$this->css_minify = $config['css_minify'];
if(isset($config['css_rewrite']))
$this->css_rewrite = $config['css_rewrite'];
// Set JS Minify state
if(isset($config['js_minify']))
$this->js_minify = $config['js_minify'];
// Set collections
if(isset($config['collections']) and is_array($config['collections']))
$this->collections = $config['collections'];
// Autoload assets
if(isset($config['autoload']) and is_array($config['autoload']))
{
foreach($config['autoload'] as $asset)
{
$this->add($asset);
}
}
return $this;
}
/**
* Add an asset or a collection of assets.
*
* It automatically detects the asset type (JavaScript, CSS or collection).
* You may add more than one asset passing an array as argument.
*
* @param mixed $asset
* @param int $priority the priority, bigger comes first
* @param bool $pipeline false if this should not be pipelined
* @return $this
*/
public function add($asset, $priority = 10, $pipeline = true)
{
// More than one asset
if(is_array($asset))
{
foreach($asset as $a)
$this->add($a, $priority, $pipeline);
}
// Collection
elseif(isset($this->collections[$asset]))
{
$this->add($this->collections[$asset], $priority, $pipeline);
}
else
{
// JavaScript or CSS
$info = pathinfo($asset);
if(isset($info['extension']))
{
$ext = strtolower($info['extension']);
if($ext === 'css')
$this->addCss($asset, $priority, $pipeline);
elseif($ext === 'js')
$this->addJs($asset, $priority, $pipeline);
}
}
return $this;
}
/**
* Add a CSS asset.
*
* It checks for duplicates.
* You may add more than one asset passing an array as argument.
*
* @param mixed $asset
* @param int $priority the priority, bigger comes first
* @param bool $pipeline false if this should not be pipelined
* @return $this
*/
public function addCss($asset, $priority = 10, $pipeline = true)
{
if(is_array($asset))
{
foreach($asset as $a)
$this->addCss($a, $priority, $pipeline);
return $this;
}
if( ! $this->isRemoteLink($asset))
$asset = $this->buildLocalLink($asset);
if( ! in_array($asset, $this->css))
$this->css[] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline];
return $this;
}
/**
* Add a JavaScript asset.
*
* It checks for duplicates.
* You may add more than one asset passing an array as argument.
*
* @param mixed $asset
* @param int $priority the priority, bigger comes first
* @param bool $pipeline false if this should not be pipelined
* @return $this
*/
public function addJs($asset, $priority = 10, $pipeline = true)
{
if(is_array($asset))
{
foreach($asset as $a)
$this->addJs($a, $priority, $pipeline);
return $this;
}
if( ! $this->isRemoteLink($asset))
$asset = $this->buildLocalLink($asset);
if( ! in_array($asset, $this->js))
$this->js[] = ['asset'=>$asset, 'priority'=>$priority, 'pipeline'=>$pipeline];
return $this;
}
/**
* Build the CSS link tags.
*
* @return string
*/
public function css()
{
if( ! $this->css)
return null;
// Sort array by priorities (larger priority first)
usort($this->css, function ($a, $b) {return $a['priority'] - $b['priority'];});
$this->css = array_reverse($this->css);
$output = '';
if($this->css_pipeline) {
$output .= '<link type="text/css" rel="stylesheet" href="'.$this->pipeline(CSS_ASSET).'" />'."\n";
foreach ($this->css_no_pipeline as $file) {
$output .= '<link type="text/css" rel="stylesheet" href="'.$file['asset'].'" />'."\n";
}
return $output;
}
foreach($this->css as $file)
$output .= '<link type="text/css" rel="stylesheet" href="'.$file['asset'].'" />'."\n";
return $output;
}
/**
* Build the JavaScript script tags.
*
* @return string
*/
public function js()
{
if( ! $this->js)
return null;
// Sort array by priorities (larger priority first)
usort($this->js, function ($a, $b) {return $a['priority'] - $b['priority'];});
$this->js = array_reverse($this->js);
$output = '';
if($this->js_pipeline) {
$output .= '<script type="text/javascript" src="'.$this->pipeline(JS_ASSET).'"></script>'."\n";
foreach ($this->js_no_pipeline as $file) {
$output .= '<script type="text/javascript" src="'.$file['asset'].'"></script>'."\n";
}
return $output;
}
foreach($this->js as $file)
$output .= '<script type="text/javascript" src="'.$file['asset'].'"></script>'."\n";
return $output;
}
/**
* Add/replace collection.
*
* @param string $collectionName
* @param array $assets
* @return $this
*/
public function registerCollection($collectionName, Array $assets)
{
$this->collections[$collectionName] = $assets;
return $this;
}
/**
* Reset all assets.
*
* @return $this
*/
public function reset()
{
return $this->resetCss()->resetJs();
}
/**
* Reset CSS assets.
*
* @return $this
*/
public function resetCss()
{
$this->css = array();
return $this;
}
/**
* Reset JavaScript assets.
*
* @return $this
*/
public function resetJs()
{
$this->js = array();
return $this;
}
/**
* Minifiy and concatenate CSS / JS files.
*
* @return string
*/
protected function pipeline($css = true)
{
/** @var Cache $cache */
$cache = self::$grav['cache'];
$key = '?'.$cache->getKey();
if ($css) {
$file = md5(json_encode($this->css) . $this->js_minify . $this->css_minify . $this->css_rewrite) . '.css';
foreach ($this->css as $id => $asset) {
if (!$asset['pipeline']) {
$this->css_no_pipeline[] = $asset;
unset($this->css[$id]);
}
}
} else {
$file = md5(json_encode($this->js) . $this->js_minify . $this->css_minify . $this->css_rewrite) . '.js';
foreach ($this->js as $id => $asset) {
if (!$asset['pipeline']) {
$this->js_no_pipeline[] = $asset;
unset($this->js[$id]);
}
}
}
$relative_path = "{$this->base_url}".basename(ASSETS_DIR)."/{$file}";
$absolute_path = ASSETS_DIR.$file;
// If pipeline exist return it
if(file_exists($absolute_path))
return $relative_path . $key;
// Concatenate files
if ($css) {
$buffer = $this->gatherLinks($this->css, CSS_ASSET);
if ($this->css_minify) {
$min = new \CSSmin();
$buffer = $min->run($buffer);
}
} else {
$buffer = $this->gatherLinks($this->js, JS_ASSET);
if ($this->js_minify) {
$buffer = \JSMin::minify($buffer);
}
}
// Write file
file_put_contents($absolute_path, $buffer);
return $relative_path . $key;
}
/**
* Download and concatenate the content of several links.
*
* @param array $links
* @return string
*/
protected function gatherLinks(array $links, $css = true)
{
$buffer = '';
$local = true;
foreach($links as $asset)
{
$link = $asset['asset'];
$relative_path = $link;
if($this->isRemoteLink($link)) {
$local = false;
if('//' === substr($link, 0, 2))
$link = 'http:' . $link;
} else {
// Fix to remove relative dir if grav is in one
if (($this->base_url != '/') && (strpos($this->base_url, $link) == 0)) {
$relative_path = str_replace($this->base_url, '/', $link);
}
$relative_dir = dirname ($relative_path);
$link = ROOT_DIR . $relative_path;
}
$file = ($this->fetch_command instanceof Closure) ? $this->fetch_command->__invoke($link) : file_get_contents($link);
// If this is CSS + the file is local + rewrite enabled
if ($css && $local && $this->css_rewrite) {
$file = $this->cssRewrite($file, $relative_dir);
}
$buffer .= $file;
}
// Pull out @imports and move to top
if ($css) {
$buffer = $this->moveImports($buffer);
}
return $buffer;
}
/**
* Moves @import statements to the top of the file per the CSS specification
*
* @param string $file the file containing the combined CSS files
* @return string the modified file with any @imports at the top of the file
*/
protected function moveImports($file)
{
$this->imports = array();
$file = preg_replace_callback(self::CSS_IMPORT_REGEX,
function($matches) {
$this->imports[] = $matches[0];
return '';
},
$file
);
return implode("\n", $this->imports) . "\n\n" . $file;
}
/**
* Finds relative CSS urls() and rewrites the URL with an absolute one
* @param string $file the css source file
* @param string $relative_path relative path to the css file
* @return [type] [description]
*/
protected function cssRewrite($file, $relative_path)
{
// Strip any sourcemap comments
$file = preg_replace(self::CSS_SOURCEMAP_REGEX, '', $file);
// Find any css url() elements, grab the URLs and calculate an absolute path
// Then replace the old url with the new one
$file = preg_replace_callback(self::CSS_URL_REGEX,
function($matches) use ($relative_path) {
$old_url = $matches[1];
$newpath = array();
$paths = explode('/', $old_url);
foreach ($paths as $path) {
if ($path == '..') {
$relative_path = dirname($relative_path);
} else {
$newpath[] = $path;
}
}
$new_url = rtrim($this->base_url,'/') . $relative_path . '/' . implode('/', $newpath);
return str_replace($old_url, $new_url, $matches[0]);
},
$file
);
return $file;
}
/**
* Build local links including grav asset shortcodes
*
* @param string $asset the asset string reference
* @return string the final link url to the asset
*/
protected function buildLocalLink($asset)
{
try {
return $this->base_url . self::$grav['locator']->findResource($asset, false);
} catch (\Exception $e) {}
return $this->base_url . $asset;
}
/**
* Determine whether a link is local or remote.
*
* Undestands both "http://" and "https://" as well as protocol agnostic links "//"
*
* @param string $link
* @return bool
*/
protected function isRemoteLink($link)
{
return ('http://' === substr($link, 0, 7) or 'https://' === substr($link, 0, 8) or '//' === substr($link, 0, 2));
}
/**
* Get all CSS assets already added.
*
* @return array
*/
public function getCss()
{
return $this->css;
}
/**
* Get all JavaScript assets already added.
*
* @return array
*/
public function getJs()
{
return $this->js;
}
/**
* Add all assets matching $pattern within $directory.
*
* @param string $directory Relative to $this->public_dir
* @param string $pattern (regex)
* @return $this
* @throws Exception
*/
public function addDir($directory, $pattern = self::DEFAULT_REGEX)
{
// Check if public_dir exists
if( ! is_dir(ASSETS_DIR))
throw new Exception('Assets: Public dir not found');
// Get files
$files = $this->rglob(ASSETS_DIR . DIRECTORY_SEPARATOR . $directory, $pattern, ASSETS_DIR);
// No luck? Nothing to do
if( ! $files)
return $this;
// Add CSS files
if($pattern === self::CSS_REGEX)
{
$this->css = array_unique(array_merge($this->css, $files));
return $this;
}
// Add JavaScript files
if($pattern === self::JS_REGEX)
{
$this->js = array_unique(array_merge($this->js, $files));
return $this;
}
// Unknown pattern. We must poll to know the extension :(
foreach($files as $asset)
{
$info = pathinfo($asset);
if(isset($info['extension']))
{
$ext = strtolower($info['extension']);
if($ext === 'css' and ! in_array($asset, $this->css))
$this->css[] = $asset;
elseif($ext === 'js' and ! in_array($asset, $this->js))
$this->js[] = $asset;
}
}
return $this;
}
/**
* Add all CSS assets within $directory (relative to public dir).
*
* @param string $directory Relative to $this->public_dir
* @return $this
*/
public function addDirCss($directory)
{
return $this->addDir($directory, self::CSS_REGEX);
}
/**
* Add all JavaScript assets within $directory.
*
* @param string $directory Relative to $this->public_dir
* @return $this
*/
public function addDirJs($directory)
{
return $this->addDir($directory, self::JS_REGEX);
}
/**
* Recursively get files matching $pattern within $directory.
*
* @param string $directory
* @param string $pattern (regex)
* @param string $ltrim Will be trimed from the left of the file path
* @return array
*/
protected function rglob($directory, $pattern, $ltrim = null)
{
$iterator = new RegexIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS)), $pattern);
$offset = strlen($ltrim);
$files = array();
foreach($iterator as $file)
$files[] = substr($file->getPathname(), $offset);
return $files;
}
/**
* @param $a
* @param $b
* @return mixed
*/
protected function priorityCompare($a, $b)
{
return $a ['priority'] - $b ['priority'];
}
public function __toString() {
return '';
}
}

View File

@@ -1,6 +1,8 @@
<?php
namespace Grav\Common;
use \Doctrine\Common\Cache\Cache as DoctrineCache;
/**
* The GravCache object is used throughout Grav to store and retrieve cached data.
* It uses DoctrineCache library and supports a variety of caching mechanisms. Those include:
@@ -23,7 +25,7 @@ class Cache extends Getters
protected $key;
/**
* @var \Doctrine\Common\Cache\Cache
* @var DoctrineCache
*/
protected $driver;
@@ -32,25 +34,67 @@ class Cache extends Getters
*/
protected $enabled;
/**
* Constructor
*
* @params Grav $grav
*/
public function __construct(Grav $grav)
{
$this->init($grav);
}
/**
* Initialization that sets a base key and the driver based on configuration settings
*
* @param Grav $grav
* @return void
*/
public function init()
public function init(Grav $grav)
{
/** @var Config $config */
$config = Registry::get('Config');
$prefix = $config->get('system.cache.prefix');
/** @var Uri $uri */
$uri = Registry::get('Uri');
$this->config = $grav['config'];
$this->enabled = (bool) $config->get('system.cache.enabled');
/** @var Uri $uri */
$uri = $grav['uri'];
$prefix = $this->config->get('system.cache.prefix');
$this->enabled = (bool) $this->config->get('system.cache.enabled');
// Cache key allows us to invalidate all cache on configuration changes.
$this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $config->key . GRAV_VERSION), 2, 8);
$this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $this->config->key . GRAV_VERSION), 2, 8);
switch ($this->getCacheDriverName($config->get('system.cache.driver'))) {
$this->driver = $this->getCacheDriver();
}
/**
* Automatically picks the cache mechanism to use. If you pick one manually it will use that
* If there is no config option for $driver in the config, or it's set to 'auto', it will
* pick the best option based on which cache extensions are installed.
*
* @return DoctrineCacheDriver The cache driver to use
*/
public function getCacheDriver()
{
$setting = $this->config->get('system.cache.driver');
$driver_name = 'file';
if (!$setting || $setting == 'auto') {
if (extension_loaded('apc')) {
$driver_name = 'apc';
} elseif (extension_loaded('wincache')) {
$driver_name = 'wincache';
} elseif (extension_loaded('xcache')) {
$driver_name = 'xcache';
} elseif (extension_loaded('memcache')) {
$driver_name = 'memcache';
}
} else {
$driver_name = $setting;
}
switch ($driver_name) {
case 'apc':
$driver = new \Doctrine\Common\Cache\ApcCache();
break;
@@ -75,33 +119,8 @@ class Cache extends Getters
$driver = new \Doctrine\Common\Cache\FilesystemCache(CACHE_DIR);
break;
}
$this->driver = $driver;
}
/**
* Automatically picks the cache mechanism to use. If you pick one manually it will use that
* If there is no config option for $driver in the config, or it's set to 'auto', it will
* pick the best option based on which cache extensions are installed.
*
* @param string $setting
* @return string The name of the best cache driver to use
*/
protected function getCacheDriverName($setting = null)
{
if (!$setting || $setting == 'auto') {
if (extension_loaded('apc') && ini_get('apc.enabled')) {
return 'apc';
} elseif (extension_loaded('wincache')) {
return 'wincache';
} elseif (extension_loaded('xcache') && ini_get('xcache.size') && ini_get('xcache.cacher')) {
return 'xcache';
} else {
return 'file';
}
} else {
return $setting;
}
return $driver;
}
/**

View File

@@ -101,16 +101,17 @@ class Config extends Data
}
/**
* Gets configuration instance.
* Load configuration.
*
* @param string $filename
* @param Grav $grav
* @return \Grav\Common\Config
*/
public static function instance($filename)
public static function instance(Grav $grav)
{
$filename = $grav['config_path'];
// Load cached version if available..
if (file_exists($filename)) {
clearstatcache(true, $filename);
require_once $filename;
if (class_exists('\Grav\Config')) {
@@ -131,11 +132,11 @@ class Config extends Data
// If not set, add manually current base url.
if (empty($instance->items['system']['base_url_absolute'])) {
$instance->items['system']['base_url_absolute'] = Registry::get('Uri')->rootUrl(true);
$instance->items['system']['base_url_absolute'] = $grav['uri']->rootUrl(true);
}
if (empty($instance->items['system']['base_url_relative'])) {
$instance->items['system']['base_url_relative'] = Registry::get('Uri')->rootUrl(false);
$instance->items['system']['base_url_relative'] = $grav['uri']->rootUrl(false);
}
return $instance;

View File

@@ -1,7 +1,6 @@
<?php
namespace Grav\Common\Data;
use \Grav\Common\Registry;
use \Symfony\Component\Yaml\Yaml;
/**

View File

@@ -0,0 +1,73 @@
<?php
namespace Grav\Common;
use \Tracy\Debugger as TracyDebugger;
/**
* Class Debugger
* @package Grav\Common
*/
class Debugger
{
const PRODUCTION = TracyDebugger::PRODUCTION;
const DEVELOPMENT = TracyDebugger::DEVELOPMENT;
const DETECT = TracyDebugger::DETECT;
public function __construct($mode = self::PRODUCTION)
{
// Start the timer and enable debugger in production mode as we do not have system configuration yet.
// Debugger catches all errors and logs them, for example if the script doesn't have write permissions.
TracyDebugger::timer();
TracyDebugger::enable($mode, is_dir(LOG_DIR) ? LOG_DIR : null);
}
public function init()
{
$grav = Grav::instance();
/** @var Config $config */
$config = $grav['config'];
$mode = $config->get('system.debugger.mode');
TracyDebugger::$logDirectory = $config->get('system.debugger.log.enabled') ? LOG_DIR : null;
TracyDebugger::$maxDepth = $config->get('system.debugger.max_depth');
// Switch debugger into development mode if configured
if ($config->get('system.debugger.enabled')) {
if ($config->get('system.debugger.strict')) {
TracyDebugger::$strictMode = true;
}
if (function_exists('ini_set')) {
ini_set('display_errors', true);
}
if ($mode == strtolower('detect')) {
TracyDebugger::$productionMode = self::DETECT;
} elseif ($mode == strtolower('production')) {
TracyDebugger::$productionMode = self::PRODUCTION;
} else {
TracyDebugger::$productionMode = self::DEVELOPMENT;
}
}
}
/**
* Log a message.
*
* @param string $message
*/
public function log($message)
{
if (TracyDebugger::$logDirectory) {
TracyDebugger::log(sprintf($message, TracyDebugger::timer() * 1000));
}
}
public static function dump($var)
{
TracyDebugger::dump($var);
}
}

View File

@@ -85,7 +85,7 @@ class Config extends General
}
$vars = implode("\n", $vars);
return "<?php\nnamespace Grav;\n\nclass Config extends \\Grav\\Common\\Config {\n {$vars}\n}";
return "<?php\nnamespace Grav;\n\nclass Config extends \\Grav\\Common\\Config {\n {$vars}\n}";
}
/**

View File

@@ -149,7 +149,7 @@ class General implements FileInterface
$this->handle = fopen($this->filename, 'wb+');
}
$lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB;
return $this->locked = flock($this->handle, $lock);
return $this->locked = $this->handle ? flock($this->handle, $lock) : false;
}
/**

View File

@@ -36,7 +36,7 @@ abstract class Getters implements \ArrayAccess, \Countable
*/
public function __get($offset)
{
return $this->offsetGet($offset);
return $this->offsetGet($offset);
}
/**

View File

@@ -1,10 +1,10 @@
<?php
namespace Grav\Common;
use \Tracy\Debugger;
use \Grav\Common\Page\Page;
use \Grav\Common\Page\Pages;
use Grav\Common\Service\StreamsServiceProvider;
use Grav\Component\DI\Container;
use Grav\Component\EventDispatcher\Event;
use Grav\Component\EventDispatcher\EventDispatcher;
/**
* Grav
@@ -12,126 +12,141 @@ use \Grav\Common\Page\Pages;
* @author Andy Miller
* @link http://www.rockettheme.com
* @license http://opensource.org/licenses/MIT
* @version 0.1
* @version 0.8.0
*
* Originally based on Pico by Gilbert Pellegrom - http://pico.dev7studios.com
* Influeced by Pico, Stacey, Kirby, PieCrust and other great platforms...
*
* @property Plugins $plugins
* @property Config $config
* @property Cache $cache
* @property Uri $uri
* @property Pages $pages
* @property Page $page
* Influenced by Pico, Stacey, Kirby, PieCrust and other great platforms...
*/
class Grav extends Getters
class Grav extends Container
{
/**
* @var string Grav output.
* @var string
*/
protected $output;
/**
* @var array
* @var static
*/
protected $plugins;
protected static $instance;
/**
* @var Config
*/
protected $config;
public static function instance(array $values = array())
{
if (!self::$instance) {
self::$instance = static::load($values);
/**
* @var Cache
*/
protected $cache;
GravTrait::setGrav(self::$instance);
/**
* @var Uri
*/
protected $uri;
} elseif ($values) {
$instance = self::$instance;
foreach ($values as $key => $value) {
$instance->offsetSet($key, $value);
}
}
/**
* @var Pages
*/
protected $pages;
return self::$instance;
}
/**
* @var Page
*/
protected $page;
protected static function load(array $values)
{
$container = new static($values);
/**
* @var Twig
*/
protected $twig;
$container['config_path'] = CACHE_DIR . 'config.php';
/**
* @var Taxonomy
*/
protected $taxonomy;
$container['grav'] = $container;
$container['events'] = function ($c) {
return new EventDispatcher;
};
$container['uri'] = function ($c) {
return new Uri($c);
};
$container['config'] = function ($c) {
return Config::instance($c);
};
$container['cache'] = function ($c) {
return new Cache($c);
};
$container['plugins'] = function ($c) {
return new Plugins($c);
};
$container['themes'] = function ($c) {
return new Themes($c);
};
$container['twig'] = function ($c) {
return new Twig($c);
};
$container['taxonomy'] = function ($c) {
return new Taxonomy($c);
};
$container['pages'] = function ($c) {
return new Page\Pages($c);
};
$container['assets'] = function ($c) {
return new Assets();
};
$container['page'] = function ($c) {
$page = $c['pages']->dispatch($c['uri']->route());
if (!$page || !$page->routable()) {
$event = $c->fireEvent('onPageNotFound');
if (isset($event->page)) {
$page = $event->page;
} else {
throw new \RuntimeException('Page Not Found', 404);
}
}
return $page;
};
$container['output'] = function ($c) {
return $c['twig']->processSite($c['uri']->extension());
};
$container['browser'] = function ($c) {
return new Browser();
};
$container->register(new StreamsServiceProvider);
return $container;
}
public function process()
{
// Get the URI and URL (needed for configuration)
$this->uri = Registry::get('Uri');
// Use output buffering to prevent headers from being sent too early.
ob_start();
// Get the Configuration settings and caching
$this->config = Registry::get('Config');
// Initialize stream wrappers.
$this['locator'];
Debugger::$logDirectory = $this->config->get('system.debugger.log.enabled') ? LOG_DIR : null;
Debugger::$maxDepth = $this->config->get('system.debugger.max_depth');
$this['plugins']->init();
// Switch debugger into development mode if configured
if ($this->config->get('system.debugger.enabled')) {
if (function_exists('ini_set')) {
ini_set('display_errors', true);
}
Debugger::$productionMode = Debugger::DEVELOPMENT;
}
$this->fireEvent('onPluginsInitialized');
// Get the Caching setup
$this->cache = Registry::get('Cache');
$this->cache->init();
$this['assets']->init();
// Get Plugins
$plugins = new Plugins();
$this->plugins = $plugins->load();
$this->fireEvent('onAfterInitPlugins');
$this->fireEvent('onAssetsInitialized');
// Get current theme and hook it into plugins.
$themes = new Themes();
$this->plugins['Theme'] = $themes->load();
$this['twig']->init();
$this['pages']->init();
// Get twig object
$this->twig = Registry::get('Twig');
$this->twig->init();
$this->fireEvent('onPagesInitialized');
// Get all the Pages that Grav knows about
$this->pages = Registry::get('Pages');
$this->pages->init();
$this->fireEvent('onAfterGetPages');
// Get the taxonomy and set it on the grav object
$this->taxonomy = Registry::get('Taxonomy');
// Get current page
$this->page = $this->pages->dispatch($this->uri->route());
$this->fireEvent('onAfterGetPage');
// If there's no page, throw exception
if (!$this->page) {
throw new \RuntimeException('Page Not Found', 404);
}
$this->fireEvent('onPageInitialized');
// Process whole page as required
$this->output = $this->twig->processSite($this->uri->extension());
$this->fireEvent('onAfterGetOutput');
$this->output = $this['output'];
$this->fireEvent('onOutputGenerated');
// Set the header type
$this->header();
echo $this->output;
ob_end_flush();
flush();
$this->fireEvent('onOutputRendered');
}
/**
@@ -142,7 +157,9 @@ class Grav extends Getters
*/
public function redirect($route, $code = 303)
{
header("Location: " . rtrim($this->uri->rootUrl(), '/') .'/'. trim($route, '/'), true, $code);
/** @var Uri $uri */
$uri = $this['uri'];
header("Location: " . rtrim($uri->rootUrl(), '/') .'/'. trim($route, '/'), true, $code);
exit();
}
@@ -174,40 +191,22 @@ class Grav extends Getters
*/
public function header()
{
header('Content-type: ' . $this->mime($this->uri->extension()));
/** @var Uri $uri */
$uri = $this['uri'];
header('Content-type: ' . $this->mime($uri->extension()));
}
/**
* Log a message.
* Fires an event with optional parameters.
*
* @param string $message
* @param string $eventName
* @param Event $event
* @return Event
*/
protected static function log($message)
public function fireEvent($eventName, Event $event = null)
{
if (Debugger::$logDirectory) {
Debugger::log(sprintf($message, Debugger::timer() * 1000));
}
}
/**
* Processes any hooks and runs them.
*/
public function fireEvent()
{
$args = func_get_args();
$hook_id = array_shift($args);
$no_timing_hooks = array('onAfterPageProcessed','onAfterFolderProcessed', 'onAfterCollectionProcessed');
if (!empty($this->plugins)) {
foreach ($this->plugins as $plugin) {
if (is_callable(array($plugin, $hook_id))) {
call_user_func_array(array($plugin, $hook_id), $args);
}
}
}
if ($this->config && $this->config->get('system.debugger.log.timing') && !in_array($hook_id, $no_timing_hooks)) {
static::log($hook_id.': %f ms');
}
/** @var EventDispatcher $events */
$events = $this['events'];
return $events->dispatch($eventName, $event);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Grav\Common;
trait GravTrait
{
/**
* @var Grav
*/
protected static $grav;
/**
* @return Grav
*/
public function getGrav()
{
return self::$grav;
}
/**
* @param Grav $grav
*/
public static function setGrav(Grav $grav)
{
self::$grav = $grav;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Grav\Common\Markdown;
class Markdown extends \Parsedown
{
use MarkdownGravLinkTrait;
function __construct($page)
{
$this->page = $page;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Grav\Common\Markdown;
class MarkdownExtra extends \ParsedownExtra
{
use MarkdownGravLinkTrait;
function __construct($page)
{
parent::__construct();
$this->page = $page;
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Grav\Common\Markdown;
use Grav\Common\Debugger;
/**
* A trait to add some custom processing to the identifyLink() method in Parsedown and ParsedownExtra
*/
trait MarkdownGravLinkTrait
{
protected function identifyLink($Excerpt)
{
// Run the parent method to get the actual results
$Excerpt = parent::identifyLink($Excerpt);
$actions = array();
$command = '';
// if this is an image
if (isset($Excerpt['element']['attributes']['src'])) {
$alt = isset($Excerpt['element']['attributes']['alt']) ? $Excerpt['element']['attributes']['alt'] : '';
$title = isset($Excerpt['element']['attributes']['title']) ? $Excerpt['element']['attributes']['title'] : '';
//get the url and parse it
$url = parse_url(htmlspecialchars_decode($Excerpt['element']['attributes']['src']));
// if there is no host set but there is a path, the file is local
if (!isset($url['host']) && isset($url['path'])) {
// get the media objects for this page
$media = $this->page->media();
// if there is a media file that matches the path referenced..
if (isset($media->images()[$url['path']])) {
// get the medium object
$medium = $media->images()[$url['path']];
// if there is a query, then parse it and build action calls
if (isset($url['query'])) {
parse_str($url['query'], $actions);
}
// loop through actions for the image and call them
foreach ($actions as $action => $params) {
// as long as it's not an html, url or ligtbox action
if (!in_array($action, ['html','url','lightbox'])) {
call_user_func_array(array(&$medium, $action), explode(',', $params));
}
}
// Get the URL for regular images, or an array of bits needed to put together
// the lightbox HTML
if (!isset($actions['lightbox'])) {
$src = $medium->url();
} else {
$src = $medium->lightboxRaw();
}
// set the src element with the new generated url
if (!isset($actions['lightbox']) && !is_array($src)) {
$Excerpt['element']['attributes']['src'] = $src;
} else {
// Create the custom lightbox element
$Element = array(
'name' => 'a',
'attributes' => array('rel' => $src['a_rel'], 'href' => $src['a_url']),
'handler' => 'element',
'text' => array(
'name' => 'img',
'attributes' => array('src' => $src['img_url'], 'alt' => $alt, 'title' => $title)
),
);
// Set the lightbox element on the Excerpt
$Excerpt['element'] = $Element;
}
}
}
}
return $Excerpt;
}
}

View File

@@ -1,7 +1,8 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Grav;
use Grav\Common\Iterator;
use Grav\Common\Registry;
/**
* Collection of Pages.
@@ -25,7 +26,7 @@ class Collection extends Iterator
parent::__construct($items);
$this->params = $params;
$this->pages = $pages ? $pages : Registry::get('Pages');
$this->pages = $pages ? $pages : Grav::instance()->offsetGet('pages');
}
public function params()
@@ -114,9 +115,10 @@ class Collection extends Iterator
}
/**
* Check to see if this item is the first in the collection
* Check to see if this item is the first in the collection.
*
* @param string $path
* @return boolean True if item is first
* @return boolean True if item is first.
*/
public function isFirst($path)
{
@@ -128,9 +130,10 @@ class Collection extends Iterator
}
/**
* Check to see if this item is the last in the collection
* Check to see if this item is the last in the collection.
*
* @param string $path
* @return boolean True if item is last
* @return boolean True if item is last.
*/
public function isLast($path)
{
@@ -142,9 +145,10 @@ class Collection extends Iterator
}
/**
* Gets the previous sibling based on current position
* Gets the previous sibling based on current position.
*
* @return Object the previous item
* @param string $path
* @return Page The previous item.
*/
public function prevSibling($path)
{
@@ -152,9 +156,10 @@ class Collection extends Iterator
}
/**
* Gets the next sibling based on current position
* Gets the next sibling based on current position.
*
* @return Object the next item
* @param string $path
* @return Page The next item.
*/
public function nextSibling($path)
{
@@ -162,9 +167,11 @@ class Collection extends Iterator
}
/**
* Returns the adjacent sibling based on a direction
* Returns the adjacent sibling based on a direction.
*
* @param string $path
* @param integer $direction either -1 or +1
* @return Object the sibling item
* @return Page The sibling item.
*/
public function adjacentSibling($path, $direction = 1)
{
@@ -177,11 +184,12 @@ class Collection extends Iterator
}
/**
* Returns the item in the current position
* @param String $path the path the item
* @return Object item in the array the the current position
* Returns the item in the current position.
*
* @param string $path the path the item
* @return Page Item in the array the the current position.
*/
public function currentPosition($path) {
return array_search($path,array_keys($this->items));
return array_search($path, array_keys($this->items));
}
}

View File

@@ -2,9 +2,9 @@
namespace Grav\Common\Page;
use Grav\Common\Getters;
use Grav\Common\Registry;
use Grav\Config;
use Symfony\Component\Yaml\Yaml;
use Grav\Common\Grav;
use Grav\Common\Config;
use Grav\Common\GravTrait;
/**
* Media is a holder object that contains references to the media of page. This object is created and
@@ -15,6 +15,8 @@ use Symfony\Component\Yaml\Yaml;
*/
class Media extends Getters
{
use GravTrait;
protected $gettersVariable = 'instances';
protected $path;
@@ -76,7 +78,7 @@ class Media extends Getters
$basename = implode('.', $parts);
/** @var Config $config */
$config = Registry::get('Config');
$config = self::$grav['config'];
// Check if medium type has been configured.
$params = $config->get("media.{$ext}");
@@ -85,6 +87,9 @@ class Media extends Getters
}
$filePath = $this->path . '/' . $filename;
// Add default settings for undefined variables.
$params += $config->get('media.defaults');
$params += array(
'type' => 'file',
'thumb' => 'media/thumb.png',

View File

@@ -1,11 +1,12 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Config;
use Grav\Common\Data\Blueprint;
use Grav\Common\Uri;
use Grav\Common\Data\Data;
use Grav\Common\Filesystem\File\Yaml;
use Grav\Common\Registry;
use Grav\Common\Grav;
use Grav\Common\GravTrait;
use Gregwar\Image\Image as ImageFile;
/**
@@ -34,6 +35,8 @@ use Gregwar\Image\Image as ImageFile;
*/
class Medium extends Data
{
use GravTrait;
/**
* @var string
*/
@@ -96,7 +99,8 @@ class Medium extends Data
*/
public function url()
{
$config = Registry::get('Config');
/** @var Config $config */
$config = self::$grav['config'];
if ($this->image) {
$output = $this->image->cacheFile($this->type, $this->quality);
@@ -172,7 +176,8 @@ class Medium extends Data
}
if ($this->linkTarget) {
$config = Registry::get('Config');
/** @var Config $config */
$config = self::$grav['config'];
$output = '<a href="' . $config->get('system.base_url_relative') . '/'. $this->linkTarget
. '"' . $this->linkAttributes. ' class="'. $class . '">' . $output . '</a>';
@@ -197,6 +202,15 @@ class Medium extends Data
return $this->link($width, $height);
}
public function lightboxRaw($width = null, $height = null)
{
$url = $this->url();
$this->link($width, $height);
$lightbox_url = self::$grav['config']->get('system.base_url_relative') . '/'. $this->linkTarget;
return array('a_url' => $lightbox_url, 'a_rel' => 'lightbox', 'img_url' => $url);
}
/**
* Return link HTML for the medium.
*

View File

@@ -1,18 +1,21 @@
<?php
namespace Grav\Common\Page;
use \Grav\Common\Registry;
use \Grav\Common\Config;
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 \Symfony\Component\Yaml\Yaml;
use Grav\Common\Config;
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\Common\Markdown\Markdown;
use Grav\Common\Markdown\MarkdownExtra;
use Grav\Component\EventDispatcher\Event;
use Symfony\Component\Yaml\Yaml;
/**
* The Page object, or "Page" object is the main powerhouse of Grav. It contains all the information
@@ -25,6 +28,8 @@ use \Symfony\Component\Yaml\Yaml;
*/
class Page
{
use GravTrait;
/**
* @var string Filename. Leave as null if page is folder.
*/
@@ -65,6 +70,7 @@ class Page
protected $modular_twig;
protected $process;
protected $summary_size;
protected $markdown_extra;
/**
* @var Page Unmodified (original) version of the page. Used for copying and moving the page.
@@ -84,7 +90,7 @@ class Page
public function __construct($array = array())
{
/** @var Config $config */
$config = Registry::get('Config');
$config = self::$grav['config'];
$this->routable = true;
$this->taxonomy = array();
@@ -196,6 +202,9 @@ class Page
if (isset($this->header->date)) {
$this->date = strtotime($this->header->date);
}
if (isset($this->header->markdown_extra)) {
$this->markdown_extra = (bool)$this->header->markdown_extra;
}
if (isset($this->header->taxonomy)) {
foreach ($this->header->taxonomy as $taxonomy => $taxitems) {
$this->taxonomy[$taxonomy] = (array)$taxitems;
@@ -209,7 +218,9 @@ class Page
$this->process[$process] = $status;
}
}
}
return $this->header;
}
@@ -231,7 +242,7 @@ class Page
// Return calculated summary based on setting in site config file
/** @var Config $config */
$config = Registry::get('Config');
$config = self::$grav['config'];
if (!$size && $config->get('site.summary.size')) {
$size = $config->get('site.summary.size');
}
@@ -269,9 +280,12 @@ class Page
// If no content, process it
if ($this->content === null) {
// Get media
$this->media();
// Load cached content
/** @var Cache $cache */
$cache = Registry::get('Cache');
$cache = self::$grav['cache'];
$cache_id = md5('page'.$this->id());
$content = $cache->fetch($cache_id);
@@ -297,7 +311,7 @@ class Page
// Do we need to process twig this time?
if ($update_cache || $process_twig) {
/** @var Twig $twig */
$twig = Registry::get('Twig');
$twig = self::$grav['twig'];
$content = $twig->processPage($this, $content);
}
}
@@ -316,7 +330,6 @@ class Page
$this->content = $content;
$this->media();
}
return $this->content;
@@ -465,7 +478,7 @@ class Page
public function blueprints()
{
/** @var Pages $pages */
$pages = Registry::get('Pages');
$pages = self::$grav['pages'];
return $pages->blueprints($this->template());
}
@@ -544,7 +557,7 @@ class Page
public function media($var = null)
{
/** @var Cache $cache */
$cache = Registry::get('Cache');
$cache = self::$grav['cache'];
if ($var) {
$this->media = $var;
@@ -762,7 +775,7 @@ class Page
public function url($include_host = false)
{
/** @var Uri $uri */
$uri = Registry::get('Uri');
$uri = self::$grav['uri'];
$rootUrl = $uri->rootUrl($include_host);
$url = $rootUrl.'/'.trim($this->route(), '/');
@@ -958,7 +971,7 @@ class Page
}
if (empty($this->max_count)) {
/** @var Config $config */
$config = Registry::get('Config');
$config = self::$grav['config'];
$this->max_count = (int) $config->get('system.pages.list.count');
}
return $this->max_count;
@@ -1029,11 +1042,13 @@ class Page
*/
public function parent(Page $var = null)
{
if ($var !== null) {
$this->parent = $var ? $var->path() : '';
if ($var) {
$this->parent = $var->path();
return $var;
}
/** @var Pages $pages */
$pages = Registry::get('Pages');
$pages = self::$grav['pages'];
return $pages->get($this->parent);
}
@@ -1046,7 +1061,7 @@ class Page
public function children()
{
/** @var Pages $pages */
$pages = Registry::get('Pages');
$pages = self::$grav['pages'];
return $pages->children($this->path());
}
@@ -1126,7 +1141,7 @@ class Page
public function isFirst()
{
/** @var Pages $pages */
$pages = Registry::get('Pages');
$pages = self::$grav['pages'];
$parent = $pages->get($this->parent);
if ($this->path() == array_values($parent->items)[0]) {
@@ -1144,7 +1159,7 @@ class Page
public function isLast()
{
/** @var Pages $pages */
$pages = Registry::get('Pages');
$pages = self::$grav['pages'];
$parent = $pages->get($this->parent);
if ($this->path() == array_values($parent->items)[count($parent->items)-1]) {
@@ -1183,7 +1198,7 @@ class Page
public function adjacentSibling($direction = 1)
{
/** @var Pages $pages */
$pages = Registry::get('Pages');
$pages = self::$grav['pages'];
$parent = $pages->get($this->parent);
$current = $this->slug();
@@ -1202,7 +1217,7 @@ class Page
public function active()
{
/** @var Uri $uri */
$uri = Registry::get('Uri');
$uri = self::$grav['uri'];
if ($this->url() == $uri->url()) {
return true;
}
@@ -1217,7 +1232,7 @@ class Page
*/
public function activeChild()
{
$uri = Registry::get('Uri');
$uri = self::$grav['uri'];
if (!$this->home() && (strpos($uri->url(), $this->url()) !== false)) {
return true;
}
@@ -1258,7 +1273,7 @@ class Page
public function find($url)
{
/** @var Pages $pages */
$pages = Registry::get('Pages');
$pages = self::$grav['pages'];
return $pages->dispatch($url);
}
@@ -1289,9 +1304,9 @@ class Page
// TODO: MOVE THIS INTO SOMEWHERE ELSE?
/** @var Uri $uri */
$uri = Registry::get('Uri');
$uri = self::$grav['uri'];
/** @var Config $config */
$config = Registry::get('Config');
$config = self::$grav['config'];
foreach ((array) $config->get('site.taxonomies') as $taxonomy) {
if ($uri->param($taxonomy)) {
@@ -1310,7 +1325,7 @@ class Page
}
}
$config->set('system.cache.enabled', false);
$config->set('system.cache.enabled', false); // TODO: Do we still need this?
}
}
// TODO: END OF MOVE
@@ -1323,10 +1338,10 @@ class Page
}
/** @var Grav $grav */
$grav = Registry::get('Grav');
$grav = self::$grav['grav'];
// New Custom event to handle things like pagination.
$grav->fireEvent('onAfterCollectionProcessed', $collection);
$grav->fireEvent('onCollectionProcessed', new Event(['collection' => $collection]));
$params = $collection->params();
@@ -1391,7 +1406,7 @@ class Page
// @taxonomy: { category: [ blog, featured ], level: 1 }
/** @var Taxonomy $taxonomy_map */
$taxonomy_map = Registry::get('Taxonomy');
$taxonomy_map = self::$grav['taxonomy'];
if (!empty($parts)) {
$params = [implode('.', $parts) => $params];
@@ -1506,7 +1521,15 @@ class Page
*/
protected function parseMarkdownContent($content)
{
$parsedown = new \Parsedown();
/** @var Config $config */
$config = self::$grav['config'];
// get the appropriate setting for markdown extra
if (isset($this->markdown_extra) ? $this->markdown_extra : $config->get('system.pages.markdown_extra')) {
$parsedown = new MarkdownExtra($this);
} else {
$parsedown = new Markdown($this);
}
$content = $parsedown->parse($content);
return $content;
}
@@ -1540,7 +1563,7 @@ class Page
// Do reordering.
if ($reorder && $this->order() != $this->_original->order()) {
/** @var Pages $pages */
$pages = Registry::get('Pages');
$pages = self::$grav['pages'];
$parent = $this->parent();

View File

@@ -1,17 +1,20 @@
<?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\Registry;
use \Grav\Common\Utils;
use \Grav\Common\Cache;
use \Grav\Common\Taxonomy;
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\EventDispatcher\Event;
/**
* GravPages is the class that is the entry point into the hierarchy of pages
*
* @author RocketTheme
* @license MIT
*/
class Pages
{
@@ -20,11 +23,6 @@ class Pages
*/
protected $grav;
/**
* @var Config
*/
protected $config;
/**
* @var array|Page[]
*/
@@ -55,14 +53,21 @@ class Pages
*/
protected $last_modified;
/**
* Constructor
*
* @params Grav $c
*/
public function __construct(Grav $c)
{
$this->grav = $c;
}
/**
* Class initialization. Must be called before using this class.
*/
public function init()
{
$this->grav = Registry::get('Grav');
$this->config = Registry::get('Config');
$this->buildPages();
}
@@ -221,7 +226,10 @@ class Pages
// If the page cannot be reached, look into site wide routes.
if (!$all && (!$page || !$page->routable())) {
$route = $this->config->get("site.routes.{$url}");
/** @var Config $config */
$config = $this->grav['config'];
$route = $config->get("site.routes.{$url}");
if ($route) {
$page = $this->dispatch($route, $all);
}
@@ -249,7 +257,10 @@ class Pages
public function blueprints($type)
{
if (!isset($this->blueprints)) {
$this->blueprints = new Data\Blueprints(THEMES_DIR . $this->config->get('system.pages.theme') . '/blueprints/');
/** @var Config $config */
$config = $this->grav['config'];
$this->blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/');
}
try {
@@ -259,9 +270,7 @@ class Pages
}
if (!$blueprint->initialized) {
/** @var Grav $grav */
$grav = Registry::get('Grav');
$grav->fireEvent('onCreateBlueprint', $blueprint);
$this->grav->fireEvent('onBlueprintCreated', new Event(['blueprint' => $blueprint]));
$blueprint->initialized = true;
}
@@ -305,8 +314,11 @@ class Pages
*/
static public function types()
{
$grav = Grav::instance();
/** @var Config $config */
$config = Registry::get('Config');
$config = $grav['config'];
$blueprints = new Data\Blueprints(THEMES_DIR . $config->get('system.pages.theme') . '/blueprints/');
return $blueprints->types();
@@ -319,8 +331,11 @@ class Pages
*/
static public function parents()
{
$grav = Grav::instance();
/** @var Pages $pages */
$pages = Registry::get('Pages');
$pages = $grav['pages'];
return $pages->getList();
}
@@ -332,12 +347,16 @@ class Pages
protected function buildPages()
{
$this->sort = array();
if ($this->config->get('system.cache.enabled')) {
/** @var Config $config */
$config = $this->grav['config'];
if ($config->get('system.cache.enabled')) {
/** @var Cache $cache */
$cache = Registry::get('Cache');
$cache = $this->grav['cache'];
/** @var Taxonomy $taxonomy */
$taxonomy = Registry::get('Taxonomy');
$last_modified = $this->config->get('system.cache.check.pages', true)
$taxonomy = $this->grav['taxonomy'];
$last_modified = $config->get('system.cache.check.pages', true)
? Folder::lastModified(PAGES_DIR) : 0;
$page_cache_id = md5(USER_DIR.$last_modified);
@@ -370,16 +389,18 @@ class Pages
* @throws \RuntimeException
* @internal
*/
protected function recurse($directory = PAGES_DIR, &$parent = null)
protected function recurse($directory = PAGES_DIR, Page &$parent = null)
{
$directory = rtrim($directory, DS);
$iterator = new \DirectoryIterator($directory);
$page = new Page;
$config = $this->grav['config'];
$page->path($directory);
$page->parent($parent);
$page->orderDir($this->config->get('system.pages.order.dir'));
$page->orderBy($this->config->get('system.pages.order.by'));
if ($parent) $page->parent($parent);
$page->orderDir($config->get('system.pages.order.dir'));
$page->orderBy($config->get('system.pages.order.by'));
// Add into instances
if (!isset($this->instances[$page->path()])) {
@@ -399,8 +420,8 @@ class Pages
$page->init($file);
if ($this->config->get('system.pages.events.page')) {
$this->grav->fireEvent('onAfterPageProcessed', $page);
if ($config->get('system.pages.events.page')) {
$this->grav->fireEvent('onPageProcessed', new Event(['page' => $page]));
}
} elseif ($file->isDir() && !$file->isDot()) {
@@ -426,8 +447,8 @@ class Pages
// set the last modified time on pages
$this->lastModified($file->getMTime());
if ($this->config->get('system.pages.events.page')) {
$this->grav->fireEvent('onAfterFolderProcessed', $page);
if ($config->get('system.pages.events.page')) {
$this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page]));
}
}
}
@@ -444,7 +465,7 @@ class Pages
protected function buildRoutes()
{
/** @var $taxonomy Taxonomy */
$taxonomy = Registry::get('Taxonomy');
$taxonomy = $this->grav['taxonomy'];
// Build routes and taxonomy map.
/** @var $page Page */
@@ -465,8 +486,11 @@ class Pages
}
}
/** @var Config $config */
$config = $this->grav['config'];
// Alias and set default route to home page.
$home = trim($this->config->get('system.home.alias'), '/');
$home = trim($config->get('system.home.alias'), '/');
if ($home && isset($this->routes['/' . $home])) {
$this->routes['/'] = $this->routes['/' . $home];
$this->get($this->routes['/' . $home])->route('/');
@@ -489,7 +513,7 @@ class Pages
$child = isset($this->instances[$key]) ? $this->instances[$key] : null;
if (!$child) {
throw new \RuntimeException("Page does not exist: {$key}");
throw new \RuntimeException("Page does not exist: {$key}");
}
switch ($order_by) {
@@ -515,8 +539,14 @@ class Pages
}
}
// Sort by the new list.
asort($list);
// handle special case when order_by is random
if ($order_by == 'random') {
$list = $this->array_shuffle($list);
} else {
// else just sort the list according to specified key
asort($list);
}
// Move manually ordered items into the beginning of the list. Order of the unlisted items does not change.
if (is_array($manual) && !empty($manual)) {
@@ -544,4 +574,17 @@ class Pages
$this->sort[$path][$order_by][$key] = $info;
}
}
// Shuffles and associative array
protected function array_shuffle($list) {
$keys = array_keys($list);
shuffle($keys);
$new = array();
foreach($keys as $key) {
$new[$key] = $list[$key];
}
return $new;
}
}

View File

@@ -1,26 +1,75 @@
<?php
namespace Grav\Common;
use Grav\Component\EventDispatcher\EventDispatcher;
use Grav\Component\EventDispatcher\EventSubscriberInterface;
/**
* The Plugin object just holds the id and path to a plugin.
*
* @author RocketTheme
* @license MIT
*/
class Plugin
class Plugin implements EventSubscriberInterface
{
/**
* @var Grav
*/
protected $grav;
/**
* @var Config
*/
public $config;
protected $config;
/**
* By default assign all methods as listeners using the default priority.
*
* @return array
*/
public static function getSubscribedEvents() {
$methods = get_class_methods(get_called_class());
$list = array();
foreach ($methods as $method) {
if (strpos($method, 'on') === 0) {
$list[$method] = [$method, 0];
}
}
return $list;
}
/**
* Constructor.
*
* @param Grav $grav
* @param Config $config
*/
public function __construct(Config $config)
public function __construct(Grav $grav, Config $config)
{
$this->grav = $grav;
$this->config = $config;
}
/**
* @param array $events
*/
protected function enable(array $events)
{
/** @var EventDispatcher $dispatcher */
$dispatcher = $this->grav['events'];
foreach ($events as $eventName => $params) {
if (is_string($params)) {
$dispatcher->addListener($eventName, array($this, $params));
} elseif (is_string($params[0])) {
$dispatcher->addListener($eventName, array($this, $params[0]), isset($params[1]) ? $params[1] : 0);
} else {
foreach ($params as $listener) {
$dispatcher->addListener($eventName, array($this, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
}
}
}
}
}

View File

@@ -2,6 +2,8 @@
namespace Grav\Common;
use Grav\Common\Filesystem\File;
use Grav\Component\EventDispatcher\EventDispatcher;
use Grav\Component\EventDispatcher\EventSubscriberInterface;
/**
* The Plugins object holds an array of all the plugin objects that
@@ -10,13 +12,13 @@ use Grav\Common\Filesystem\File;
* @author RocketTheme
* @license MIT
*/
class Plugins
class Plugins extends Iterator
{
/**
* @var array|Plugin[]
*/
protected $plugins;
protected $grav;
public function __construct(Grav $grav) {
$this->grav = $grav;
}
/**
* Recurses through the plugins directory creating Plugin objects for each plugin it finds.
@@ -24,20 +26,22 @@ class Plugins
* @return array|Plugin[] array of Plugin objects
* @throws \RuntimeException
*/
public function load()
public function init()
{
/** @var Config $config */
$config = Registry::get('Config');
$config = $this->grav['config'];
$plugins = (array) $config->get('plugins');
/** @var EventDispatcher $events */
$events = $this->grav['events'];
foreach ($plugins as $plugin => $data) {
if (empty($data['enabled'])) {
// Only load enabled plugins.
continue;
}
$folder = PLUGINS_DIR . $plugin;
$filePath = $folder . DS . $plugin . PLUGIN_EXT;
$filePath = $this->grav['locator']('plugin://' . $plugin . DS . $plugin . PLUGIN_EXT);
if (!is_file($filePath)) {
throw new \RuntimeException(sprintf("Plugin '%s' enabled but not found!", $filePath, $plugin));
}
@@ -50,16 +54,25 @@ class Plugins
throw new \RuntimeException(sprintf("Plugin '%s' class not found!", $plugin));
}
$this->plugins[$pluginClass] = new $pluginClass($config);
$instance = new $pluginClass($this->grav, $config);
if ($instance instanceof EventSubscriberInterface) {
$events->addSubscriber($instance);
}
}
return $this->plugins;
$instance = $this->grav['themes']->load();
$instance->configure();
if ($instance instanceof EventSubscriberInterface) {
$events->addSubscriber($instance);
}
return $this->items;
}
public function add($plugin)
{
if (is_object($plugin)) {
$this->plugins[get_class($plugin)] = $plugin;
$this->items[get_class($plugin)] = $plugin;
}
}
@@ -71,7 +84,7 @@ class Plugins
static public function all()
{
$list = array();
$iterator = new \DirectoryIterator(PLUGINS_DIR);
$iterator = new \DirectoryIterator('plugin://');
/** @var \DirectoryIterator $directory */
foreach ($iterator as $directory) {
@@ -90,16 +103,16 @@ class Plugins
static public function get($type)
{
$blueprints = new Data\Blueprints(PLUGINS_DIR . $type);
$blueprints = new Data\Blueprints('plugin://' . $type);
$blueprint = $blueprints->get('blueprints');
$blueprint->name = $type;
// Load default configuration.
$file = File\Yaml::instance(PLUGINS_DIR . "{$type}/{$type}" . YAML_EXT);
$file = File\Yaml::instance('plugin://' . "{$type}/{$type}" . YAML_EXT);
$obj = new Data\Data($file->content(), $blueprint);
// Override with user configuration.
$file = File\Yaml::instance(USER_DIR . "config/plugins/{$type}" . YAML_EXT);
$file = File\Yaml::instance('plugin://' . "config/plugins/{$type}" . YAML_EXT);
$obj->merge($file->content());
// Save configuration always to user/config.

View File

@@ -7,18 +7,10 @@ namespace Grav\Common;
*
* @author RocketTheme
* @license MIT
* @deprecated
*/
class Registry
{
/**
* @var array
*/
private $registry = array();
/**
* @var Registry
*/
private static $instance = null;
/**
* Return global instance.
@@ -27,11 +19,8 @@ class Registry
*/
public static function instance()
{
if (self::$instance === null) {
self::$instance = new Registry();
}
return self::$instance;
user_error(__METHOD__ . '()', E_USER_DEPRECATED);
return new Registry;
}
/**
@@ -43,11 +32,9 @@ class Registry
*/
public static function get($key)
{
if (!isset(self::$instance->registry[$key])) {
throw new \Exception("There is no entry for key " . $key);
}
return self::$instance->registry[$key];
user_error(__METHOD__ . '()', E_USER_DEPRECATED);
$instance = Grav::instance();
return $instance[strtolower($key)];
}
/**
@@ -73,11 +60,9 @@ class Registry
*/
public function store($key, $value)
{
if (isset($this->registry[$key])) {
throw new \Exception("There is already an entry for key " . $key);
}
$this->registry[$key] = $value;
user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED);
$instance = Grav::instance();
$instance[strtolower($key)] = $value;
}
/**
@@ -89,10 +74,8 @@ class Registry
*/
public function retrieve($key)
{
if (!isset($this->registry[$key])) {
throw new \Exception("There is no entry for key " . $key);
}
return $this->registry[$key];
user_error(__CLASS__ . '::' . __METHOD__ . '()', E_USER_DEPRECATED);
$instance = Grav::instance();
return $instance[strtolower($key)];
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Grav\Common\Service;
use Grav\Component\DI\ServiceProviderInterface;
use Grav\Component\Filesystem\ResourceLocator;
use Grav\Component\Filesystem\StreamWrapper\ReadOnlyStream;
use Grav\Component\Filesystem\StreamWrapper\Stream;
use Pimple\Container;
class StreamsServiceProvider implements ServiceProviderInterface
{
public function register(Container $container)
{
$self = $this;
$container['locator'] = function($c) use ($self) {
$locator = new ResourceLocator;
$self->init($c, $locator);
return $locator;
};
}
protected function init(Container $container, ResourceLocator $locator)
{
$schemes = $container['config']->get('streams.schemes');
if (!$schemes) {
return;
}
// Set locator to both streams.
Stream::setLocator($locator);
ReadOnlyStream::setLocator($locator);
$registered = stream_get_wrappers();
foreach ($schemes as $scheme => $config) {
if (isset($config['paths'])) {
$locator->addPath($scheme, '', $config['paths']);
}
if (isset($config['prefixes'])) {
foreach ($config['prefixes'] as $prefix => $paths) {
$locator->addPath($scheme, $prefix, $paths);
}
}
if (in_array($scheme, $registered)) {
stream_wrapper_unregister($scheme);
}
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
if ($type[0] != '\\') {
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
}
if (!stream_wrapper_register($scheme, $type)) {
throw new \InvalidArgumentException("Stream '{$type}' could not be initialized.");
}
}
}
}

View File

@@ -79,7 +79,7 @@ class Session implements \IteratorAggregate
$this->started = true;
return $this;
}
}
/**
* Get session ID

View File

@@ -26,13 +26,15 @@ use \Grav\Common\Page;
class Taxonomy
{
protected $taxonomy_map;
protected $grav;
/**
* Constructor that resets the map
*/
public function __construct()
public function __construct(Grav $grav)
{
$this->taxonomy_map = array();
$this->grav = $grav;
}
/**
@@ -48,7 +50,8 @@ class Taxonomy
$page_taxonomy = $page->taxonomy();
}
$config = Registry::get('Config');
/** @var Config $config */
$config = $this->grav['config'];
if ($config->get('site.taxonomies') && count($page_taxonomy) > 0) {
foreach ((array) $config->get('site.taxonomies') as $taxonomy) {
if (isset($page_taxonomy[$taxonomy])) {

View File

@@ -1,6 +1,64 @@
<?php
namespace Grav\Common;
class Theme
use Grav\Common\Filesystem\File\Yaml;
use Grav\Component\Filesystem\ResourceLocator;
class Theme extends Plugin
{
public $name;
/**
* Constructor.
*
* @param Grav $grav
* @param Config $config
* @param string $name
*/
public function __construct(Grav $grav, Config $config, $name)
{
$this->name = $name;
parent::__construct($grav, $config);
}
public function configure() {
$themeConfig = Yaml::instance(THEMES_DIR . "{$this->name}/{$this->name}.yaml")->content();
$this->config->merge(['themes' => [$this->name => $themeConfig]]);
/** @var ResourceLocator $locator */
$locator = $this->grav['locator'];
// TODO: move
$registered = stream_get_wrappers();
$schemes = $this->config->get(
"themes.{$this->name}.streams.scheme",
['theme' => ['paths' => ["user/themes/{$this->name}"]]]
);
foreach ($schemes as $scheme => $config) {
if (isset($config['paths'])) {
$locator->addPath($scheme, '', $config['paths']);
}
if (isset($config['prefixes'])) {
foreach ($config['prefixes'] as $prefix => $paths) {
$locator->addPath($scheme, $prefix, $paths);
}
}
if (in_array($scheme, $registered)) {
stream_wrapper_unregister($scheme);
}
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
if ($type[0] != '\\') {
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
}
if (!stream_wrapper_register($scheme, $type)) {
throw new \InvalidArgumentException("Stream '{$type}' could not be initialized.");
}
}
}
}

View File

@@ -11,12 +11,22 @@ use Grav\Common\Filesystem\File;
*/
class Themes
{
/**
* @var Grav
*/
protected $grav;
public function __construct(Grav $grav)
{
$this->grav = $grav;
}
/**
* Return list of all theme data with their blueprints.
*
* @return array|Data\Data[]
*/
static public function all()
public function all()
{
$list = array();
$iterator = new \DirectoryIterator(THEMES_DIR);
@@ -43,7 +53,7 @@ class Themes
* @return Data\Data
* @throws \RuntimeException
*/
static public function get($type)
public function get($type)
{
if (!$type) {
throw new \RuntimeException('Theme name not provided.');
@@ -76,8 +86,10 @@ class Themes
public function load($name = null)
{
/** @var Config $config */
$config = $this->grav['config'];
if (!$name) {
$config = Registry::get('Config');
$name = $config->get('system.pages.theme');
}
@@ -89,13 +101,13 @@ class Themes
$className = '\\Grav\\Theme\\' . ucfirst($name);
if (class_exists($className)) {
$class = new $className;
$class = new $className($this->grav, $config, $name);
}
}
}
if (empty($class)) {
$class = new Theme;
$class = new Theme($this->grav, $config, $name);
}
return $class;

View File

@@ -16,38 +16,28 @@ class Twig
/**
* @var \Twig_Environment
*/
protected $twig;
/**
* @var Grav
*/
protected $grav;
/**
* @var Config
*/
protected $config;
/**
* @var Uri
*/
protected $uri;
/**
* @var Taxonomy
*/
protected $taxonomy;
public $twig;
/**
* @var array
*/
public $twig_vars;
/**
* @var array
*/
public $twig_paths;
/**
* @var string
*/
public $template;
/**
* @var Grav
*/
protected $grav;
/**
* @var \Twig_Loader_Filesystem
*/
@@ -58,6 +48,15 @@ class Twig
*/
protected $loaderArray;
/**
* Constructor
*/
public function __construct(Grav $grav)
{
$this->grav = $grav;
}
/**
* Twig initialization that sets the twig loader chain, then the environment, then extensions
* and also the base set of twig vars
@@ -65,58 +64,54 @@ class Twig
public function init()
{
if (!isset($this->twig)) {
/** @var Config $config */
$config = $this->grav['config'];
// get Grav and Config
$this->grav = Registry::get('Grav');
$this->config = $this->grav->config;
$this->uri = Registry::get('Uri');
$this->taxonomy = Registry::get('Taxonomy');
$this->twig_paths = array(THEMES_DIR . $this->config->get('system.pages.theme') . '/templates');
$this->grav->fireEvent('onAfterTwigTemplatesPaths');
$this->twig_paths = array(THEMES_DIR . $config->get('system.pages.theme') . '/templates');
$this->grav->fireEvent('onTwigTemplatePaths');
$this->loader = new \Twig_Loader_Filesystem($this->twig_paths);
$this->loaderArray = new \Twig_Loader_Array(array());
$loader_chain = new \Twig_Loader_Chain(array($this->loaderArray, $this->loader));
$params = $this->config->get('system.twig');
$params = $config->get('system.twig');
if (!empty($params['cache'])) {
$params['cache'] = CACHE_DIR;
}
$this->twig = new \Twig_Environment($loader_chain, $params);
$this->grav->fireEvent('onAfterTwigInit');
$this->grav->fireEvent('onTwigInitialized');
// set default date format if set in config
if ($this->config->get('system.pages.dateformat.long')) {
$this->twig->getExtension('core')->setDateFormat($this->config->get('system.pages.dateformat.long'));
if ($config->get('system.pages.dateformat.long')) {
$this->twig->getExtension('core')->setDateFormat($config->get('system.pages.dateformat.long'));
}
// enable the debug extension if required
if ($this->config->get('system.twig.debug')) {
if ($config->get('system.twig.debug')) {
$this->twig->addExtension(new \Twig_Extension_Debug());
}
$this->twig->addExtension(new TwigExtension());
$this->grav->fireEvent('onAfterTwigExtensions');
$this->grav->fireEvent('onTwigExtensions');
$baseUrlAbsolute = $this->config->get('system.base_url_absolute');
$baseUrlRelative = $this->config->get('system.base_url_relative');
$theme = $this->config->get('system.pages.theme');
$baseUrlAbsolute = $config->get('system.base_url_absolute');
$baseUrlRelative = $config->get('system.base_url_relative');
$theme = $config->get('system.pages.theme');
$themeUrl = $baseUrlRelative .'/'. USER_PATH . basename(THEMES_DIR) .'/'. $theme;
// Set some standard variables for twig
$this->twig_vars = array(
'config' => $this->config,
'uri' => $this->uri,
'grav_version' => GRAV_VERSION,
'config' => $config,
'uri' => $this->grav['uri'],
'base_dir' => rtrim(ROOT_DIR, '/'),
'base_url_absolute' => $baseUrlAbsolute,
'base_url_relative' => $baseUrlRelative,
'theme_dir' => THEMES_DIR . $theme,
'theme_url' => $themeUrl,
'site' => $this->config->get('site'),
'stylesheets' => array(),
'scripts' => array(),
'taxonomy' => $this->taxonomy,
'site' => $config->get('site'),
'assets' => $this->grav['assets'],
'taxonomy' => $this->grav['taxonomy'],
'browser' => $this->grav['browser'],
);
}
@@ -161,11 +156,10 @@ class Twig
*/
public function processPage(Page $item, $content = null)
{
$this->init();
$content = $content !== null ? $content : $item->content();
// override the twig header vars for local resolution
$this->grav->fireEvent('onAfterTwigPageVars');
$this->grav->fireEvent('onTwigPageVariables');
$twig_vars = $this->twig_vars;
$twig_vars['page'] = $item;
@@ -194,10 +188,8 @@ class Twig
*/
public function processString($string, array $vars = array())
{
$this->init();
// override the twig header vars for local resolution
$this->grav->fireEvent('onAfterTwigVars');
$this->grav->fireEvent('onTwigStringVariables');
$vars += $this->twig_vars;
$name = '@Var:' . $string;
@@ -213,17 +205,15 @@ class Twig
*
* @param string $format Output format (defaults to HTML).
* @return string the rendered output
* @throws \Twig_Error_Loader
* @throws \RuntimeException
*/
public function processSite($format = null)
{
$this->init();
// set the page now its been processed
$this->grav->fireEvent('onAfterTwigSiteVars');
$this->grav->fireEvent('onTwigSiteVariables');
$twig_vars = $this->twig_vars;
$pages = $this->grav->pages;
$page = $this->grav->page;
$pages = $this->grav['pages'];
$page = $this->grav['page'];
$twig_vars['pages'] = $pages->root();
$twig_vars['page'] = $page;
@@ -233,7 +223,12 @@ class Twig
// Get Twig template layout
$template = $this->template($page->template() . $ext);
$output = $this->twig->render($template, $twig_vars);
try {
$output = $this->twig->render($template, $twig_vars);
} catch (\Twig_Error_Loader $e) {
throw new \RuntimeException('Resource not found.', 404, $e);
}
return $output;
}

View File

@@ -1,6 +1,12 @@
<?php
namespace Grav\Common;
/**
* The Twig extension adds some filters and functions that are useful for Grav
*
* @author RocketTheme
* @license MIT
*/
class TwigExtension extends \Twig_Extension
{
/**

View File

@@ -1,6 +1,12 @@
<?php
namespace Grav\Common;
/**
* The URI object provides information about the current URL
*
* @author RocketTheme
* @license MIT
*/
class Uri
{
protected $base;
@@ -20,21 +26,29 @@ class Uri
*/
public function __construct()
{
$base = 'http://';
$uri = $_SERVER["REQUEST_URI"];
if (isset($_SERVER["HTTPS"])) {
$base = (@$_SERVER["HTTPS"] == "on") ? "https://" : "http://";
$base = 'http://';
$uri = $_SERVER['REQUEST_URI'];
$root_path = rtrim(substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], 'index.php')), '/');
if (isset($_SERVER['HTTPS'])) {
$base = (@$_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
}
$base .= $_SERVER["SERVER_NAME"];
$base .= $_SERVER['SERVER_NAME'];
if ($_SERVER["SERVER_PORT"] != "80" && $_SERVER["SERVER_PORT"] != "443") {
$base .= ":".$_SERVER["SERVER_PORT"];
if ($_SERVER['SERVER_PORT'] != '80' && $_SERVER['SERVER_PORT'] != '443') {
$base .= ":".$_SERVER['SERVER_PORT'];
}
// check if userdir in the path and workaround PHP bug with PHP_SELF
if (strpos($_SERVER['REQUEST_URI'], '/~') !== false && strpos($_SERVER['PHP_SELF'], '/~') === false) {
$root_path = substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '/', 1)) . $root_path;
}
$this->base = $base;
$this->root = $base . rtrim(substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], 'index.php')), '/');
$this->root = $base . $root_path;
$this->url = $base . $uri;
$this->init();
@@ -57,7 +71,7 @@ class Uri
if (strpos($bit, ':') !== false) {
$param = explode(':', $bit);
if (count($param) == 2) {
$this->params[$param[0]] = str_replace('%7C', '/', $param[1]);
$this->params[$param[0]] = str_replace('%7C', '/', filter_var($param[1], FILTER_SANITIZE_STRING));
}
} else {
$path[] = $bit;
@@ -128,7 +142,7 @@ class Uri
public function query($id = null)
{
if (isset($id)) {
return $this->query[$id];
return filter_var($this->query[$id], FILTER_SANITIZE_STRING) ;
} else {
return http_build_query($this->query);
}

View File

@@ -15,7 +15,7 @@ abstract class Authentication
* @param string $password Plaintext password.
* @return string|bool
*/
static public function create($password)
public static function create($password)
{
return password_hash($password, PASSWORD_DEFAULT);
}
@@ -27,7 +27,7 @@ abstract class Authentication
* @param string $hash Hash to verify against.
* @return int Returns 0 if the check fails, 1 if password matches, 2 if hash needs to be updated.
*/
static public function verify($password, $hash)
public static function verify($password, $hash)
{
// Always accept plaintext passwords (needs an update).
// FIXME: not safe to do this...

View File

@@ -51,6 +51,7 @@ abstract class Utils
* @return string
*/
public static function truncateHtml($text, $length = 100, $ending = '...', $exact = false, $considerHtml = true) {
$open_tags = array();
if ($considerHtml) {
// if the plain text is shorter than the maximum length, return the whole text
if (strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
@@ -59,7 +60,6 @@ abstract class Utils
// splits all html-tags to scanable lines
preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
$total_length = strlen($ending);
$open_tags = array();
$truncate = '';
foreach ($lines as $line_matchings) {
// if there is any html-tag in this line, handle it and add it (uncounted) to the output
@@ -72,7 +72,7 @@ abstract class Utils
// delete tag from $open_tags list
$pos = array_search($tag_matchings[1], $open_tags);
if ($pos !== false) {
unset($open_tags[$pos]);
unset($open_tags[$pos]);
}
// if tag is an opening tag
} else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
@@ -102,14 +102,14 @@ abstract class Utils
}
}
$truncate .= substr($line_matchings[2], 0, $left+$entities_length);
// maximum lenght is reached, so get off the loop
// maximum length is reached, so get off the loop
break;
} else {
$truncate .= $line_matchings[2];
$total_length += $content_length;
}
// if the maximum length is reached, get off the loop
if($total_length>= $length) {
if ($total_length >= $length) {
break;
}
}
@@ -131,7 +131,7 @@ abstract class Utils
}
// add the defined ending to the text
$truncate .= $ending;
if($considerHtml) {
if ($considerHtml) {
// close all unclosed html-tags
foreach ($open_tags as $tag) {
$truncate .= '</' . $tag . '>';

View File

@@ -0,0 +1,46 @@
<?php
namespace Grav\Component\ArrayTraits;
/**
* Class ArrayAccess
* @package Grav\Component\ArrayTraits
*
* @property array $items
*/
trait ArrayAccess
{
/**
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset)
{
return isset($this->items[$offset]);
}
/**
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset)
{
return isset($this->items[$offset]) ? $this->items[$offset] : null;
}
/**
* @param mixed $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
$this->items[$offset] = $value;
}
/**
* @param mixed $offset
*/
public function offsetUnset($offset)
{
unset($this->items[$offset]);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Grav\Component\ArrayTraits;
/**
* Class Constructor
* @package Grav\Component\ArrayTraits
*
* @property array $items
*/
trait Constructor
{
/**
* Constructor.
*
* @param array $items Initial items inside the iterator.
*/
public function __construct(array $items = array())
{
$this->items = $items;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Grav\Component\ArrayTraits;
/**
* Class Countable
* @package Grav\Component\ArrayTraits
*
* @property array $items;
*/
trait Countable
{
/**
* @return int
*/
public function count()
{
count($this->items);
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace Grav\Component\ArrayTraits;
trait Getters
{
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);
}
}

View File

@@ -0,0 +1,6 @@
<?php
namespace Grav\Component\DI;
class Container extends \Pimple\Container
{
}

View File

@@ -0,0 +1,6 @@
<?php
namespace Grav\Component\DI;
interface ServiceProviderInterface extends \Pimple\ServiceProviderInterface
{
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Grav\Component\EventDispatcher;
use Grav\Component\ArrayTraits\ArrayAccess;
use Grav\Component\ArrayTraits\Constructor;
class Event extends \Symfony\Component\EventDispatcher\Event implements \ArrayAccess
{
use ArrayAccess, Constructor;
/**
* @var array
*/
protected $items = array();
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Grav\Component\EventDispatcher;
use \Symfony\Component\EventDispatcher\Event as BaseEvent;
class EventDispatcher extends \Symfony\Component\EventDispatcher\EventDispatcher
{
public function dispatch($eventName, BaseEvent $event = null)
{
if (null === $event) {
$event = new Event();
}
return parent::dispatch($eventName, $event);
}
}

View File

@@ -0,0 +1,6 @@
<?php
namespace Grav\Component\EventDispatcher;
interface EventSubscriberInterface extends \Symfony\Component\EventDispatcher\EventSubscriberInterface
{
}

View File

@@ -0,0 +1,114 @@
<?php
namespace Grav\Component\Filesystem;
/**
* Implements Uniform Resource Location
*
* @link http://webmozarts.com/2013/06/19/the-power-of-uniform-resource-location-in-php/
*/
class ResourceLocator
{
/**
* @var array
*/
protected $schemes = [];
/**
* @param string $scheme
* @param string $prefix
* @param string|array $paths
*/
public function addPath($scheme, $prefix, $paths)
{
$list = [];
foreach((array) $paths as $path) {
$list[] = trim($path, '/');
}
if (isset($this->schemes[$scheme][$prefix])) {
$list = array_merge($list, $this->schemes[$scheme][$prefix]);
}
$this->schemes[$scheme][$prefix] = $list;
// Sort in reverse order to get longer prefixes to be matched first.
krsort($this->schemes[$scheme]);
}
/**
* @param $uri
* @return string|bool
*/
public function __invoke($uri)
{
return $this->find($uri, false, true);
}
/**
* @param string $uri
* @param bool $absolute
* @return string|bool
*/
public function findResource($uri, $absolute = true)
{
return $this->find($uri, false, $absolute);
}
/**
* @param string $uri
* @param bool $absolute
* @return array
*/
public function findResources($uri, $absolute = true)
{
return $this->find($uri, true, $absolute);
}
/**
* @param string $uri
* @param bool $absolute
* @param bool $array
*
* @throws \InvalidArgumentException
* @return array|string|bool
*/
protected function find($uri, $array, $absolute)
{
$segments = explode('://', $uri, 2);
$file = array_pop($segments);
$scheme = array_pop($segments);
if (!$scheme) {
$scheme = 'file';
}
if (!$file || $uri[0] == ':') {
throw new \InvalidArgumentException('Invalid resource URI');
}
if (!isset($this->schemes[$scheme])) {
throw new \InvalidArgumentException("Invalid resource {$scheme}://");
}
$paths = $array ? [] : false;
foreach ($this->schemes[$scheme] as $prefix => $paths) {
if ($prefix && strpos($file, $prefix) !== 0) {
continue;
}
foreach ($paths as $path) {
$filename = $path . '/' . ltrim(substr($file, strlen($prefix)), '\/');
$lookup = ROOT_DIR . '/' . $filename;
if (file_exists($lookup)) {
if (!$array) {
return $absolute ? $lookup : $filename;
}
$paths[] = $absolute ? $lookup : $filename;
}
}
}
return $paths;
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace Grav\Component\Filesystem\StreamWrapper;
use Grav\Component\Filesystem\ResourceLocator;
class ReadOnlyStream extends Stream implements StreamInterface
{
/**
* @var ResourceLocator
*/
protected static $locator;
public function stream_open($uri, $mode, $options, &$opened_url)
{
if (!in_array($mode, ['r', 'rb', 'rt'])) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error('stream_open() write modes not supported for read-only stream wrappers', E_USER_WARNING);
}
return false;
}
$path = $this->getPath($uri);
if (!$path) {
return false;
}
$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])) {
trigger_error(
'stream_lock() exclusive lock operations not supported for read-only stream wrappers',
E_USER_WARNING
);
return false;
}
return flock($this->handle, $operation);
}
public function stream_write($data)
{
throw new \BadMethodCallException('stream_write() not supported for read-only stream wrappers');
}
public function unlink($uri)
{
throw new \BadMethodCallException('unlink() not supported for read-only stream wrappers');
}
public function rename($from_uri, $to_uri)
{
throw new \BadMethodCallException('rename() not supported for read-only stream wrappers');
}
public function mkdir($uri, $mode, $options)
{
throw new \BadMethodCallException('mkdir() not supported for read-only stream wrappers');
}
public function rmdir($uri, $options)
{
throw new \BadMethodCallException('rmdir() not supported for read-only stream wrappers');
}
}

View File

@@ -0,0 +1,232 @@
<?php
namespace Grav\Component\Filesystem\StreamWrapper;
use Grav\Component\Filesystem\ResourceLocator;
class Stream implements StreamInterface
{
/**
* A generic resource handle.
*
* @var Resource
*/
protected $handle = null;
/**
* @var ResourceLocator
*/
protected static $locator;
/**
* @param ResourceLocator $locator
*/
public static function setLocator(ResourceLocator $locator)
{
static::$locator = $locator;
}
public function stream_open($uri, $mode, $options, &$opened_url)
{
$path = $this->getPath($uri, $mode);
if (!$path) {
return false;
}
$this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode);
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])) {
return flock($this->handle, $operation);
}
return false;
}
public function stream_metadata($uri, $option, $value)
{
switch ($option) {
case STREAM_META_TOUCH:
list ($time, $atime) = $value;
return touch($uri, $time, $atime);
case STREAM_META_OWNER_NAME:
case STREAM_META_OWNER:
return chown($uri, $value);
case STREAM_META_GROUP_NAME:
case STREAM_META_GROUP:
return chgrp($uri, $value);
case STREAM_META_ACCESS:
return chmod($uri, $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);
}
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);
if (!($fromPath && $toPath)) {
return false;
}
return rename($fromPath, $toPath);
}
public function mkdir($uri, $mode, $options)
{
$recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
$path = $this->getPath($uri, $recursive ? $mode : null);
if (!$path) {
return false;
}
return ($options & STREAM_REPORT_ERRORS) ? mkdir($path, $mode, $recursive) : @mkdir($path, $mode, $recursive);
}
public function rmdir($uri, $options)
{
$path = $this->getPath($uri);
if (!$path) {
return false;
}
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 PHP's 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->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)
{
$path = $this->findPath($uri);
if ($mode == null || !$path || file_exists($path)) {
return $path;
}
if ($mode[0] == 'r') {
return false;
}
// We are either opening a file or creating directory.
list($scheme, $target) = explode('://', $uri, 2);
$path = $this->findPath($scheme . '://' . dirname($target));
if (!$path) {
return false;
}
return $path . '/' . basename($uri);
}
protected function findPath($uri)
{
return static::$locator ? static::$locator->findResource($uri) : false;
}
}

View File

@@ -0,0 +1,251 @@
<?php
namespace Grav\Component\Filesystem\StreamWrapper;
/**
* Generic PHP stream wrapper interface.
*
* @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 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();
}

View File

@@ -26,6 +26,13 @@ class CleanCommand extends Command {
'user/plugins/email/vendor/swiftmailer/swiftmailer/notes',
'user/plugins/email/vendor/swiftmailer/swiftmailer/doc',
'user/themes/antimatter/.sass-cache',
'vendor/donatj/phpuseragentparser/.git',
'vendor/donatj/phpuseragentparser/.gitignore',
'vendor/donatj/phpuseragentparser/.travis.yml',
'vendor/donatj/phpuseragentparser/composer.json',
'vendor/donatj/phpuseragentparser/phpunit.xml.dist',
'vendor/donatj/phpuseragentparser/Tests',
'vendor/donatj/phpuseragentparser/Tools',
'vendor/doctrine/cache/.travis.yml',
'vendor/doctrine/cache/build.properties',
'vendor/doctrine/cache/build.xml',
@@ -40,6 +47,8 @@ class CleanCommand extends Command {
'vendor/erusev/parsedown/.travis.yml',
'vendor/erusev/parsedown/.git',
'vendor/erusev/parsedown/test',
'vendor/erusev/parsedown-extra/composer.json',
'vendor/erusev/parsedown-extra/.git',
'vendor/gregwar/image/Gregwar/Image/composer.json',
'vendor/gregwar/image/Gregwar/Image/phpunit.xml',
'vendor/gregwar/image/Gregwar/Image/.gitignore',
@@ -57,11 +66,37 @@ class CleanCommand extends Command {
'vendor/ircmaxell/password-compat/version-test.php',
'vendor/ircmaxell/password-compat/.travis.yml',
'vendor/ircmaxell/password-compat/test',
'vendor/mrclay/minify/.editorconfig',
'vendor/mrclay/minify/.git',
'vendor/mrclay/minify/.gitignore',
'vendor/mrclay/minify/composer.json',
'vendor/mrclay/minify/min_extras',
'vendor/mrclay/minify/min_unit_tests',
'vendor/mrclay/minify/min/.htaccess',
'vendor/mrclay/minify/min/builder',
'vendor/mrclay/minify/min/config-test.php',
'vendor/mrclay/minify/min/config.php',
'vendor/mrclay/minify/min/groupsConfig.php',
'vendor/mrclay/minify/min/index.php',
'vendor/mrclay/minify/min/quick-test.css',
'vendor/mrclay/minify/min/quick-test.js',
'vendor/mrclay/minify/min/utils.php',
'vendor/pimple/pimple/.gitignore',
'vendor/pimple/pimple/.travis.yml',
'vendor/pimple/pimple/composer.json',
'vendor/pimple/pimple/ext',
'vendor/pimple/pimple/phpunit.xml.dist',
'vendor/pimple/pimple/src/Pimple/Tests',
'vendor/symfony/console/Symfony/Component/Console/composer.json',
'vendor/symfony/console/Symfony/Component/Console/phpunit.xml.dist',
'vendor/symfony/console/Symfony/Component/Console/.gitignore',
'vendor/symfony/console/Symfony/Component/Console/.git',
'vendor/symfony/console/Symfony/Component/Console/Tests',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.git',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist',
'vendor/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests',
'vendor/symfony/yaml/Symfony/Component/Yaml/composer.json',
'vendor/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist',
'vendor/symfony/yaml/Symfony/Component/Yaml/.gitignore',

View File

@@ -13,7 +13,8 @@ class ClearCacheCommand extends Command {
protected $paths_to_remove = [
'cache',
'images'
'images',
'assets'
];
protected function configure() {

View File

@@ -8,16 +8,12 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
//Use the Composer classes
// use Composer\Console\Application;
// use Composer\Command\UpdateCommand;
// use Symfony\Component\Console\Input\ArrayInput;
class SetupCommand extends Command
{
protected $directories = array('/cache',
'/logs',
'/images',
'/assets',
'/user/accounts',
'/user/config',
'/user/pages',
@@ -86,7 +82,6 @@ EOT
// Copy the Core STuff
} else {
$options = true;
// Create Some core stuff if it doesn't exist
$this->createDirectories($output);
@@ -156,8 +151,13 @@ EOT
$to = $this->destination . $target;
$output->writeln(' <cyan>' . $source . '</cyan> <comment>-></comment> ' . $to);
@unlink ($to);
symlink ($from, $to);
if (is_dir($to)) {
$this->rmdir($to);
} else {
@unlink($to);
}
symlink($from, $to);
}
}
@@ -277,4 +277,21 @@ EOT
}
}
}
private function rmdir($dir) {
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $fileinfo) {
if ($fileinfo->isDir()) {
if (false === rmdir($fileinfo->getRealPath())) return false;
} else {
if (false === unlink($fileinfo->getRealPath())) return false;
}
}
return rmdir($dir);
}
}

View File

@@ -3,12 +3,10 @@ home:
pages:
theme: antimatter
markdown_extra: false
process:
markdown: true
twig: false
events:
page: false
twig: true
cache:
enabled: true
@@ -23,8 +21,16 @@ twig:
auto_reload: true
autoescape: false
assets:
css_pipeline: false
css_minify: true
css_rewrite: true
js_pipeline: false
js_minify: true
debugger:
enabled: true
strict: false
max_depth: 10
log:
enabled: false

2
vendor/autoload.php vendored
View File

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

View File

@@ -6,6 +6,236 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'CSSmin' => $vendorDir . '/mrclay/minify/min/lib/CSSmin.php',
'Doctrine\\Common\\Cache\\ApcCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php',
'Doctrine\\Common\\Cache\\ArrayCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php',
'Doctrine\\Common\\Cache\\Cache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php',
'Doctrine\\Common\\Cache\\CacheProvider' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php',
'Doctrine\\Common\\Cache\\CouchbaseCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php',
'Doctrine\\Common\\Cache\\FileCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php',
'Doctrine\\Common\\Cache\\FilesystemCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php',
'Doctrine\\Common\\Cache\\MemcacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php',
'Doctrine\\Common\\Cache\\MemcachedCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php',
'Doctrine\\Common\\Cache\\MongoDBCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php',
'Doctrine\\Common\\Cache\\PhpFileCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php',
'Doctrine\\Common\\Cache\\RedisCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php',
'Doctrine\\Common\\Cache\\RiakCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php',
'Doctrine\\Common\\Cache\\Version' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Version.php',
'Doctrine\\Common\\Cache\\WinCacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php',
'Doctrine\\Common\\Cache\\XcacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php',
'Doctrine\\Common\\Cache\\ZendDataCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php',
'DooDigestAuth' => $vendorDir . '/mrclay/minify/min/lib/DooDigestAuth.php',
'FirePHP' => $vendorDir . '/mrclay/minify/min/lib/FirePHP.php',
'Grav\\Common\\Assets' => $baseDir . '/system/src/Grav/Common/Assets.php',
'Grav\\Common\\Browser' => $baseDir . '/system/src/Grav/Browser.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\\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\\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',
'Grav\\Common\\Inflector' => $baseDir . '/system/src/Grav/Common/Inflector.php',
'Grav\\Common\\Iterator' => $baseDir . '/system/src/Grav/Common/Iterator.php',
'Grav\\Common\\Markdown\\Markdown' => $baseDir . '/system/src/Grav/Common/Markdown/Markdown.php',
'Grav\\Common\\Markdown\\MarkdownExtra' => $baseDir . '/system/src/Grav/Common/Markdown/MarkdownExtra.php',
'Grav\\Common\\Markdown\\MarkdownGravLinkTrait' => $baseDir . '/system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php',
'Grav\\Common\\Page\\Collection' => $baseDir . '/system/src/Grav/Common/Page/Collection.php',
'Grav\\Common\\Page\\Media' => $baseDir . '/system/src/Grav/Common/Page/Media.php',
'Grav\\Common\\Page\\Medium' => $baseDir . '/system/src/Grav/Common/Page/Medium.php',
'Grav\\Common\\Page\\Page' => $baseDir . '/system/src/Grav/Common/Page/Page.php',
'Grav\\Common\\Page\\Pages' => $baseDir . '/system/src/Grav/Common/Page/Pages.php',
'Grav\\Common\\Plugin' => $baseDir . '/system/src/Grav/Common/Plugin.php',
'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',
'Grav\\Common\\Twig' => $baseDir . '/system/src/Grav/Common/Twig.php',
'Grav\\Common\\TwigExtension' => $baseDir . '/system/src/Grav/Common/TwigExtension.php',
'Grav\\Common\\Uri' => $baseDir . '/system/src/Grav/Common/Uri.php',
'Grav\\Common\\User\\Authentication' => $baseDir . '/system/src/Grav/Common/User/Authentication.php',
'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\\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\\DI\\Container' => $baseDir . '/system/src/Grav/Component/DI/Container.php',
'Grav\\Component\\DI\\ServiceProviderInterface' => $baseDir . '/system/src/Grav/Component/DI/ServiceProviderInterface.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\\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\\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',
'Grav\\Console\\InstallCommand' => $baseDir . '/system/src/Grav/Console/InstallCommand.php',
'Grav\\Console\\NewProjectCommand' => $baseDir . '/system/src/Grav/Console/NewProjectCommand.php',
'Grav\\Console\\SetupCommand' => $baseDir . '/system/src/Grav/Console/SetupCommand.php',
'Gregwar\\Cache\\Cache' => $vendorDir . '/gregwar/cache/Gregwar/Cache/Cache.php',
'Gregwar\\Cache\\GarbageCollect' => $vendorDir . '/gregwar/cache/Gregwar/Cache/GarbageCollect.php',
'Gregwar\\Image\\Adapter\\Adapter' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/Adapter.php',
'Gregwar\\Image\\Adapter\\AdapterInterface' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/AdapterInterface.php',
'Gregwar\\Image\\Adapter\\Common' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/Common.php',
'Gregwar\\Image\\Adapter\\GD' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/GD.php',
'Gregwar\\Image\\Adapter\\Imagick' => $vendorDir . '/gregwar/image/Gregwar/Image/Adapter/Imagick.php',
'Gregwar\\Image\\Exceptions\\GenerationError' => $vendorDir . '/gregwar/image/Gregwar/Image/Exceptions/GenerationError.php',
'Gregwar\\Image\\GarbageCollect' => $vendorDir . '/gregwar/image/Gregwar/Image/GarbageCollect.php',
'Gregwar\\Image\\Image' => $vendorDir . '/gregwar/image/Gregwar/Image/Image.php',
'Gregwar\\Image\\ImageColor' => $vendorDir . '/gregwar/image/Gregwar/Image/ImageColor.php',
'Gregwar\\Image\\Source\\Create' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/Create.php',
'Gregwar\\Image\\Source\\Data' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/Data.php',
'Gregwar\\Image\\Source\\File' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/File.php',
'Gregwar\\Image\\Source\\Resource' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/Resource.php',
'Gregwar\\Image\\Source\\Source' => $vendorDir . '/gregwar/image/Gregwar/Image/Source/Source.php',
'HTTP_ConditionalGet' => $vendorDir . '/mrclay/minify/min/lib/HTTP/ConditionalGet.php',
'HTTP_Encoder' => $vendorDir . '/mrclay/minify/min/lib/HTTP/Encoder.php',
'JSCompilerContext' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php',
'JSMin' => $vendorDir . '/mrclay/minify/min/lib/JSMin.php',
'JSMinPlus' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php',
'JSMin_UnterminatedCommentException' => $vendorDir . '/mrclay/minify/min/lib/JSMin.php',
'JSMin_UnterminatedRegExpException' => $vendorDir . '/mrclay/minify/min/lib/JSMin.php',
'JSMin_UnterminatedStringException' => $vendorDir . '/mrclay/minify/min/lib/JSMin.php',
'JSNode' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php',
'JSParser' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php',
'JSToken' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php',
'JSTokenizer' => $vendorDir . '/mrclay/minify/min/lib/JSMinPlus.php',
'Minify' => $vendorDir . '/mrclay/minify/min/lib/Minify.php',
'Minify_Build' => $vendorDir . '/mrclay/minify/min/lib/Minify/Build.php',
'Minify_CSS' => $vendorDir . '/mrclay/minify/min/lib/Minify/CSS.php',
'Minify_CSS_Compressor' => $vendorDir . '/mrclay/minify/min/lib/Minify/CSS/Compressor.php',
'Minify_CSS_UriRewriter' => $vendorDir . '/mrclay/minify/min/lib/Minify/CSS/UriRewriter.php',
'Minify_CSSmin' => $vendorDir . '/mrclay/minify/min/lib/Minify/CSSmin.php',
'Minify_Cache_APC' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/APC.php',
'Minify_Cache_File' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/File.php',
'Minify_Cache_Memcache' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/Memcache.php',
'Minify_Cache_XCache' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/XCache.php',
'Minify_Cache_ZendPlatform' => $vendorDir . '/mrclay/minify/min/lib/Minify/Cache/ZendPlatform.php',
'Minify_ClosureCompiler' => $vendorDir . '/mrclay/minify/min/lib/Minify/ClosureCompiler.php',
'Minify_CommentPreserver' => $vendorDir . '/mrclay/minify/min/lib/Minify/CommentPreserver.php',
'Minify_Controller_Base' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Base.php',
'Minify_Controller_Files' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Files.php',
'Minify_Controller_Groups' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Groups.php',
'Minify_Controller_MinApp' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/MinApp.php',
'Minify_Controller_Page' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Page.php',
'Minify_Controller_Version1' => $vendorDir . '/mrclay/minify/min/lib/Minify/Controller/Version1.php',
'Minify_DebugDetector' => $vendorDir . '/mrclay/minify/min/lib/Minify/DebugDetector.php',
'Minify_HTML' => $vendorDir . '/mrclay/minify/min/lib/Minify/HTML.php',
'Minify_HTML_Helper' => $vendorDir . '/mrclay/minify/min/lib/Minify/HTML/Helper.php',
'Minify_ImportProcessor' => $vendorDir . '/mrclay/minify/min/lib/Minify/ImportProcessor.php',
'Minify_JS_ClosureCompiler' => $vendorDir . '/mrclay/minify/min/lib/Minify/JS/ClosureCompiler.php',
'Minify_JS_ClosureCompiler_Exception' => $vendorDir . '/mrclay/minify/min/lib/Minify/JS/ClosureCompiler.php',
'Minify_Lines' => $vendorDir . '/mrclay/minify/min/lib/Minify/Lines.php',
'Minify_Loader' => $vendorDir . '/mrclay/minify/min/lib/Minify/Loader.php',
'Minify_Logger' => $vendorDir . '/mrclay/minify/min/lib/Minify/Logger.php',
'Minify_Packer' => $vendorDir . '/mrclay/minify/min/lib/Minify/Packer.php',
'Minify_Source' => $vendorDir . '/mrclay/minify/min/lib/Minify/Source.php',
'Minify_YUICompressor' => $vendorDir . '/mrclay/minify/min/lib/Minify/YUICompressor.php',
'Minify_YUI_CssCompressor' => $vendorDir . '/mrclay/minify/min/lib/Minify/YUI/CssCompressor.php',
'MrClay\\Cli' => $vendorDir . '/mrclay/minify/min/lib/MrClay/Cli.php',
'MrClay\\Cli\\Arg' => $vendorDir . '/mrclay/minify/min/lib/MrClay/Cli/Arg.php',
'Parsedown' => $vendorDir . '/erusev/parsedown/Parsedown.php',
'ParsedownExtra' => $vendorDir . '/erusev/parsedown-extra/ParsedownExtra.php',
'Pimple\\Container' => $vendorDir . '/pimple/pimple/src/Pimple/Container.php',
'Pimple\\ServiceProviderInterface' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceProviderInterface.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',
'Symfony\\Component\\Console\\Command\\ListCommand' => $vendorDir . '/symfony/console/Symfony/Component/Console/Command/ListCommand.php',
'Symfony\\Component\\Console\\ConsoleEvents' => $vendorDir . '/symfony/console/Symfony/Component/Console/ConsoleEvents.php',
'Symfony\\Component\\Console\\Descriptor\\ApplicationDescription' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/ApplicationDescription.php',
'Symfony\\Component\\Console\\Descriptor\\Descriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/Descriptor.php',
'Symfony\\Component\\Console\\Descriptor\\DescriptorInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/DescriptorInterface.php',
'Symfony\\Component\\Console\\Descriptor\\JsonDescriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/JsonDescriptor.php',
'Symfony\\Component\\Console\\Descriptor\\MarkdownDescriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php',
'Symfony\\Component\\Console\\Descriptor\\TextDescriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/TextDescriptor.php',
'Symfony\\Component\\Console\\Descriptor\\XmlDescriptor' => $vendorDir . '/symfony/console/Symfony/Component/Console/Descriptor/XmlDescriptor.php',
'Symfony\\Component\\Console\\Event\\ConsoleCommandEvent' => $vendorDir . '/symfony/console/Symfony/Component/Console/Event/ConsoleCommandEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleEvent' => $vendorDir . '/symfony/console/Symfony/Component/Console/Event/ConsoleEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent' => $vendorDir . '/symfony/console/Symfony/Component/Console/Event/ConsoleExceptionEvent.php',
'Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent' => $vendorDir . '/symfony/console/Symfony/Component/Console/Event/ConsoleTerminateEvent.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatter' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatter.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterInterface.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyle' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyle.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php',
'Symfony\\Component\\Console\\Formatter\\OutputFormatterStyleStack' => $vendorDir . '/symfony/console/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php',
'Symfony\\Component\\Console\\Helper\\DescriptorHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/DescriptorHelper.php',
'Symfony\\Component\\Console\\Helper\\DialogHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/DialogHelper.php',
'Symfony\\Component\\Console\\Helper\\FormatterHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/FormatterHelper.php',
'Symfony\\Component\\Console\\Helper\\Helper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/Helper.php',
'Symfony\\Component\\Console\\Helper\\HelperInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/HelperInterface.php',
'Symfony\\Component\\Console\\Helper\\HelperSet' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/HelperSet.php',
'Symfony\\Component\\Console\\Helper\\InputAwareHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/InputAwareHelper.php',
'Symfony\\Component\\Console\\Helper\\ProgressBar' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/ProgressBar.php',
'Symfony\\Component\\Console\\Helper\\ProgressHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/ProgressHelper.php',
'Symfony\\Component\\Console\\Helper\\QuestionHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/QuestionHelper.php',
'Symfony\\Component\\Console\\Helper\\Table' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/Table.php',
'Symfony\\Component\\Console\\Helper\\TableHelper' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/TableHelper.php',
'Symfony\\Component\\Console\\Helper\\TableSeparator' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/TableSeparator.php',
'Symfony\\Component\\Console\\Helper\\TableStyle' => $vendorDir . '/symfony/console/Symfony/Component/Console/Helper/TableStyle.php',
'Symfony\\Component\\Console\\Input\\ArgvInput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/ArgvInput.php',
'Symfony\\Component\\Console\\Input\\ArrayInput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/ArrayInput.php',
'Symfony\\Component\\Console\\Input\\Input' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/Input.php',
'Symfony\\Component\\Console\\Input\\InputArgument' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputArgument.php',
'Symfony\\Component\\Console\\Input\\InputAwareInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputAwareInterface.php',
'Symfony\\Component\\Console\\Input\\InputDefinition' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputDefinition.php',
'Symfony\\Component\\Console\\Input\\InputInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputInterface.php',
'Symfony\\Component\\Console\\Input\\InputOption' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/InputOption.php',
'Symfony\\Component\\Console\\Input\\StringInput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Input/StringInput.php',
'Symfony\\Component\\Console\\Logger\\ConsoleLogger' => $vendorDir . '/symfony/console/Symfony/Component/Console/Logger/ConsoleLogger.php',
'Symfony\\Component\\Console\\Output\\BufferedOutput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/BufferedOutput.php',
'Symfony\\Component\\Console\\Output\\ConsoleOutput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/ConsoleOutput.php',
'Symfony\\Component\\Console\\Output\\ConsoleOutputInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/ConsoleOutputInterface.php',
'Symfony\\Component\\Console\\Output\\NullOutput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/NullOutput.php',
'Symfony\\Component\\Console\\Output\\Output' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/Output.php',
'Symfony\\Component\\Console\\Output\\OutputInterface' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/OutputInterface.php',
'Symfony\\Component\\Console\\Output\\StreamOutput' => $vendorDir . '/symfony/console/Symfony/Component/Console/Output/StreamOutput.php',
'Symfony\\Component\\Console\\Question\\ChoiceQuestion' => $vendorDir . '/symfony/console/Symfony/Component/Console/Question/ChoiceQuestion.php',
'Symfony\\Component\\Console\\Question\\ConfirmationQuestion' => $vendorDir . '/symfony/console/Symfony/Component/Console/Question/ConfirmationQuestion.php',
'Symfony\\Component\\Console\\Question\\Question' => $vendorDir . '/symfony/console/Symfony/Component/Console/Question/Question.php',
'Symfony\\Component\\Console\\Shell' => $vendorDir . '/symfony/console/Symfony/Component/Console/Shell.php',
'Symfony\\Component\\Console\\Tester\\ApplicationTester' => $vendorDir . '/symfony/console/Symfony/Component/Console/Tester/ApplicationTester.php',
'Symfony\\Component\\Console\\Tester\\CommandTester' => $vendorDir . '/symfony/console/Symfony/Component/Console/Tester/CommandTester.php',
'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php',
'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php',
'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php',
'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/WrappedListener.php',
'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php',
'Symfony\\Component\\EventDispatcher\\Event' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php',
'Symfony\\Component\\EventDispatcher\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php',
'Symfony\\Component\\EventDispatcher\\EventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php',
'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php',
'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php',
'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php',
'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Dumper.php',
'Symfony\\Component\\Yaml\\Escaper' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Escaper.php',
'Symfony\\Component\\Yaml\\Exception\\DumpException' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php',
'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php',
'Symfony\\Component\\Yaml\\Exception\\ParseException' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php',
'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php',
'Symfony\\Component\\Yaml\\Inline' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Inline.php',
'Symfony\\Component\\Yaml\\Parser' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Parser.php',
'Symfony\\Component\\Yaml\\Unescaper' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Unescaper.php',
'Symfony\\Component\\Yaml\\Yaml' => $vendorDir . '/symfony/yaml/Symfony/Component/Yaml/Yaml.php',
'Tracy\\Bar' => $vendorDir . '/tracy/tracy/src/Tracy/Bar.php',
'Tracy\\BlueScreen' => $vendorDir . '/tracy/tracy/src/Tracy/BlueScreen.php',
'Tracy\\Debugger' => $vendorDir . '/tracy/tracy/src/Tracy/Debugger.php',
@@ -14,7 +244,170 @@ return array(
'Tracy\\FireLogger' => $vendorDir . '/tracy/tracy/src/Tracy/FireLogger.php',
'Tracy\\Helpers' => $vendorDir . '/tracy/tracy/src/Tracy/Helpers.php',
'Tracy\\IBarPanel' => $vendorDir . '/tracy/tracy/src/Tracy/IBarPanel.php',
'Tracy\\ILogger' => $vendorDir . '/tracy/tracy/src/Tracy/ILogger.php',
'Tracy\\Logger' => $vendorDir . '/tracy/tracy/src/Tracy/Logger.php',
'Tracy\\OutputDebugger' => $vendorDir . '/tracy/tracy/src/Tracy/OutputDebugger.php',
'Twig_Autoloader' => $vendorDir . '/twig/twig/lib/Twig/Autoloader.php',
'Twig_Compiler' => $vendorDir . '/twig/twig/lib/Twig/Compiler.php',
'Twig_CompilerInterface' => $vendorDir . '/twig/twig/lib/Twig/CompilerInterface.php',
'Twig_Environment' => $vendorDir . '/twig/twig/lib/Twig/Environment.php',
'Twig_Error' => $vendorDir . '/twig/twig/lib/Twig/Error.php',
'Twig_Error_Loader' => $vendorDir . '/twig/twig/lib/Twig/Error/Loader.php',
'Twig_Error_Runtime' => $vendorDir . '/twig/twig/lib/Twig/Error/Runtime.php',
'Twig_Error_Syntax' => $vendorDir . '/twig/twig/lib/Twig/Error/Syntax.php',
'Twig_ExistsLoaderInterface' => $vendorDir . '/twig/twig/lib/Twig/ExistsLoaderInterface.php',
'Twig_ExpressionParser' => $vendorDir . '/twig/twig/lib/Twig/ExpressionParser.php',
'Twig_Extension' => $vendorDir . '/twig/twig/lib/Twig/Extension.php',
'Twig_ExtensionInterface' => $vendorDir . '/twig/twig/lib/Twig/ExtensionInterface.php',
'Twig_Extension_Core' => $vendorDir . '/twig/twig/lib/Twig/Extension/Core.php',
'Twig_Extension_Debug' => $vendorDir . '/twig/twig/lib/Twig/Extension/Debug.php',
'Twig_Extension_Escaper' => $vendorDir . '/twig/twig/lib/Twig/Extension/Escaper.php',
'Twig_Extension_Optimizer' => $vendorDir . '/twig/twig/lib/Twig/Extension/Optimizer.php',
'Twig_Extension_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/Extension/Sandbox.php',
'Twig_Extension_Staging' => $vendorDir . '/twig/twig/lib/Twig/Extension/Staging.php',
'Twig_Extension_StringLoader' => $vendorDir . '/twig/twig/lib/Twig/Extension/StringLoader.php',
'Twig_Filter' => $vendorDir . '/twig/twig/lib/Twig/Filter.php',
'Twig_FilterCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/FilterCallableInterface.php',
'Twig_FilterInterface' => $vendorDir . '/twig/twig/lib/Twig/FilterInterface.php',
'Twig_Filter_Function' => $vendorDir . '/twig/twig/lib/Twig/Filter/Function.php',
'Twig_Filter_Method' => $vendorDir . '/twig/twig/lib/Twig/Filter/Method.php',
'Twig_Filter_Node' => $vendorDir . '/twig/twig/lib/Twig/Filter/Node.php',
'Twig_Function' => $vendorDir . '/twig/twig/lib/Twig/Function.php',
'Twig_FunctionCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/FunctionCallableInterface.php',
'Twig_FunctionInterface' => $vendorDir . '/twig/twig/lib/Twig/FunctionInterface.php',
'Twig_Function_Function' => $vendorDir . '/twig/twig/lib/Twig/Function/Function.php',
'Twig_Function_Method' => $vendorDir . '/twig/twig/lib/Twig/Function/Method.php',
'Twig_Function_Node' => $vendorDir . '/twig/twig/lib/Twig/Function/Node.php',
'Twig_Lexer' => $vendorDir . '/twig/twig/lib/Twig/Lexer.php',
'Twig_LexerInterface' => $vendorDir . '/twig/twig/lib/Twig/LexerInterface.php',
'Twig_LoaderInterface' => $vendorDir . '/twig/twig/lib/Twig/LoaderInterface.php',
'Twig_Loader_Array' => $vendorDir . '/twig/twig/lib/Twig/Loader/Array.php',
'Twig_Loader_Chain' => $vendorDir . '/twig/twig/lib/Twig/Loader/Chain.php',
'Twig_Loader_Filesystem' => $vendorDir . '/twig/twig/lib/Twig/Loader/Filesystem.php',
'Twig_Loader_String' => $vendorDir . '/twig/twig/lib/Twig/Loader/String.php',
'Twig_Markup' => $vendorDir . '/twig/twig/lib/Twig/Markup.php',
'Twig_Node' => $vendorDir . '/twig/twig/lib/Twig/Node.php',
'Twig_NodeInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeInterface.php',
'Twig_NodeOutputInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeOutputInterface.php',
'Twig_NodeTraverser' => $vendorDir . '/twig/twig/lib/Twig/NodeTraverser.php',
'Twig_NodeVisitorInterface' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitorInterface.php',
'Twig_NodeVisitor_Escaper' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/Escaper.php',
'Twig_NodeVisitor_Optimizer' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/Optimizer.php',
'Twig_NodeVisitor_SafeAnalysis' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php',
'Twig_NodeVisitor_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/NodeVisitor/Sandbox.php',
'Twig_Node_AutoEscape' => $vendorDir . '/twig/twig/lib/Twig/Node/AutoEscape.php',
'Twig_Node_Block' => $vendorDir . '/twig/twig/lib/Twig/Node/Block.php',
'Twig_Node_BlockReference' => $vendorDir . '/twig/twig/lib/Twig/Node/BlockReference.php',
'Twig_Node_Body' => $vendorDir . '/twig/twig/lib/Twig/Node/Body.php',
'Twig_Node_Do' => $vendorDir . '/twig/twig/lib/Twig/Node/Do.php',
'Twig_Node_Embed' => $vendorDir . '/twig/twig/lib/Twig/Node/Embed.php',
'Twig_Node_Expression' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression.php',
'Twig_Node_Expression_Array' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Array.php',
'Twig_Node_Expression_AssignName' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/AssignName.php',
'Twig_Node_Expression_Binary' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary.php',
'Twig_Node_Expression_Binary_Add' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Add.php',
'Twig_Node_Expression_Binary_And' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/And.php',
'Twig_Node_Expression_Binary_BitwiseAnd' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php',
'Twig_Node_Expression_Binary_BitwiseOr' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php',
'Twig_Node_Expression_Binary_BitwiseXor' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php',
'Twig_Node_Expression_Binary_Concat' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php',
'Twig_Node_Expression_Binary_Div' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Div.php',
'Twig_Node_Expression_Binary_EndsWith' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php',
'Twig_Node_Expression_Binary_Equal' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php',
'Twig_Node_Expression_Binary_FloorDiv' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php',
'Twig_Node_Expression_Binary_Greater' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php',
'Twig_Node_Expression_Binary_GreaterEqual' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php',
'Twig_Node_Expression_Binary_In' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/In.php',
'Twig_Node_Expression_Binary_Less' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Less.php',
'Twig_Node_Expression_Binary_LessEqual' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php',
'Twig_Node_Expression_Binary_Matches' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php',
'Twig_Node_Expression_Binary_Mod' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php',
'Twig_Node_Expression_Binary_Mul' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php',
'Twig_Node_Expression_Binary_NotEqual' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php',
'Twig_Node_Expression_Binary_NotIn' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php',
'Twig_Node_Expression_Binary_Or' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Or.php',
'Twig_Node_Expression_Binary_Power' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Power.php',
'Twig_Node_Expression_Binary_Range' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Range.php',
'Twig_Node_Expression_Binary_StartsWith' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php',
'Twig_Node_Expression_Binary_Sub' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php',
'Twig_Node_Expression_BlockReference' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/BlockReference.php',
'Twig_Node_Expression_Call' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Call.php',
'Twig_Node_Expression_Conditional' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Conditional.php',
'Twig_Node_Expression_Constant' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Constant.php',
'Twig_Node_Expression_ExtensionReference' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php',
'Twig_Node_Expression_Filter' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Filter.php',
'Twig_Node_Expression_Filter_Default' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Filter/Default.php',
'Twig_Node_Expression_Function' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Function.php',
'Twig_Node_Expression_GetAttr' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/GetAttr.php',
'Twig_Node_Expression_MethodCall' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/MethodCall.php',
'Twig_Node_Expression_Name' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Name.php',
'Twig_Node_Expression_Parent' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Parent.php',
'Twig_Node_Expression_TempName' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/TempName.php',
'Twig_Node_Expression_Test' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test.php',
'Twig_Node_Expression_Test_Constant' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Constant.php',
'Twig_Node_Expression_Test_Defined' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Defined.php',
'Twig_Node_Expression_Test_Divisibleby' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php',
'Twig_Node_Expression_Test_Even' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Even.php',
'Twig_Node_Expression_Test_Null' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Null.php',
'Twig_Node_Expression_Test_Odd' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Odd.php',
'Twig_Node_Expression_Test_Sameas' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php',
'Twig_Node_Expression_Unary' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary.php',
'Twig_Node_Expression_Unary_Neg' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php',
'Twig_Node_Expression_Unary_Not' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary/Not.php',
'Twig_Node_Expression_Unary_Pos' => $vendorDir . '/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php',
'Twig_Node_Flush' => $vendorDir . '/twig/twig/lib/Twig/Node/Flush.php',
'Twig_Node_For' => $vendorDir . '/twig/twig/lib/Twig/Node/For.php',
'Twig_Node_ForLoop' => $vendorDir . '/twig/twig/lib/Twig/Node/ForLoop.php',
'Twig_Node_If' => $vendorDir . '/twig/twig/lib/Twig/Node/If.php',
'Twig_Node_Import' => $vendorDir . '/twig/twig/lib/Twig/Node/Import.php',
'Twig_Node_Include' => $vendorDir . '/twig/twig/lib/Twig/Node/Include.php',
'Twig_Node_Macro' => $vendorDir . '/twig/twig/lib/Twig/Node/Macro.php',
'Twig_Node_Module' => $vendorDir . '/twig/twig/lib/Twig/Node/Module.php',
'Twig_Node_Print' => $vendorDir . '/twig/twig/lib/Twig/Node/Print.php',
'Twig_Node_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/Node/Sandbox.php',
'Twig_Node_SandboxedModule' => $vendorDir . '/twig/twig/lib/Twig/Node/SandboxedModule.php',
'Twig_Node_SandboxedPrint' => $vendorDir . '/twig/twig/lib/Twig/Node/SandboxedPrint.php',
'Twig_Node_Set' => $vendorDir . '/twig/twig/lib/Twig/Node/Set.php',
'Twig_Node_SetTemp' => $vendorDir . '/twig/twig/lib/Twig/Node/SetTemp.php',
'Twig_Node_Spaceless' => $vendorDir . '/twig/twig/lib/Twig/Node/Spaceless.php',
'Twig_Node_Text' => $vendorDir . '/twig/twig/lib/Twig/Node/Text.php',
'Twig_Parser' => $vendorDir . '/twig/twig/lib/Twig/Parser.php',
'Twig_ParserInterface' => $vendorDir . '/twig/twig/lib/Twig/ParserInterface.php',
'Twig_Sandbox_SecurityError' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityError.php',
'Twig_Sandbox_SecurityPolicy' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php',
'Twig_Sandbox_SecurityPolicyInterface' => $vendorDir . '/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php',
'Twig_SimpleFilter' => $vendorDir . '/twig/twig/lib/Twig/SimpleFilter.php',
'Twig_SimpleFunction' => $vendorDir . '/twig/twig/lib/Twig/SimpleFunction.php',
'Twig_Template' => $vendorDir . '/twig/twig/lib/Twig/Template.php',
'Twig_TemplateInterface' => $vendorDir . '/twig/twig/lib/Twig/TemplateInterface.php',
'Twig_Test' => $vendorDir . '/twig/twig/lib/Twig/Test.php',
'Twig_TestCallableInterface' => $vendorDir . '/twig/twig/lib/Twig/TestCallableInterface.php',
'Twig_TestInterface' => $vendorDir . '/twig/twig/lib/Twig/TestInterface.php',
'Twig_Test_Function' => $vendorDir . '/twig/twig/lib/Twig/Test/Function.php',
'Twig_Test_IntegrationTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/IntegrationTestCase.php',
'Twig_Test_Method' => $vendorDir . '/twig/twig/lib/Twig/Test/Method.php',
'Twig_Test_Node' => $vendorDir . '/twig/twig/lib/Twig/Test/Node.php',
'Twig_Test_NodeTestCase' => $vendorDir . '/twig/twig/lib/Twig/Test/NodeTestCase.php',
'Twig_Token' => $vendorDir . '/twig/twig/lib/Twig/Token.php',
'Twig_TokenParser' => $vendorDir . '/twig/twig/lib/Twig/TokenParser.php',
'Twig_TokenParserBroker' => $vendorDir . '/twig/twig/lib/Twig/TokenParserBroker.php',
'Twig_TokenParserBrokerInterface' => $vendorDir . '/twig/twig/lib/Twig/TokenParserBrokerInterface.php',
'Twig_TokenParserInterface' => $vendorDir . '/twig/twig/lib/Twig/TokenParserInterface.php',
'Twig_TokenParser_AutoEscape' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/AutoEscape.php',
'Twig_TokenParser_Block' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Block.php',
'Twig_TokenParser_Do' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Do.php',
'Twig_TokenParser_Embed' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Embed.php',
'Twig_TokenParser_Extends' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Extends.php',
'Twig_TokenParser_Filter' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Filter.php',
'Twig_TokenParser_Flush' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Flush.php',
'Twig_TokenParser_For' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/For.php',
'Twig_TokenParser_From' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/From.php',
'Twig_TokenParser_If' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/If.php',
'Twig_TokenParser_Import' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Import.php',
'Twig_TokenParser_Include' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Include.php',
'Twig_TokenParser_Macro' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Macro.php',
'Twig_TokenParser_Sandbox' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Sandbox.php',
'Twig_TokenParser_Set' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Set.php',
'Twig_TokenParser_Spaceless' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Spaceless.php',
'Twig_TokenParser_Use' => $vendorDir . '/twig/twig/lib/Twig/TokenParser/Use.php',
'Twig_TokenStream' => $vendorDir . '/twig/twig/lib/Twig/TokenStream.php',
);

View File

@@ -8,4 +8,6 @@ $baseDir = dirname($vendorDir);
return array(
$vendorDir . '/tracy/tracy/src/shortcuts.php',
$vendorDir . '/ircmaxell/password-compat/lib/password.php',
$vendorDir . '/donatj/phpuseragentparser/Source/UserAgentParser.php',
$baseDir . '/system/defines.php',
);

View File

@@ -8,7 +8,10 @@ $baseDir = dirname($vendorDir);
return array(
'Twig_' => array($vendorDir . '/twig/twig/lib'),
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'Pimple' => array($vendorDir . '/pimple/pimple/src'),
'ParsedownExtra' => array($vendorDir . '/erusev/parsedown-extra'),
'Parsedown' => array($vendorDir . '/erusev/parsedown'),
'Gregwar\\Image' => array($vendorDir . '/gregwar/image'),
'Gregwar\\Cache' => array($vendorDir . '/gregwar/cache'),

View File

@@ -6,4 +6,5 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Grav\\' => array($baseDir . '/system/src'),
);

View File

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

View File

@@ -1,22 +1,22 @@
[
{
"name": "erusev/parsedown",
"version": "dev-master",
"version_normalized": "9999999-dev",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/erusev/parsedown.git",
"reference": "e33ac1c56ea591f21b9cf2fa74356ef708d4e130"
"reference": "d24439ada0704948deef0d3eda2ea20fd8db1747"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/erusev/parsedown/zipball/e33ac1c56ea591f21b9cf2fa74356ef708d4e130",
"reference": "e33ac1c56ea591f21b9cf2fa74356ef708d4e130",
"url": "https://api.github.com/repos/erusev/parsedown/zipball/d24439ada0704948deef0d3eda2ea20fd8db1747",
"reference": "d24439ada0704948deef0d3eda2ea20fd8db1747",
"shasum": ""
},
"time": "2014-06-18 09:27:25",
"time": "2014-05-21 20:20:46",
"type": "library",
"installation-source": "source",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Parsedown": ""
@@ -42,17 +42,17 @@
},
{
"name": "doctrine/cache",
"version": "dev-master",
"version_normalized": "9999999-dev",
"version": "v1.3.0",
"version_normalized": "1.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "72121e68265cd8b37f9b69778308251f6c5133e4"
"reference": "e16d7adf45664a50fa86f515b6d5e7f670130449"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/72121e68265cd8b37f9b69778308251f6c5133e4",
"reference": "72121e68265cd8b37f9b69778308251f6c5133e4",
"url": "https://api.github.com/repos/doctrine/cache/zipball/e16d7adf45664a50fa86f515b6d5e7f670130449",
"reference": "e16d7adf45664a50fa86f515b6d5e7f670130449",
"shasum": ""
},
"require": {
@@ -65,14 +65,14 @@
"phpunit/phpunit": ">=3.7",
"satooshi/php-coveralls": "~0.6"
},
"time": "2014-08-05 12:51:19",
"time": "2013-10-25 19:04:14",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
"dev-master": "1.0.x-dev"
}
},
"installation-source": "source",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Doctrine\\Common\\Cache\\": "lib/"
@@ -83,6 +83,17 @@
"MIT"
],
"authors": [
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com",
"homepage": "http://www.jwage.com/",
"role": "Creator"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com",
"homepage": "http://www.instaclick.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
@@ -91,17 +102,11 @@
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh",
"role": "Developer of wrapped JMSSerializerBundle"
}
],
"description": "Caching library offering an object-oriented API for many cache backends",
@@ -112,18 +117,273 @@
]
},
{
"name": "tracy/tracy",
"version": "dev-master",
"version_normalized": "9999999-dev",
"name": "mrclay/minify",
"version": "2.2.0",
"version_normalized": "2.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/nette/tracy.git",
"reference": "1250ac4907947b28ec66d6e00a337dadaddaad4c"
"url": "https://github.com/mrclay/minify.git",
"reference": "d245bca4987dec197d1e6d7dc117614b60ff7494"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/tracy/zipball/1250ac4907947b28ec66d6e00a337dadaddaad4c",
"reference": "1250ac4907947b28ec66d6e00a337dadaddaad4c",
"url": "https://api.github.com/repos/mrclay/minify/zipball/d245bca4987dec197d1e6d7dc117614b60ff7494",
"reference": "d245bca4987dec197d1e6d7dc117614b60ff7494",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"php": ">=5.2.1"
},
"time": "2014-03-12 12:54:23",
"type": "library",
"installation-source": "dist",
"autoload": {
"classmap": [
"min/lib/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Steve Clay",
"email": "steve@mrclay.org",
"homepage": "http://www.mrclay.org/",
"role": "Developer"
}
],
"description": "Minify is a PHP5 app that helps you follow several rules for client-side performance. It combines multiple CSS or Javascript files, removes unnecessary whitespace and comments, and serves them with gzip encoding and optimal client-side cache headers",
"homepage": "http://code.google.com/p/minify/"
},
{
"name": "pimple/pimple",
"version": "v3.0.0",
"version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Pimple.git",
"reference": "876bf0899d01feacd2a2e83f04641e51350099ef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Pimple/zipball/876bf0899d01feacd2a2e83f04641e51350099ef",
"reference": "876bf0899d01feacd2a2e83f04641e51350099ef",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2014-07-24 09:48:15",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple is a simple Dependency Injection Container for PHP 5.3",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
]
},
{
"name": "symfony/yaml",
"version": "v2.5.3",
"version_normalized": "2.5.3.0",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f",
"reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2014-08-05 09:00:40",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Symfony\\Component\\Yaml\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/console",
"version": "v2.5.3",
"version_normalized": "2.5.3.0",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/cd2d1e4bac2206b337326b0140ff475fe9ad5f63",
"reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/event-dispatcher": "~2.1"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": ""
},
"time": "2014-08-05 09:00:40",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Symfony\\Component\\Console\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.5.3",
"version_normalized": "2.5.3.0",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/8faf5cc7e80fde74a650a36e60d32ce3c3e0457b",
"reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~2.0",
"symfony/dependency-injection": "~2.0",
"symfony/stopwatch": "~2.2"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"time": "2014-07-28 13:20:46",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Symfony\\Component\\EventDispatcher\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"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": {
@@ -132,14 +392,9 @@
"require-dev": {
"nette/tester": "~1.0"
},
"time": "2014-08-07 17:19:48",
"time": "2014-06-24 01:18:03",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"installation-source": "source",
"installation-source": "dist",
"autoload": {
"classmap": [
"src/Tracy"
@@ -174,24 +429,21 @@
},
{
"name": "gregwar/cache",
"version": "v1.0.9",
"version_normalized": "1.0.9.0",
"version": "v1.0.7",
"version_normalized": "1.0.7.0",
"target-dir": "Gregwar/Cache",
"source": {
"type": "git",
"url": "https://github.com/Gregwar/Cache.git",
"reference": "514b9b469082028d094e33e4a059c863c546b14e"
"reference": "b2d0197c07cc1ccf7436e1ae7bf64f9948e02052"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Gregwar/Cache/zipball/514b9b469082028d094e33e4a059c863c546b14e",
"reference": "514b9b469082028d094e33e4a059c863c546b14e",
"url": "https://api.github.com/repos/Gregwar/Cache/zipball/b2d0197c07cc1ccf7436e1ae7bf64f9948e02052",
"reference": "b2d0197c07cc1ccf7436e1ae7bf64f9948e02052",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"time": "2014-07-05 17:42:36",
"time": "2014-02-20 20:04:58",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -220,28 +472,28 @@
},
{
"name": "gregwar/image",
"version": "dev-master",
"version_normalized": "9999999-dev",
"version": "v2.0.17",
"version_normalized": "2.0.17.0",
"target-dir": "Gregwar/Image",
"source": {
"type": "git",
"url": "https://github.com/Gregwar/Image.git",
"reference": "31cf30151015d66f320011ea8646e90bb62ffb13"
"reference": "a7bba10ed0e3004c098bb7fde3a3e5bcafca8a2c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Gregwar/Image/zipball/31cf30151015d66f320011ea8646e90bb62ffb13",
"reference": "31cf30151015d66f320011ea8646e90bb62ffb13",
"url": "https://api.github.com/repos/Gregwar/Image/zipball/a7bba10ed0e3004c098bb7fde3a3e5bcafca8a2c",
"reference": "a7bba10ed0e3004c098bb7fde3a3e5bcafca8a2c",
"shasum": ""
},
"require": {
"ext-gd": "*",
"gregwar/cache": "1.*",
"gregwar/cache": "v1.0.7",
"php": ">=5.3.0"
},
"time": "2014-06-30 15:00:37",
"time": "2014-02-20 22:20:45",
"type": "library",
"installation-source": "source",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Gregwar\\Image": ""
@@ -265,171 +517,6 @@
"image"
]
},
{
"name": "twig/twig",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig.git",
"reference": "e2d2a250d963dd5bba4ad23fc01c7282fc042946"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Twig/zipball/e2d2a250d963dd5bba4ad23fc01c7282fc042946",
"reference": "e2d2a250d963dd5bba4ad23fc01c7282fc042946",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"time": "2014-08-06 06:46:32",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.16-dev"
}
},
"installation-source": "source",
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "https://github.com/fabpot/Twig/graphs/contributors",
"role": "Contributors"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
]
},
{
"name": "symfony/yaml",
"version": "2.5.x-dev",
"version_normalized": "2.5.9999999.9999999-dev",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f",
"reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"time": "2014-08-05 09:00:40",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"installation-source": "source",
"autoload": {
"psr-0": {
"Symfony\\Component\\Yaml\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com"
},
{
"name": "symfony/console",
"version": "2.5.x-dev",
"version_normalized": "2.5.9999999.9999999-dev",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/cd2d1e4bac2206b337326b0140ff475fe9ad5f63",
"reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/event-dispatcher": "~2.1"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": ""
},
"time": "2014-08-05 09:00:40",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"installation-source": "source",
"autoload": {
"psr-0": {
"Symfony\\Component\\Console\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com"
},
{
"name": "ircmaxell/password-compat",
"version": "1.0.3",
@@ -470,5 +557,159 @@
"hashing",
"password"
]
},
{
"name": "twig/twig",
"version": "v1.16.0",
"version_normalized": "1.16.0.0",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig.git",
"reference": "8ce37115802e257a984a82d38254884085060024"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Twig/zipball/8ce37115802e257a984a82d38254884085060024",
"reference": "8ce37115802e257a984a82d38254884085060024",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"time": "2014-07-05 12:19:05",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.16-dev"
}
},
"installation-source": "source",
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "https://github.com/fabpot/Twig/graphs/contributors",
"role": "Contributors"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
]
},
{
"name": "donatj/phpuseragentparser",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/donatj/PhpUserAgent.git",
"reference": "abbd69a119f067e4afc3c4baf28d04646114a668"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/abbd69a119f067e4afc3c4baf28d04646114a668",
"reference": "abbd69a119f067e4afc3c4baf28d04646114a668",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"camspiers/json-pretty": "0.1.*"
},
"time": "2014-08-06 03:39:39",
"type": "library",
"installation-source": "source",
"autoload": {
"files": [
"Source/UserAgentParser.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jesse G. Donat",
"email": "donatj@gmail.com",
"homepage": "http://donatstudios.com",
"role": "Developer"
}
],
"description": "Simple, streamlined PHP user-agent parser",
"homepage": "http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT",
"keywords": [
"parser",
"user agent",
"useragent"
]
},
{
"name": "erusev/parsedown-extra",
"version": "dev-master",
"version_normalized": "9999999-dev",
"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": "source",
"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"
]
}
]

View File

@@ -43,7 +43,7 @@ abstract class CacheProvider implements Cache
/**
* The namespace version.
*
* @var integer|null
* @var string
*/
private $namespaceVersion;
@@ -162,7 +162,7 @@ abstract class CacheProvider implements Cache
/**
* Returns the namespace version.
*
* @return integer
* @return string
*/
private function getNamespaceVersion()
{
@@ -189,7 +189,7 @@ abstract class CacheProvider implements Cache
*
* @param string $id The id of the cache entry to fetch.
*
* @return string|boolean The cached data or FALSE, if no cache entry exists for the given id.
* @return string|bool The cached data or FALSE, if no cache entry exists for the given id.
*/
abstract protected function doFetch($id);

View File

@@ -130,7 +130,7 @@ abstract class FileCache extends CacheProvider
protected function doGetStats()
{
$usage = 0;
foreach ($this->getIterator() as $file) {
foreach ($this->getIterator() as $name => $file) {
$usage += $file->getSize();
}

View File

@@ -105,21 +105,9 @@ class FilesystemCache extends FileCache
$filepath = pathinfo($filename, PATHINFO_DIRNAME);
if ( ! is_dir($filepath)) {
if (false === @mkdir($filepath, 0777, true) && !is_dir($filepath)) {
return false;
}
} elseif ( ! is_writable($filepath)) {
return false;
mkdir($filepath, 0777, true);
}
$tmpFile = tempnam($filepath, basename($filename));
if ((file_put_contents($tmpFile, $lifeTime . PHP_EOL . $data) !== false) && @rename($tmpFile, $filename)) {
@chmod($filename, 0666 & ~umask());
return true;
}
return false;
return file_put_contents($filename, $lifeTime . PHP_EOL . $data) !== false;
}
}

View File

@@ -21,5 +21,5 @@ namespace Doctrine\Common\Cache;
class Version
{
const VERSION = '1.4.0-DEV';
const VERSION = '1.3.0';
}

View File

@@ -0,0 +1,22 @@
The MIT License
===============
Copyright (c) 2013 Jesse G. Donat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,110 @@
# PHP User Agent Parser
[![Latest Stable Version](https://poser.pugx.org/donatj/phpuseragentparser/v/stable.png)](https://packagist.org/packages/donatj/phpuseragentparser) [![Total Downloads](https://poser.pugx.org/donatj/phpuseragentparser/downloads.png)](https://packagist.org/packages/donatj/phpuseragentparser) [![Latest Unstable Version](https://poser.pugx.org/donatj/phpuseragentparser/v/unstable.png)](https://packagist.org/packages/donatj/phpuseragentparser) [![License](https://poser.pugx.org/donatj/phpuseragentparser/license.png)](https://packagist.org/packages/donatj/phpuseragentparser)
[![Build Status](https://travis-ci.org/donatj/PhpUserAgent.png?branch=master)](https://travis-ci.org/donatj/PhpUserAgent)
[![HHVM Status](http://hhvm.h4cc.de/badge/donatj/phpuseragentparser.png)](http://hhvm.h4cc.de/package/donatj/phpuseragentparser) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/donatj/PhpUserAgent/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/donatj/PhpUserAgent/?branch=master)
## What It Is
A simple, streamlined PHP user-agent parser!
Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
## Why Use This
You have your choice in user-agent parsers. This one detects **all modern browsers** in a very light, quick, understandable fashion.
It is less than 150 lines of code, and consists of just two regular expressions!
It can also correctly identify exotic versions of IE others fail on.
It offers 100% unit test coverage, is installable via Composer, and is very easy to use.
## What It Doesn't Do
### OS Versions
User-agent strings **are not** a reliable source of OS Version!
- Many agents simply don't send the information.
- Others provide varying levels of accuracy.
- Parsing Windows versions alone almost nearly doubles the size of the code.
I'm much more interested in keeping this thing *tiny* and accurate than adding niché features and would rather focus on things that can be **done well**.
All that said, there is the start of a [branch to do it](https://github.com/donatj/PhpUserAgent/tree/os_version_detection) I created for a client if you want to poke it, I update it from time to time, but frankly if you need to *reliably detect OS Version*, using user-agent isn't the way to do it. I'd go with JavaScript.
## Requirements
- PHP 5.3.0+
## Installing
PHP User Agent is available through Packagist via Composer.
```json
{
"require": {
"donatj/phpuseragentparser": "*"
}
}
```
## Sample Usage
```php
$ua_info = parse_user_agent();
/*
array(
'platform' => '[Detected Platform]',
'browser' => '[Detected Browser]',
'version' => '[Detected Browser Version]',
);
*/
```
## Currently Detected Platforms
- Desktop
- Windows
- Linux
- Macintosh
- Chrome OS
- Mobile
- Android
- iPhone
- iPad
- Windows Phone OS
- Kindle
- Kindle Fire
- BlackBerry
- Playbook
- Console
- Nintendo 3DS
- Nintendo Wii
- Nintendo WiiU
- PlayStation 3
- PlayStation 4
- PlayStation Vita
- Xbox 360
- Xbox One
## Currently Detected Browsers
- Android Browser
- BlackBerry Browser
- Camino
- Kindle / Silk
- Firefox / Iceweasel
- Safari
- Internet Explorer
- IEMobile
- Chrome
- Opera
- Midori
- Lynx
- Wget
- Curl
More information is available at [Donat Studios](http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT).

View File

@@ -0,0 +1,144 @@
<?php
/**
* Parses a user agent string into its important parts
*
* @author Jesse G. Donat <donatj@gmail.com>
* @link https://github.com/donatj/PhpUserAgent
* @link http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT
* @param string|null $u_agent User agent string to parse or null. Uses $_SERVER['HTTP_USER_AGENT'] on NULL
* @throws InvalidArgumentException on not having a proper user agent to parse.
* @return array an array with browser, version and platform keys
*/
function parse_user_agent( $u_agent = null ) {
if( is_null($u_agent) ) {
if( isset($_SERVER['HTTP_USER_AGENT']) ) {
$u_agent = $_SERVER['HTTP_USER_AGENT'];
} else {
throw new \InvalidArgumentException('parse_user_agent requires a user agent');
}
}
$platform = null;
$browser = null;
$version = null;
$empty = array( 'platform' => $platform, 'browser' => $browser, 'version' => $version );
if( !$u_agent ) return $empty;
if( preg_match('/\((.*?)\)/im', $u_agent, $parent_matches) ) {
preg_match_all('/(?P<platform>BB\d+;|Android|CrOS|iPhone|iPad|Linux|Macintosh|Windows(\ Phone)?|Silk|linux-gnu|BlackBerry|PlayBook|Nintendo\ (WiiU?|3DS)|Xbox(\ One)?)
(?:\ [^;]*)?
(?:;|$)/imx', $parent_matches[1], $result, PREG_PATTERN_ORDER);
$priority = array( 'Android', 'Xbox One', 'Xbox' );
$result['platform'] = array_unique($result['platform']);
if( count($result['platform']) > 1 ) {
if( $keys = array_intersect($priority, $result['platform']) ) {
$platform = reset($keys);
} else {
$platform = $result['platform'][0];
}
} elseif( isset($result['platform'][0]) ) {
$platform = $result['platform'][0];
}
}
if( $platform == 'linux-gnu' ) {
$platform = 'Linux';
} elseif( $platform == 'CrOS' ) {
$platform = 'Chrome OS';
}
preg_match_all('%(?P<browser>Camino|Kindle(\ Fire\ Build)?|Firefox|Iceweasel|Safari|MSIE|Trident/.*rv|AppleWebKit|Chrome|IEMobile|Opera|OPR|Silk|Lynx|Midori|Version|Wget|curl|NintendoBrowser|PLAYSTATION\ (\d|Vita)+)
(?:\)?;?)
(?:(?:[:/ ])(?P<version>[0-9A-Z.]+)|/(?:[A-Z]*))%ix',
$u_agent, $result, PREG_PATTERN_ORDER);
// If nothing matched, return null (to avoid undefined index errors)
if( !isset($result['browser'][0]) || !isset($result['version'][0]) ) {
return $empty;
}
$browser = $result['browser'][0];
$version = $result['version'][0];
$find = function ( $search, &$key ) use ( $result ) {
$xkey = array_search(strtolower($search), array_map('strtolower', $result['browser']));
if( $xkey !== false ) {
$key = $xkey;
return true;
}
return false;
};
$key = 0;
if( $browser == 'Iceweasel' ) {
$browser = 'Firefox';
} elseif( $find('Playstation Vita', $key) ) {
$platform = 'PlayStation Vita';
$browser = 'Browser';
} elseif( $find('Kindle Fire Build', $key) || $find('Silk', $key) ) {
$browser = $result['browser'][$key] == 'Silk' ? 'Silk' : 'Kindle';
$platform = 'Kindle Fire';
if( !($version = $result['version'][$key]) || !is_numeric($version[0]) ) {
$version = $result['version'][array_search('Version', $result['browser'])];
}
} elseif( $find('NintendoBrowser', $key) || $platform == 'Nintendo 3DS' ) {
$browser = 'NintendoBrowser';
$version = $result['version'][$key];
} elseif( $find('Kindle', $key) ) {
$browser = $result['browser'][$key];
$platform = 'Kindle';
$version = $result['version'][$key];
} elseif( $find('OPR', $key) ) {
$browser = 'Opera Next';
$version = $result['version'][$key];
} elseif( $find('Opera', $key) ) {
$browser = 'Opera';
$find('Version', $key);
$version = $result['version'][$key];
} elseif( $find('Midori', $key) ) {
$browser = 'Midori';
$version = $result['version'][$key];
} elseif( $browser == 'MSIE' || strpos($browser, 'Trident') !== false ) {
if( $find('IEMobile', $key) ) {
$browser = 'IEMobile';
} else {
$browser = 'MSIE';
$key = 0;
}
$version = $result['version'][$key];
} elseif( $find('Chrome', $key) ) {
$browser = 'Chrome';
$version = $result['version'][$key];
} elseif( $browser == 'AppleWebKit' ) {
if( ($platform == 'Android' && !($key = 0)) ) {
$browser = 'Android Browser';
} elseif( strpos($platform, 'BB') === 0 ) {
$browser = 'BlackBerry Browser';
$platform = 'BlackBerry';
} elseif( $platform == 'BlackBerry' || $platform == 'PlayBook' ) {
$browser = 'BlackBerry Browser';
} elseif( $find('Safari', $key) ) {
$browser = 'Safari';
}
$find('Version', $key);
$version = $result['version'][$key];
} elseif( $key = preg_grep('/playstation \d/i', array_map('strtolower', $result['browser'])) ) {
$key = reset($key);
$platform = 'PlayStation ' . preg_replace('/[^\d]/i', '', $key);
$browser = 'NetFront';
}
return array( 'platform' => $platform, 'browser' => $browser, 'version' => $version );
}

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Emanuil Rusev, erusev.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,415 @@
<?php
#
#
# Parsedown Extra
# https://github.com/erusev/parsedown-extra
#
# (c) Emanuil Rusev
# http://erusev.com
#
# For the full license information, view the LICENSE file that was distributed
# with this source code.
#
#
class ParsedownExtra extends Parsedown
{
#
# ~
function __construct()
{
$this->BlockTypes[':'] []= 'DefinitionList';
$this->DefinitionTypes['*'] []= 'Abbreviation';
# identify footnote definitions before reference definitions
array_unshift($this->DefinitionTypes['['], 'Footnote');
# identify footnote markers before before links
array_unshift($this->SpanTypes['['], 'FootnoteMarker');
}
#
# ~
function text($text)
{
$markup = parent::text($text);
# merge consecutive dl elements
$markup = preg_replace('/<\/dl>\s+<dl>\s+/', '', $markup);
# add footnotes
if (isset($this->Definitions['Footnote']))
{
$Element = $this->buildFootnoteElement();
$markup .= "\n" . $this->element($Element);
}
return $markup;
}
#
# Blocks
#
#
# Atx
protected function identifyAtx($Line)
{
$Block = parent::identifyAtx($Line);
if (preg_match('/[ ]*'.$this->attributesPattern.'[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE))
{
$attributeString = $matches[1][0];
$Block['element']['attributes'] = $this->parseAttributes($attributeString);
$Block['element']['text'] = substr($Block['element']['text'], 0, $matches[0][1]);
}
return $Block;
}
#
# Definition List
protected function identifyDefinitionList($Line, $Block)
{
if (isset($Block['type']))
{
return;
}
$Element = array(
'name' => 'dl',
'handler' => 'elements',
'text' => array(),
);
$terms = explode("\n", $Block['element']['text']);
foreach ($terms as $term)
{
$Element['text'] []= array(
'name' => 'dt',
'handler' => 'line',
'text' => $term,
);
}
$Element['text'] []= array(
'name' => 'dd',
'handler' => 'line',
'text' => ltrim($Line['text'], ' :'),
);
$Block['element'] = $Element;
return $Block;
}
protected function addToDefinitionList($Line, array $Block)
{
if ($Line['text'][0] === ':')
{
$Block['element']['text'] []= array(
'name' => 'dd',
'handler' => 'line',
'text' => ltrim($Line['text'], ' :'),
);
return $Block;
}
if ( ! isset($Block['interrupted']))
{
$Element = array_pop($Block['element']['text']);
$Element['text'] .= "\n" . chop($Line['text']);
$Block['element']['text'] []= $Element;
return $Block;
}
}
#
# Setext
protected function identifySetext($Line, array $Block = null)
{
$Block = parent::identifySetext($Line, $Block);
if (preg_match('/[ ]*'.$this->attributesPattern.'[ ]*$/', $Block['element']['text'], $matches, PREG_OFFSET_CAPTURE))
{
$attributeString = $matches[1][0];
$Block['element']['attributes'] = $this->parseAttributes($attributeString);
$Block['element']['text'] = substr($Block['element']['text'], 0, $matches[0][1]);
}
return $Block;
}
#
# Markup
protected function completeMarkup($Block)
{
$DOMDocument = new DOMDocument;
$DOMDocument->loadXML($Block['element']);
$result = $DOMDocument->documentElement->getAttribute('markdown');
if ($result !== '1')
{
return $Block;
}
$DOMDocument->documentElement->removeAttribute('markdown');
$index = 0;
$texts = array();
foreach ($DOMDocument->documentElement->childNodes as $Node)
{
if ($Node instanceof DOMText)
{
$texts [] = $this->text($Node->nodeValue);
# replaces the text of the node with a placeholder
$Node->nodeValue = '\x1A'.$index ++;
}
}
$markup = $DOMDocument->saveXML($DOMDocument->documentElement);
foreach ($texts as $index => $text)
{
$markup = str_replace('\x1A'.$index, $text, $markup);
}
$Block['element'] = $markup;
return $Block;
}
#
# Definitions
#
#
# Abbreviation
protected function identifyAbbreviation($Line)
{
if (preg_match('/^\*\[(.+?)\]:[ ]*(.+?)[ ]*$/', $Line['text'], $matches))
{
$Abbreviation = array(
'id' => $matches[1],
'data' => $matches[2],
);
return $Abbreviation;
}
}
#
# Footnote
protected function identifyFootnote($Line)
{
if (preg_match('/^\[\^(.+?)\]:[ ]?(.+)$/', $Line['text'], $matches))
{
$Footnote = array(
'id' => $matches[1],
'data' => array(
'text' => $matches[2],
'count' => null,
'number' => null,
),
);
return $Footnote;
}
}
#
# Spans
#
#
# Footnote Marker
protected function identifyFootnoteMarker($Excerpt)
{
if (preg_match('/^\[\^(.+?)\]/', $Excerpt['text'], $matches))
{
$name = $matches[1];
if ( ! isset($this->Definitions['Footnote'][$name]))
{
return;
}
$this->Definitions['Footnote'][$name]['count'] ++;
if ( ! isset($this->Definitions['Footnote'][$name]['number']))
{
$this->Definitions['Footnote'][$name]['number'] = ++ $this->footnoteCount; # » &
}
$Element = array(
'name' => 'sup',
'attributes' => array('id' => 'fnref'.$this->Definitions['Footnote'][$name]['count'].':'.$name),
'handler' => 'element',
'text' => array(
'name' => 'a',
'attributes' => array('href' => '#fn:'.$name, 'class' => 'footnote-ref'),
'text' => $this->Definitions['Footnote'][$name]['number'],
),
);
return array(
'extent' => strlen($matches[0]),
'element' => $Element,
);
}
}
private $footnoteCount = 0;
#
# Link
protected function identifyLink($Excerpt)
{
$Span = parent::identifyLink($Excerpt);
$remainder = substr($Excerpt['text'], $Span['extent']);
if (preg_match('/^[ ]*'.$this->attributesPattern.'/', $remainder, $matches))
{
$Span['element']['attributes'] += $this->parseAttributes($matches[1]);
$Span['extent'] += strlen($matches[0]);
}
return $Span;
}
#
# ~
protected function readPlainText($text)
{
$text = parent::readPlainText($text);
if (isset($this->Definitions['Abbreviation']))
{
foreach ($this->Definitions['Abbreviation'] as $abbreviation => $phrase)
{
$text = str_replace($abbreviation, '<abbr title="'.$phrase.'">'.$abbreviation.'</abbr>', $text);
}
}
return $text;
}
#
# ~
#
protected function buildFootnoteElement()
{
$Element = array(
'name' => 'div',
'attributes' => array('class' => 'footnotes'),
'handler' => 'elements',
'text' => array(
array(
'name' => 'hr',
),
array(
'name' => 'ol',
'handler' => 'elements',
'text' => array(),
),
),
);
usort($this->Definitions['Footnote'], function($A, $B) {
return $A['number'] - $B['number'];
});
foreach ($this->Definitions['Footnote'] as $name => $Data)
{
if ( ! isset($Data['number']))
{
continue;
}
$text = $Data['text'];
foreach (range(1, $Data['count']) as $number)
{
$text .= '&#160;<a href="#fnref'.$number.':'.$name.'" rev="footnote" class="footnote-backref">&#8617;</a>';
}
$Element['text'][1]['text'] []= array(
'name' => 'li',
'attributes' => array('id' => 'fn:'.$name),
'handler' => 'elements',
'text' => array(
array(
'name' => 'p',
'text' => $text,
),
),
);
}
return $Element;
}
#
# Private
#
private function parseAttributes($attributeString)
{
$Data = array();
$attributes = preg_split('/[ ]+/', $attributeString, - 1, PREG_SPLIT_NO_EMPTY);
foreach ($attributes as $attribute)
{
if ($attribute[0] === '#')
{
$Data['id'] = substr($attribute, 1);
}
else # "."
{
$classes []= substr($attribute, 1);
}
}
if (isset($classes))
{
$Data['class'] = implode(' ', $classes);
}
return $Data;
}
private $attributesPattern = '{((?:[#.][-\w]+[ ]*)+)}';
}

17
vendor/erusev/parsedown-extra/README.md vendored Normal file
View File

@@ -0,0 +1,17 @@
## Parsedown Extra
An extension of [Parsedown](http://parsedown.org) that adds support for [Markdown Extra](http://en.wikipedia.org/wiki/Markdown_Extra).
[[ demo ]](http://parsedown.org/demo?extra=1)
### Installation
Include both `Parsedown.php` and `ParsedownExtra.php` or install [the composer package](https://packagist.org/packages/erusev/parsedown-extra).
### Example
``` php
$Instance = new ParsedownExtra();
echo $Instance->text('Hello _Parsedown Extra_!'); # prints: <p>Hello <em>Parsedown Extra</em>!</p>
```

View File

@@ -13,7 +13,6 @@ Better [Markdown](http://en.wikipedia.org/wiki/Markdown) parser for PHP.
* [Tested](https://travis-ci.org/erusev/parsedown) in PHP 5.2, 5.3, 5.4, 5.5, 5.6 and [hhvm](http://www.hhvm.com/)
* Extensible
* [Markdown Extra extension](https://github.com/erusev/parsedown-extra) <sup>new</sup>
* [JavaScript port](https://github.com/hkdobrev/parsedown.js) under development <sup>new</sup>
### Installation

View File

@@ -35,7 +35,7 @@ class Cache
*/
public function __construct($cacheDirectory = 'cache')
{
$this->cacheDirectory = $cacheDirectory;
$this->cacheDirectory = $cacheDirectory;
}
/**
@@ -45,9 +45,9 @@ class Cache
*/
public function setCacheDirectory($cacheDirectory)
{
$this->cacheDirectory = $cacheDirectory;
$this->cacheDirectory = $cacheDirectory;
return $this;
return $this;
}
/**
@@ -57,7 +57,7 @@ class Cache
*/
public function getCacheDirectory()
{
return $this->cacheDirectory;
return $this->cacheDirectory;
}
/**
@@ -112,24 +112,24 @@ class Cache
*/
public function getCacheFile($filename, $actual = false, $mkdir = false)
{
$path = array();
$path = array();
// Getting the length of the filename before the extension
$parts = explode('.', $filename);
$len = strlen($parts[0]);
// Getting the length of the filename before the extension
$parts = explode('.', $filename);
$len = strlen($parts[0]);
for ($i=0; $i<min($len, $this->prefixSize); $i++) {
$path[] = $filename[$i];
for ($i=0; $i<min($len, $this->prefixSize); $i++) {
$path[] = $filename[$i];
}
$path = implode('/', $path);
$path = implode('/', $path);
$actualDir = $this->getActualCacheDirectory() . '/' . $path;
if ($mkdir && !is_dir($actualDir)) {
mkdir($actualDir, 0755, true);
}
mkdir($actualDir, 0755, true);
}
$path .= '/' . $filename;
$path .= '/' . $filename;
if ($actual) {
return $this->getActualCacheDirectory() . '/' . $path;
@@ -148,20 +148,20 @@ class Cache
{
// Implicit condition: the cache file should exist
if (!file_exists($cacheFile)) {
return false;
}
return false;
}
foreach ($conditions as $type => $value) {
switch ($type) {
case 'maxage':
foreach ($conditions as $type => $value) {
switch ($type) {
case 'maxage':
case 'max-age':
// Return false if the file is older than $value
// Return false if the file is older than $value
$age = time() - filectime($cacheFile);
if ($age > $value) {
return false;
}
break;
case 'younger-than':
break;
case 'younger-than':
case 'youngerthan':
// Return false if the file is older than the file $value, or the files $value
$check = function($filename) use ($cacheFile) {
@@ -179,13 +179,13 @@ class Cache
}
}
}
break;
default:
throw new \Exception('Cache condition '.$type.' not supported');
}
}
break;
default:
throw new \Exception('Cache condition '.$type.' not supported');
}
}
return true;
return true;
}
/**
@@ -199,7 +199,7 @@ class Cache
{
$cacheFile = $this->getCacheFile($filename, true);
return $this->checkConditions($cacheFile, $conditions);
return $this->checkConditions($cacheFile, $conditions);
}
/**
@@ -215,7 +215,7 @@ class Cache
*/
public function set($filename, $contents = '')
{
$cacheFile = $this->getCacheFile($filename, true, true);
$cacheFile = $this->getCacheFile($filename, true, true);
file_put_contents($cacheFile, $contents);
@@ -235,11 +235,11 @@ class Cache
*/
public function get($filename, array $conditions = array())
{
if ($this->exists($filename, $conditions)) {
return file_get_contents($this->getCacheFile($filename, true));
} else {
return null;
}
if ($this->exists($filename, $conditions)) {
return file_get_contents($this->getCacheFile($filename, true));
} else {
return null;
}
}
/**
@@ -259,12 +259,8 @@ class Cache
* @param $file returns the cache file or the file contents
* @param $actual returns the actual cache file
*/
public function getOrCreate($filename, array $conditions = array(), $function, $file = false, $actual = false)
public function getOrCreate($filename, array $conditions = array(), \Closure $function, $file = false, $actual = false)
{
if (!is_callable($function)) {
throw new InvalidArgumentException('The argument $function should be callable');
}
$cacheFile = $this->getCacheFile($filename, true, true);
$data = null;
@@ -272,7 +268,7 @@ class Cache
$data = file_get_contents($cacheFile);
} else {
@unlink($cacheFile);
$data = call_user_func($function, $cacheFile);
$data = $function($cacheFile);
// Test if the closure wrote the file or if it returned the data
if (!file_exists($cacheFile)) {
@@ -288,7 +284,7 @@ class Cache
/**
* Alias to getOrCreate with $file = true
*/
public function getOrCreateFile($filename, array $conditions = array(), $function, $actual = false)
public function getOrCreateFile($filename, array $conditions = array(), \Closure $function, $actual = false)
{
return $this->getOrCreate($filename, $conditions, $function, true, $actual);
}

View File

@@ -682,7 +682,7 @@ class Image
*/
public static function open($file = '')
{
return new static($file);
return new self($file);
}
/**
@@ -690,7 +690,7 @@ class Image
*/
public static function create($width, $height)
{
return new static(null, $width, $height);
return new self(null, $width, $height);
}
/**
@@ -698,7 +698,7 @@ class Image
*/
public static function fromData($data)
{
$image = new static();
$image = new self();
$image->setData($data);
return $image;
@@ -709,7 +709,7 @@ class Image
*/
public static function fromResource($resource)
{
$image = new static();
$image = new self();
$image->setResource($resource);
return $image;

138
vendor/mrclay/minify/HISTORY.txt vendored Normal file
View File

@@ -0,0 +1,138 @@
Minify Release History
Version 2.2.0
* Fix handling of RegEx in certain situations in JSMin
* Thanks to Vovan-VE for reporting this
* Update composer.json with support info
* Add ability to set ClosureCompiler URL
* Thanks Elan Ruusamäe for the pull request
* Better report of temp directory errors
* Also thanks to Elan Ruusamäe for anatoher pull request
* Updated CSSmin and added Minify_CSSmin wrapper
* Fix windows issue associated with long cache filenames
* Fix issue with web-based tool
* Fix bug in JSMin exceptions
* Fix "about:blank" bug in CSS_UriRewriter
* Cite is no longer a block element in HTML minification
* Allow for definition of custom config locations outside of the min directory
* Thanks Sam Bauers for the pull request
* Allow option for overriding the maximum byte size POST limit for ClosureCompiler and other additions
* Thanks Joscha Feth for the code
* Fixes to file-relative URL identification in UriRewriter
* Allow far-future expiration and file versioning with the "v" querystirng parameter in addition to existing method
* Lots of general code tidy ups
Version 2.1.7
* Fixes arbitrary file inclusion vulnerability on some systems
* Thanks to Matt Mecham for reporting this
Version 2.1.6
* JSMin fixes
* Prevents some Closure Compiler API failures
* Uses autoloading for all class loading
* Multiple group support in HTML Helper
* Cache adaptor for XCache
* Allow setting stack-size in YUI Compressor wrapper
* Adds jsCleanComments option to HTML minifier
* Upgrades CSSmin
* CLI script more portable
* Adds composer.json
Version 2.1.5
* Removed XSS vulnerability
* Disabled builder bby default
* command line tools to minify and rewrite URIs in CSS
* upgrade (optional) JSMin+ library
* more efficient JS minification when using CC/YUIC
* Closure Compiler uses cURL when allow_url_fopen is off
* Missing file notices when using groups
Version 2.1.4
* Option to minify JS with Closure Compiler API w/ JSMin failover
* Cookie/bookmarklet-based debug mode. No HTML editing!
* Allows 1 file to be missing w/o complete failure
* Combine multiple groups and files in single URI
* More useful HTML helpers for writing versioned URIs
* More detailed error logging, including minifier exceptions
* Builder offers more helpful messages/PHP environment warnings
* Bypass minification based on filename pattern. e.g. foo.min.js / foo-min.css
* JSMin won't choke on common Closure compiler syntaxes (i+ ++j)
* Better caching in IE6
* Cache ids are influenced by group/file names
* Debug mode for Javascript doesn't break on common XPath strings (Prototype 1.6)
* Removed annoying maxFiles limit
* mbstring.func_overload usage is safer
Version 2.1.3
* HTTP fixes
* ETag generation now valid (different when gzipped)
* Vary header always sent when Accept-Encoding is sniffed
* Cache-Control no longer has "must-revalidate" due to webkit bug
See: http://mrclay.org/index.php/2009/02/24/safari-4-beta-cache-controlmust-revalidate-bug/
* Dropped deflate encoding. Browser and proxy support could be buggy.
See: http://stackoverflow.com/questions/883841/
* File cache now works w/o setting $min_cachePath
* Allow setting contentType in Minify_Source objects
* No more 5.3 deprecation warnings: split() removed
Version 2.1.2
* Javascript fixes
* Debug mode no longer confused by "*/*" in strings/RegExps (jQuery)
* quote characters inside RegExp literals no longer cause exception
* files ending in single-line comments no longer cause code loss
* CSS: data: URLs no longer mangled
* Optional error logging to Firefox's FirePHP extension
* Unit tests to check for common DOCUMENT_ROOT problems
* DOCUMENT_ROOT no longer overwritten on IIS servers
* Builder app doesn't fail on systems without gzdeflate()
* APC caching class included
Version 2.1.1
* Bug fix release
* Detection and workarounds for zlib.output_compression and non-PHP encoding modules
* Zlib not required (mod_rewrite, et.al., can still be used for encoding)
* HTML : More IE conditional comments preserved
* Minify_groupUri() utility fixed
Version 2.1.0
* "min" default application for quick deployment
* Minify URI Builder app & bookmarklet for quickly creating minify URIs
* Relative URIs in CSS file are fixed automatically by default
* "debug" mode for revealing original line #s in combined files
* Better IIS support
* Improved minifier classes:
* JS: preserves IE conditional comments
* CSS: smaller output, preserves more hacks and valid CSS syntax,
shorter line lengths, other bug fixes
* HTML: smaller output, shorter line lengths, other bug fixes
* Default Cache-Control: max-age of 30 minutes
* Conditional GETs supported even when max-age sent
* Experimental memcache cache class (default is files)
* Minify_Cache_File has flock()s (by default)
* Workaround for Windows mtime reporting bug
Version 2.0.2 beta (2008-06-24)
* Fast new cache system. Cached files served almost 3x as fast.
* Dropped support of compress encoding (though HTTP_Encoder still supports it)
Version 2.0.1 (2008-05-31)
* E_STRICT compliance (Cache_Lite_File).
Version 2.0.0 (2008-05-22)
* Complete code overhaul. Minify is now a PEAR-style class and toolkit
for building customized minifying file servers.
* Content-Encoding: deflate/gzip/compress, based on request headers
* Expanded CSS and HTML minifiers with test cases
* Easily plug-in 3rd-party minifiers (like Packer)
* Plug-able front end controller allows changing the way files are chosen
* Compression & encoding modules lazy-loaded as needed (304 responses use
use minimal code)
* Separate utility classes for HTTP encoding and cache control
Version 1.0.1 (2007-05-05)
* Fixed various problems resolving pathnames when hosted on an NFS mount.
* Fixed 'undefined constant' notice.
* Replaced old JSMin library with a much faster custom implementation.
Version 1.0.0 (2007-05-02)
* First release.

26
vendor/mrclay/minify/LICENSE.txt vendored Normal file
View File

@@ -0,0 +1,26 @@
Copyright (c) 2008 Ryan Grove <ryan@wonko.com>
Copyright (c) 2008 Steve Clay <steve@mrclay.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of this project nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

181
vendor/mrclay/minify/MIN.txt vendored Normal file
View File

@@ -0,0 +1,181 @@
The files in the /min/ directory represent the default Minify setup designed to ease
integration with your site. This app will combine and minify your Javascript or
CSS files and serve them with HTTP compression and cache headers.
RECOMMENDED
It's recommended to edit /min/config.php to set $min_cachePath to a writeable
(by PHP) directory on your system. This will improve performance.
GETTING STARTED
The quickest way to get started is to use the Minify URI Builder application
on your website: http://example.com/min/builder/
MINIFYING A SINGLE FILE
Let's say you want to serve this file:
http://example.com/wp-content/themes/default/default.css
Here's the "Minify URL" for this file:
http://example.com/min/?f=wp-content/themes/default/default.css
In other words, the "f" argument is set to the file path from root without the
initial "/". As CSS files may contain relative URIs, Minify will automatically
"fix" these by rewriting them as root relative.
COMBINING MULTIPLE FILES IN ONE DOWNLOAD
Separate the paths given to "f" with commas.
Let's say you have CSS files at these URLs:
http://example.com/scripts/jquery-1.2.6.js
http://example.com/scripts/site.js
You can combine these files through Minify by requesting this URL:
http://example.com/min/?f=scripts/jquery-1.2.6.js,scripts/site.js
SIMPLIFYING URLS WITH A BASE PATH
If you're combining files that share the same ancestor directory, you can use
the "b" argument to set the base directory for the "f" argument. Do not include
the leading or trailing "/" characters.
E.g., the following URLs will serve the exact same content:
http://example.com/min/?f=scripts/jquery-1.2.6.js,scripts/site.js,scripts/home.js
http://example.com/min/?b=scripts&f=jquery-1.2.6.js,site.js,home.js
MINIFY URLS IN HTML
In HTML files, don't forget to replace any "&" characters with "&amp;".
SPECIFYING ALLOWED DIRECTORIES
By default, Minify will serve any *.css/*.js files within the DOCUMENT_ROOT. If
you'd prefer to limit Minify's access to certain directories, set the
$min_serveOptions['minApp']['allowDirs'] array in config.php. E.g. to limit
to the /js and /themes/default directories, use:
$min_serveOptions['minApp']['allowDirs'] = array('//js', '//themes/default');
GROUPS: NICER URLS
For nicer URLs, edit groupsConfig.php to pre-specify groups of files
to be combined under preset keys. E.g., here's an example configuration in
groupsConfig.php:
return array(
'js' => array('//js/Class.js', '//js/email.js')
);
This pre-selects the following files to be combined under the key "js":
http://example.com/js/Class.js
http://example.com/js/email.js
You can now serve these files with this simple URL:
http://example.com/min/?g=js
GROUPS: SPECIFYING FILES OUTSIDE THE DOC_ROOT
In the groupsConfig.php array, the "//" in the file paths is a shortcut for
the DOCUMENT_ROOT, but you can also specify paths from the root of the filesystem
or relative to the DOC_ROOT:
return array(
'js' => array(
'//js/file.js' // file within DOC_ROOT
,'//../file.js' // file in parent directory of DOC_ROOT
,'C:/Users/Steve/file.js' // file anywhere on filesystem
)
);
COMBINE MULTIPLE GROUPS AND FILES IN ONE URL
E.g.: http://example.com/min/?g=js&f=more/scripts.js
Separate group keys with commas:
http://example.com/min/?g=baseCss,css1&f=moreStyles.css
FAR-FUTURE EXPIRES HEADERS
Minify can send far-future (one year) Expires headers. To enable this you must
add a number or the parameter "v" to the querystring (e.g. /min/?g=js&1234 or
/min/?g=js&v=1234) and alter it whenever a source file is changed. If you have a
build process you can use a build/source control revision number.
You can alternately use the utility function Minify_getUri() to get a "versioned"
Minify URI for use in your HTML. E.g.:
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/min/utils.php';
$jsUri = Minify_getUri('js'); // a key in groupsConfig.php
echo "<script src='{$jsUri}'></script>";
$cssUri = Minify_getUri(array(
'//css/styles1.css'
,'//css/styles2.css'
)); // a list of files
echo "<link rel=stylesheet href='{$cssUri}'>";
STORING CONFIG FILES OUTSIDE THE MINIFY DIRECTORY
It is possible to store config files (min/config.php, min/config-test.php,
min/groupsConfig.php) in a custom directory outside the Minify directory. This is
useful if you wish to include Minify as an external dependency inside another
project via SVN external or Git submodule inclusion.
For example, let's assume you have a Minify directory "min" in your site root. Then
you could create a new directory called "min-configs" in the site root. Copy any
config files you wish to modify to "min-configs", and modify as desired.
Then create a new file, for example "min.php" in your site root. The contents of
this file could look like this:
<?php
$customConfigDirectory = dirname(__FILE__) . '/min-configs';
$min_customConfigPaths = array(
'base' => $customConfigDirectory . '/config.php',
'test' => $customConfigDirectory . '/config-test.php',
'groups' => $customConfigDirectory . '/groupsConfig.php'
);
include_once 'min/index.php';
You would then reference min.php in your JS and CSS links instead of min/index.php.
This method will affect those using the Minify_getUri() function. You will need
to add options to calls to that function, e.g.:
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/min/utils.php';
$jsUri = Minify_getUri('//js/file.js', array('minAppUri' => '/min.php'));
echo "<script src='{$jsUri}'></script>";
DEBUG MODE
In debug mode, instead of compressing files, Minify sends combined files with
comments prepended to each line to show the line number in the original source
file. To enable this, set $min_allowDebugFlag to true in config.php and append
"&debug=1" to your URIs. E.g. /min/?f=script1.js,script2.js&debug=1
Known issue: files with comment-like strings/regexps can cause problems in this mode.
QUESTIONS?
http://groups.google.com/group/minify

68
vendor/mrclay/minify/README.txt vendored Normal file
View File

@@ -0,0 +1,68 @@
WELCOME TO MINIFY!
Minify is an HTTP content server. It compresses sources of content
(usually files), combines the result and serves it with appropriate
HTTP headers. These headers can allow clients to perform conditional
GETs (serving content only when clients do not have a valid cache)
and tell clients to cache the file for a period of time.
More info: http://code.google.com/p/minify/
WORDPRESS USER?
These WP plugins integrate Minify into WordPress's style and script hooks to
get you set up faster.
http://wordpress.org/extend/plugins/bwp-minify/
http://wordpress.org/extend/plugins/w3-total-cache/
INSTALLATION
Place the /min/ directory as a child of your DOCUMENT_ROOT
directory: i.e. you will have: /home/example/www/min
You can see verify that it is working by visiting these two URLs:
http://example.org/min/?f=min/quick-test.js
http://example.org/min/?f=min/quick-test.css
If your server supports mod_rewrite, this URL should also work:
http://example.org/min/f=min/quick-test.js
CONFIGURATION & USAGE
See the MIN.txt file and http://code.google.com/p/minify/wiki/UserGuide
Minify also comes with a URI Builder application that can help you write URLs
for use with Minify or configure groups of files. See here for details:
http://code.google.com/p/minify/wiki/BuilderApp
The cookbook also provides some more advanced options for minification:
http://code.google.com/p/minify/wiki/CookBook
UPGRADING
See UPGRADING.txt for instructions.
UNIT TESTING:
1. Place the /min_unit_tests/ directory as a child of your DOCUMENT_ROOT
directory: i.e. you will have: /home/example/www/min_unit_tests
2. To run unit tests, access: http://example.org/min_unit_tests/test_all.php
(If you wish, the other test_*.php files can be run to test individual
components with more verbose output.)
3. Remove /min_unit_tests/ from your DOCUMENT_ROOT when you are done.
FILE ENCODINGS
Minify *should* work fine with files encoded in UTF-8 or other 8-bit
encodings like ISO 8859/Windows-1252. By default Minify appends
";charset=utf-8" to the Content-Type headers it sends.
Leading UTF-8 BOMs are stripped from all sources to prevent
duplication in output files, and files are converted to Unix newlines.

28
vendor/mrclay/minify/UPGRADING.txt vendored Normal file
View File

@@ -0,0 +1,28 @@
Minify Upgrade Guide
UPGRADING FROM 2.1.*
1. Rename the following files:
/min/config.php --> /min/old_config.php
/min/groupsConfig.php --> /min/old_groupsConfig.php
2. Overwrite all files in /min (and /min_unit_tests) with those from this zip.
3. Delete /min/groupsConfig.php
4. Rename /min/old_groupsConfig.php --> /min/groupsConfig.php
5. Merge your settings in old_config.php into config.php.
6. (optional) Delete /min/old_config.php.
INSTALLING FRESH
See README.txt for instructions on installing this app for the first time.
SUPPORT
Send a message to http://groups.google.com/group/minify

775
vendor/mrclay/minify/min/lib/CSSmin.php vendored Normal file
View File

@@ -0,0 +1,775 @@
<?php
/*!
* cssmin.php 2.4.8-2
* Author: Tubal Martin - http://tubalmartin.me/
* Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
*
* This is a PHP port of the CSS minification tool distributed with YUICompressor,
* itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
* Permission is hereby granted to use the PHP version under the same
* conditions as the YUICompressor.
*/
/*!
* YUI Compressor
* http://developer.yahoo.com/yui/compressor/
* Author: Julien Lecomte - http://www.julienlecomte.net/
* Copyright (c) 2013 Yahoo! Inc. All rights reserved.
* The copyrights embodied in the content of this file are licensed
* by Yahoo! Inc. under the BSD (revised) open source license.
*/
class CSSmin
{
const NL = '___YUICSSMIN_PRESERVED_NL___';
const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
private $comments;
private $preserved_tokens;
private $memory_limit;
private $max_execution_time;
private $pcre_backtrack_limit;
private $pcre_recursion_limit;
private $raise_php_limits;
/**
* @param bool|int $raise_php_limits
* If true, PHP settings will be raised if needed
*/
public function __construct($raise_php_limits = TRUE)
{
// Set suggested PHP limits
$this->memory_limit = 128 * 1048576; // 128MB in bytes
$this->max_execution_time = 60; // 1 min
$this->pcre_backtrack_limit = 1000 * 1000;
$this->pcre_recursion_limit = 500 * 1000;
$this->raise_php_limits = (bool) $raise_php_limits;
}
/**
* Minify a string of CSS
* @param string $css
* @param int|bool $linebreak_pos
* @return string
*/
public function run($css = '', $linebreak_pos = FALSE)
{
if (empty($css)) {
return '';
}
if ($this->raise_php_limits) {
$this->do_raise_php_limits();
}
$this->comments = array();
$this->preserved_tokens = array();
$start_index = 0;
$length = strlen($css);
$css = $this->extract_data_urls($css);
// collect all comment blocks...
while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
$end_index = $this->index_of($css, '*/', $start_index + 2);
if ($end_index < 0) {
$end_index = $length;
}
$comment_found = $this->str_slice($css, $start_index + 2, $end_index);
$this->comments[] = $comment_found;
$comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
$css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
// Set correct start_index: Fixes issue #2528130
$start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
}
// preserve strings so their content doesn't get accidentally minified
$css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
// Let's divide css code in chunks of 5.000 chars aprox.
// Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
// of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
// long strings and a (sub)pattern matches a number of chars greater than
// the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
// returning NULL and $css would be empty.
$charset = '';
$charset_regexp = '/(@charset)( [^;]+;)/i';
$css_chunks = array();
$css_chunk_length = 5000; // aprox size, not exact
$start_index = 0;
$i = $css_chunk_length; // save initial iterations
$l = strlen($css);
// if the number of characters is 25000 or less, do not chunk
if ($l <= $css_chunk_length) {
$css_chunks[] = $css;
} else {
// chunk css code securely
while ($i < $l) {
$i += 50; // save iterations
if ($l - $start_index <= $css_chunk_length || $i >= $l) {
$css_chunks[] = $this->str_slice($css, $start_index);
break;
}
if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
// If there are two ending curly braces }} separated or not by spaces,
// join them in the same chunk (i.e. @media blocks)
$next_chunk = substr($css, $i);
if (preg_match('/^\s*\}/', $next_chunk)) {
$i = $i + $this->index_of($next_chunk, '}') + 1;
}
$css_chunks[] = $this->str_slice($css, $start_index, $i);
$start_index = $i;
}
}
}
// Minify each chunk
for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
$css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
// Keep the first @charset at-rule found
if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
$charset = strtolower($matches[1]) . $matches[2];
}
// Delete all @charset at-rules
$css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
}
// Update the first chunk and push the charset to the top of the file.
$css_chunks[0] = $charset . $css_chunks[0];
return implode('', $css_chunks);
}
/**
* Sets the memory limit for this script
* @param int|string $limit
*/
public function set_memory_limit($limit)
{
$this->memory_limit = $this->normalize_int($limit);
}
/**
* Sets the maximum execution time for this script
* @param int|string $seconds
*/
public function set_max_execution_time($seconds)
{
$this->max_execution_time = (int) $seconds;
}
/**
* Sets the PCRE backtrack limit for this script
* @param int $limit
*/
public function set_pcre_backtrack_limit($limit)
{
$this->pcre_backtrack_limit = (int) $limit;
}
/**
* Sets the PCRE recursion limit for this script
* @param int $limit
*/
public function set_pcre_recursion_limit($limit)
{
$this->pcre_recursion_limit = (int) $limit;
}
/**
* Try to configure PHP to use at least the suggested minimum settings
*/
private function do_raise_php_limits()
{
$php_limits = array(
'memory_limit' => $this->memory_limit,
'max_execution_time' => $this->max_execution_time,
'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
'pcre.recursion_limit' => $this->pcre_recursion_limit
);
// If current settings are higher respect them.
foreach ($php_limits as $name => $suggested) {
$current = $this->normalize_int(ini_get($name));
// memory_limit exception: allow -1 for "no memory limit".
if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
ini_set($name, $suggested);
}
}
}
/**
* Does bulk of the minification
* @param string $css
* @param int|bool $linebreak_pos
* @return string
*/
private function minify($css, $linebreak_pos)
{
// strings are safe, now wrestle the comments
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
$token = $this->comments[$i];
$placeholder = '/' . self::COMMENT . $i . '___/';
// ! in the first position of the comment means preserve
// so push to the preserved tokens keeping the !
if (substr($token, 0, 1) === '!') {
$this->preserved_tokens[] = $token;
$token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
$css = preg_replace($placeholder, $token_tring, $css, 1);
// Preserve new lines for /*! important comments
$css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
$css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
continue;
}
// \ in the last position looks like hack for Mac/IE5
// shorten that to /*\*/ and the next one to /**/
if (substr($token, (strlen($token) - 1), 1) === '\\') {
$this->preserved_tokens[] = '\\';
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
$i = $i + 1; // attn: advancing the loop
$this->preserved_tokens[] = '';
$css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
continue;
}
// keep empty comments after child selectors (IE7 hack)
// e.g. html >/**/ body
if (strlen($token) === 0) {
$start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
if ($start_index > 2) {
if (substr($css, $start_index - 3, 1) === '>') {
$this->preserved_tokens[] = '';
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
}
}
}
// in all other cases kill the comment
$css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
}
// Normalize all whitespace strings to single spaces. Easier to work with that way.
$css = preg_replace('/\s+/', ' ', $css);
// Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
$css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);
// Shorten & preserve calculations calc(...) since spaces are important
$css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
// Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
// +1.2em to 1.2em, +.8px to .8px, +2% to 2%
$css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
// Remove leading zeros from integer and float numbers preceded by : or a white-space
// 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
// Remove trailing zeros from float numbers preceded by : or a white-space
// -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
// Remove trailing .0 -> -9.0 to -9
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
// Replace 0 length numbers with 0
$css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
// Remove the spaces before the things that should not have spaces before them.
// But, be careful not to turn "p :link {...}" into "p:link{...}"
// Swap out any pseudo-class colons with the token, and then swap back.
$css = preg_replace_callback('/(?:^|\})(?:(?:[^\{\:])+\:)+(?:[^\{]*\{)/', array($this, 'replace_colon'), $css);
// Remove spaces before the things that should not have spaces before them.
$css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
// Restore spaces for !important
$css = preg_replace('/\!important/i', ' !important', $css);
// bring back the colon
$css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
// retain space for special IE6 cases
$css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
// no space after the end of a preserved comment
$css = preg_replace('/\*\/ /', '*/', $css);
// lowercase some popular @directives
$css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
// lowercase some more common pseudo-elements
$css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css);
// lowercase some more common functions
$css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css);
// lower case some common function that can be values
// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
$css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);
// Put the space back in some cases, to support stuff like
// @media screen and (-webkit-min-device-pixel-ratio:0){
$css = preg_replace('/\band\(/i', 'and (', $css);
// Remove the spaces after the things that should not have spaces after them.
$css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
// remove unnecessary semicolons
$css = preg_replace('/;+\}/', '}', $css);
// Fix for issue: #2528146
// Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
// to avoid issues on Symbian S60 3.x browsers.
$css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
// Replace 0 length units 0(px,em,%) with 0.
$css = preg_replace('/(^|[^0-9])(?:0?\.)?0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%|deg|g?rad|m?s|k?hz)/iS', '${1}0', $css);
// 0% step in a keyframe? restore the % unit
$css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]*?\{)(.*?\}\s*\})/iS', array($this, 'replace_keyframe_zero'), $css);
// Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
$css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
// Fix for issue: #2528142
// Replace text-shadow:0; with text-shadow:0 0 0;
$css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
// Replace background-position:0; with background-position:0 0;
// same for transform-origin
// Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
$css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
// Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
// Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
// This makes it more likely that it'll get further compressed in the next step.
$css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
$css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
// Shorten colors from #AABBCC to #ABC or short color name.
$css = $this->compress_hex_colors($css);
// border: none to border:0, outline: none to outline:0
$css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
// shorter opacity IE filter
$css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
// Find a fraction that is used for Opera's -o-device-pixel-ratio query
// Add token to add the "\" back in later
$css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
// Remove empty rules.
$css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
// Add "/" back to fix Opera -o-device-pixel-ratio query
$css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
// Replace multiple semi-colons in a row by a single one
// See SF bug #1980989
$css = preg_replace('/;;+/', ';', $css);
// Restore new lines for /*! important comments
$css = preg_replace('/'. self::NL .'/', "\n", $css);
// Lowercase all uppercase properties
$css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
// Some source control tools don't like it when files containing lines longer
// than, say 8000 characters, are checked in. The linebreak option is used in
// that case to split long lines after a specific column.
if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
$linebreak_pos = (int) $linebreak_pos;
$start_index = $i = 0;
while ($i < strlen($css)) {
$i++;
if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
$css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
$start_index = $i;
}
}
}
// restore preserved comments and strings in reverse order
for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
$css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
}
// Trim the final string (for any leading or trailing white spaces)
return trim($css);
}
/**
* Utility method to replace all data urls with tokens before we start
* compressing, to avoid performance issues running some of the subsequent
* regexes against large strings chunks.
*
* @param string $css
* @return string
*/
private function extract_data_urls($css)
{
// Leave data urls alone to increase parse performance.
$max_index = strlen($css) - 1;
$append_index = $index = $last_index = $offset = 0;
$sb = array();
$pattern = '/url\(\s*(["\']?)data\:/i';
// Since we need to account for non-base64 data urls, we need to handle
// ' and ) being part of the data string. Hence switching to indexOf,
// to determine whether or not we have matching string terminators and
// handling sb appends directly, instead of using matcher.append* methods.
while (preg_match($pattern, $css, $m, 0, $offset)) {
$index = $this->index_of($css, $m[0], $offset);
$last_index = $index + strlen($m[0]);
$start_index = $index + 4; // "url(".length()
$end_index = $last_index - 1;
$terminator = $m[1]; // ', " or empty (not quoted)
$found_terminator = FALSE;
if (strlen($terminator) === 0) {
$terminator = ')';
}
while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
$end_index = $this->index_of($css, $terminator, $end_index + 1);
// endIndex == 0 doesn't really apply here
if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
$found_terminator = TRUE;
if (')' != $terminator) {
$end_index = $this->index_of($css, ')', $end_index);
}
}
}
// Enough searching, start moving stuff over to the buffer
$sb[] = $this->str_slice($css, $append_index, $index);
if ($found_terminator) {
$token = $this->str_slice($css, $start_index, $end_index);
$token = preg_replace('/\s+/', '', $token);
$this->preserved_tokens[] = $token;
$preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
$sb[] = $preserver;
$append_index = $end_index + 1;
} else {
// No end terminator found, re-add the whole match. Should we throw/warn here?
$sb[] = $this->str_slice($css, $index, $last_index);
$append_index = $last_index;
}
$offset = $last_index;
}
$sb[] = $this->str_slice($css, $append_index);
return implode('', $sb);
}
/**
* Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
*
* DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
* e.g. #AddressForm { ... }
*
* DOES NOT compress IE filters, which have hex color values (which would break things).
* e.g. filter: chroma(color="#FFFFFF");
*
* DOES NOT compress invalid hex values.
* e.g. background-color: #aabbccdd
*
* @param string $css
* @return string
*/
private function compress_hex_colors($css)
{
// Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
$pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
$_index = $index = $last_index = $offset = 0;
$sb = array();
// See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
$short_safe = array(
'#808080' => 'gray',
'#008000' => 'green',
'#800000' => 'maroon',
'#000080' => 'navy',
'#808000' => 'olive',
'#ffa500' => 'orange',
'#800080' => 'purple',
'#c0c0c0' => 'silver',
'#008080' => 'teal',
'#f00' => 'red'
);
while (preg_match($pattern, $css, $m, 0, $offset)) {
$index = $this->index_of($css, $m[0], $offset);
$last_index = $index + strlen($m[0]);
$is_filter = $m[1] !== null && $m[1] !== '';
$sb[] = $this->str_slice($css, $_index, $index);
if ($is_filter) {
// Restore, maintain case, otherwise filter will break
$sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
} else {
if (strtolower($m[2]) == strtolower($m[3]) &&
strtolower($m[4]) == strtolower($m[5]) &&
strtolower($m[6]) == strtolower($m[7])) {
// Compress.
$hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
} else {
// Non compressible color, restore but lower case.
$hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
}
// replace Hex colors to short safe color names
$sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
}
$_index = $offset = $last_index - strlen($m[8]);
}
$sb[] = $this->str_slice($css, $_index);
return implode('', $sb);
}
/* CALLBACKS
* ---------------------------------------------------------------------------------------------
*/
private function replace_string($matches)
{
$match = $matches[0];
$quote = substr($match, 0, 1);
// Must use addcslashes in PHP to avoid parsing of backslashes
$match = addcslashes($this->str_slice($match, 1, -1), '\\');
// maybe the string contains a comment-like substring?
// one, maybe more? put'em back then
if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
$match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
}
}
// minify alpha opacity in filter strings
$match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
$this->preserved_tokens[] = $match;
return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
}
private function replace_colon($matches)
{
return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
}
private function replace_calc($matches)
{
$this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]));
return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
}
private function preserve_old_IE_specific_matrix_definition($matches)
{
$this->preserved_tokens[] = $matches[1];
return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
}
private function replace_keyframe_zero($matches)
{
return $matches[1] . preg_replace('/0\s*,/', '0%,', preg_replace('/\s*0\s*\{/', '0%{', $matches[2]));
}
private function rgb_to_hex($matches)
{
// Support for percentage values rgb(100%, 0%, 45%);
if ($this->index_of($matches[1], '%') >= 0){
$rgbcolors = explode(',', str_replace('%', '', $matches[1]));
for ($i = 0; $i < count($rgbcolors); $i++) {
$rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
}
} else {
$rgbcolors = explode(',', $matches[1]);
}
// Values outside the sRGB color space should be clipped (0-255)
for ($i = 0; $i < count($rgbcolors); $i++) {
$rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
$rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
}
// Fix for issue #2528093
if (!preg_match('/[\s\,\);\}]/', $matches[2])){
$matches[2] = ' ' . $matches[2];
}
return '#' . implode('', $rgbcolors) . $matches[2];
}
private function hsl_to_hex($matches)
{
$values = explode(',', str_replace('%', '', $matches[1]));
$h = floatval($values[0]);
$s = floatval($values[1]);
$l = floatval($values[2]);
// Wrap and clamp, then fraction!
$h = ((($h % 360) + 360) % 360) / 360;
$s = $this->clamp_number($s, 0, 100) / 100;
$l = $this->clamp_number($l, 0, 100) / 100;
if ($s == 0) {
$r = $g = $b = $this->round_number(255 * $l);
} else {
$v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
$v1 = (2 * $l) - $v2;
$r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
$g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
$b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
}
return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
}
private function lowercase_pseudo_first($matches)
{
return ':first-'. strtolower($matches[1]) .' '. $matches[2];
}
private function lowercase_directives($matches)
{
return '@'. strtolower($matches[1]);
}
private function lowercase_pseudo_elements($matches)
{
return ':'. strtolower($matches[1]);
}
private function lowercase_common_functions($matches)
{
return ':'. strtolower($matches[1]) .'(';
}
private function lowercase_common_functions_values($matches)
{
return $matches[1] . strtolower($matches[2]);
}
private function lowercase_properties($matches)
{
return $matches[1].strtolower($matches[2]).$matches[3];
}
/* HELPERS
* ---------------------------------------------------------------------------------------------
*/
private function hue_to_rgb($v1, $v2, $vh)
{
$vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
if ($vh * 2 < 1) return $v2;
if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
return $v1;
}
private function round_number($n)
{
return intval(floor(floatval($n) + 0.5), 10);
}
private function clamp_number($n, $min, $max)
{
return min(max($n, $min), $max);
}
/**
* PHP port of Javascript's "indexOf" function for strings only
* Author: Tubal Martin http://blog.margenn.com
*
* @param string $haystack
* @param string $needle
* @param int $offset index (optional)
* @return int
*/
private function index_of($haystack, $needle, $offset = 0)
{
$index = strpos($haystack, $needle, $offset);
return ($index !== FALSE) ? $index : -1;
}
/**
* PHP port of Javascript's "slice" function for strings only
* Author: Tubal Martin http://blog.margenn.com
* Tests: http://margenn.com/tubal/str_slice/
*
* @param string $str
* @param int $start index
* @param int|bool $end index (optional)
* @return string
*/
private function str_slice($str, $start = 0, $end = FALSE)
{
if ($end !== FALSE && ($start < 0 || $end <= 0)) {
$max = strlen($str);
if ($start < 0) {
if (($start = $max + $start) < 0) {
return '';
}
}
if ($end < 0) {
if (($end = $max + $end) < 0) {
return '';
}
}
if ($end <= $start) {
return '';
}
}
$slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
return ($slice === FALSE) ? '' : $slice;
}
/**
* Convert strings like "64M" or "30" to int values
* @param mixed $size
* @return int
*/
private function normalize_int($size)
{
if (is_string($size)) {
switch (substr($size, -1)) {
case 'M': case 'm': return $size * 1048576;
case 'K': case 'k': return $size * 1024;
case 'G': case 'g': return $size * 1073741824;
}
}
return (int) $size;
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* DooDigestAuth class file.
*
* @author Leng Sheng Hong <darkredz@gmail.com>
* @link http://www.doophp.com/
* @copyright Copyright &copy; 2009 Leng Sheng Hong
* @license http://www.doophp.com/license
*/
/**
* Handles HTTP digest authentication
*
* <p>HTTP digest authentication can be used with the URI router.
* HTTP digest is much more recommended over the use of HTTP Basic auth which doesn't provide any encryption.
* If you are running PHP on Apache in CGI/FastCGI mode, you would need to
* add the following line to your .htaccess for digest auth to work correctly.</p>
* <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code>
*
* <p>This class is tested under Apache 2.2 and Cherokee web server. It should work in both mod_php and cgi mode.</p>
*
* @author Leng Sheng Hong <darkredz@gmail.com>
* @version $Id: DooDigestAuth.php 1000 2009-07-7 18:27:22
* @package doo.auth
* @since 1.0
*/
class DooDigestAuth{
/**
* Authenticate against a list of username and passwords.
*
* <p>HTTP Digest Authentication doesn't work with PHP in CGI mode,
* you have to add this into your .htaccess <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code></p>
*
* @param string $realm Name of the authentication session
* @param array $users An assoc array of username and password: array('uname1'=>'pwd1', 'uname2'=>'pwd2')
* @param string $fail_msg Message to be displayed if the User cancel the login
* @param string $fail_url URL to be redirect if the User cancel the login
* @return string The username if login success.
*/
public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL){
$realm = "Restricted area - $realm";
//user => password
//$users = array('admin' => '1234', 'guest' => 'guest');
if(!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Digest')===0){
$_SERVER['PHP_AUTH_DIGEST'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
}
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
header('HTTP/1.1 401 Unauthorized');
if($fail_msg!=NULL)
die($fail_msg);
if($fail_url!=NULL)
die("<script>window.location.href = '$fail_url'</script>");
exit;
}
// analyze the PHP_AUTH_DIGEST variable
if (!($data = self::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($users[$data['username']])){
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
header('HTTP/1.1 401 Unauthorized');
if($fail_msg!=NULL)
die($fail_msg);
if($fail_url!=NULL)
die("<script>window.location.href = '$fail_url'</script>");
exit;
}
// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if ($data['response'] != $valid_response){
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
if($fail_msg!=NULL)
die($fail_msg);
if($fail_url!=NULL)
die("<script>window.location.href = '$fail_url'</script>");
exit;
}
// ok, valid username & password
return $data['username'];
}
/**
* Method to parse the http auth header, works with IE.
*
* Internet Explorer returns a qop="xxxxxxxxxxx" in the header instead of qop=xxxxxxxxxxx as most browsers do.
*
* @param string $txt header string to parse
* @return array An assoc array of the digest auth session
*/
private static function http_digest_parse($txt)
{
$res = preg_match("/username=\"([^\"]+)\"/i", $txt, $match);
$data['username'] = (isset($match[1]))?$match[1]:null;
$res = preg_match('/nonce=\"([^\"]+)\"/i', $txt, $match);
$data['nonce'] = $match[1];
$res = preg_match('/nc=([0-9]+)/i', $txt, $match);
$data['nc'] = $match[1];
$res = preg_match('/cnonce=\"([^\"]+)\"/i', $txt, $match);
$data['cnonce'] = $match[1];
$res = preg_match('/qop=([^,]+)/i', $txt, $match);
$data['qop'] = str_replace('"','',$match[1]);
$res = preg_match('/uri=\"([^\"]+)\"/i', $txt, $match);
$data['uri'] = $match[1];
$res = preg_match('/response=\"([^\"]+)\"/i', $txt, $match);
$data['response'] = $match[1];
return $data;
}
}

1370
vendor/mrclay/minify/min/lib/FirePHP.php vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,366 @@
<?php
/**
* Class HTTP_ConditionalGet
* @package Minify
* @subpackage HTTP
*/
/**
* Implement conditional GET via a timestamp or hash of content
*
* E.g. Content from DB with update time:
* <code>
* list($updateTime, $content) = getDbUpdateAndContent();
* $cg = new HTTP_ConditionalGet(array(
* 'lastModifiedTime' => $updateTime
* ,'isPublic' => true
* ));
* $cg->sendHeaders();
* if ($cg->cacheIsValid) {
* exit();
* }
* echo $content;
* </code>
*
* E.g. Shortcut for the above
* <code>
* HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache
* echo $content;
* </code>
*
* E.g. Content from DB with no update time:
* <code>
* $content = getContentFromDB();
* $cg = new HTTP_ConditionalGet(array(
* 'contentHash' => md5($content)
* ));
* $cg->sendHeaders();
* if ($cg->cacheIsValid) {
* exit();
* }
* echo $content;
* </code>
*
* E.g. Static content with some static includes:
* <code>
* // before content
* $cg = new HTTP_ConditionalGet(array(
* 'lastUpdateTime' => max(
* filemtime(__FILE__)
* ,filemtime('/path/to/header.inc')
* ,filemtime('/path/to/footer.inc')
* )
* ));
* $cg->sendHeaders();
* if ($cg->cacheIsValid) {
* exit();
* }
* </code>
* @package Minify
* @subpackage HTTP
* @author Stephen Clay <steve@mrclay.org>
*/
class HTTP_ConditionalGet {
/**
* Does the client have a valid copy of the requested resource?
*
* You'll want to check this after instantiating the object. If true, do
* not send content, just call sendHeaders() if you haven't already.
*
* @var bool
*/
public $cacheIsValid = null;
/**
* @param array $spec options
*
* 'isPublic': (bool) if false, the Cache-Control header will contain
* "private", allowing only browser caching. (default false)
*
* 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers
* will be sent with content. This is recommended.
*
* 'encoding': (string) if set, the header "Vary: Accept-Encoding" will
* always be sent and a truncated version of the encoding will be appended
* to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient
* checking of the client's If-None-Match header, as the encoding portion of
* the ETag will be stripped before comparison.
*
* 'contentHash': (string) if given, only the ETag header can be sent with
* content (only HTTP1.1 clients can conditionally GET). The given string
* should be short with no quote characters and always change when the
* resource changes (recommend md5()). This is not needed/used if
* lastModifiedTime is given.
*
* 'eTag': (string) if given, this will be used as the ETag header rather
* than values based on lastModifiedTime or contentHash. Also the encoding
* string will not be appended to the given value as described above.
*
* 'invalidate': (bool) if true, the client cache will be considered invalid
* without testing. Effectively this disables conditional GET.
* (default false)
*
* 'maxAge': (int) if given, this will set the Cache-Control max-age in
* seconds, and also set the Expires header to the equivalent GMT date.
* After the max-age period has passed, the browser will again send a
* conditional GET to revalidate its cache.
*/
public function __construct($spec)
{
$scope = (isset($spec['isPublic']) && $spec['isPublic'])
? 'public'
: 'private';
$maxAge = 0;
// backwards compatibility (can be removed later)
if (isset($spec['setExpires'])
&& is_numeric($spec['setExpires'])
&& ! isset($spec['maxAge'])) {
$spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME'];
}
if (isset($spec['maxAge'])) {
$maxAge = $spec['maxAge'];
$this->_headers['Expires'] = self::gmtDate(
$_SERVER['REQUEST_TIME'] + $spec['maxAge']
);
}
$etagAppend = '';
if (isset($spec['encoding'])) {
$this->_stripEtag = true;
$this->_headers['Vary'] = 'Accept-Encoding';
if ('' !== $spec['encoding']) {
if (0 === strpos($spec['encoding'], 'x-')) {
$spec['encoding'] = substr($spec['encoding'], 2);
}
$etagAppend = ';' . substr($spec['encoding'], 0, 2);
}
}
if (isset($spec['lastModifiedTime'])) {
$this->_setLastModified($spec['lastModifiedTime']);
if (isset($spec['eTag'])) { // Use it
$this->_setEtag($spec['eTag'], $scope);
} else { // base both headers on time
$this->_setEtag($spec['lastModifiedTime'] . $etagAppend, $scope);
}
} elseif (isset($spec['eTag'])) { // Use it
$this->_setEtag($spec['eTag'], $scope);
} elseif (isset($spec['contentHash'])) { // Use the hash as the ETag
$this->_setEtag($spec['contentHash'] . $etagAppend, $scope);
}
$privacy = ($scope === 'private')
? ', private'
: '';
$this->_headers['Cache-Control'] = "max-age={$maxAge}{$privacy}";
// invalidate cache if disabled, otherwise check
$this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate'])
? false
: $this->_isCacheValid();
}
/**
* Get array of output headers to be sent
*
* In the case of 304 responses, this array will only contain the response
* code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified')
*
* Otherwise something like:
* <code>
* array(
* 'Cache-Control' => 'max-age=0, public'
* ,'ETag' => '"foobar"'
* )
* </code>
*
* @return array
*/
public function getHeaders()
{
return $this->_headers;
}
/**
* Set the Content-Length header in bytes
*
* With most PHP configs, as long as you don't flush() output, this method
* is not needed and PHP will buffer all output and set Content-Length for
* you. Otherwise you'll want to call this to let the client know up front.
*
* @param int $bytes
*
* @return int copy of input $bytes
*/
public function setContentLength($bytes)
{
return $this->_headers['Content-Length'] = $bytes;
}
/**
* Send headers
*
* @see getHeaders()
*
* Note this doesn't "clear" the headers. Calling sendHeaders() will
* call header() again (but probably have not effect) and getHeaders() will
* still return the headers.
*
* @return null
*/
public function sendHeaders()
{
$headers = $this->_headers;
if (array_key_exists('_responseCode', $headers)) {
// FastCGI environments require 3rd arg to header() to be set
list(, $code) = explode(' ', $headers['_responseCode'], 3);
header($headers['_responseCode'], true, $code);
unset($headers['_responseCode']);
}
foreach ($headers as $name => $val) {
header($name . ': ' . $val);
}
}
/**
* Exit if the client's cache is valid for this resource
*
* This is a convenience method for common use of the class
*
* @param int $lastModifiedTime if given, both ETag AND Last-Modified headers
* will be sent with content. This is recommended.
*
* @param bool $isPublic (default false) if true, the Cache-Control header
* will contain "public", allowing proxies to cache the content. Otherwise
* "private" will be sent, allowing only browser caching.
*
* @param array $options (default empty) additional options for constructor
*/
public static function check($lastModifiedTime = null, $isPublic = false, $options = array())
{
if (null !== $lastModifiedTime) {
$options['lastModifiedTime'] = (int)$lastModifiedTime;
}
$options['isPublic'] = (bool)$isPublic;
$cg = new HTTP_ConditionalGet($options);
$cg->sendHeaders();
if ($cg->cacheIsValid) {
exit();
}
}
/**
* Get a GMT formatted date for use in HTTP headers
*
* <code>
* header('Expires: ' . HTTP_ConditionalGet::gmtdate($time));
* </code>
*
* @param int $time unix timestamp
*
* @return string
*/
public static function gmtDate($time)
{
return gmdate('D, d M Y H:i:s \G\M\T', $time);
}
protected $_headers = array();
protected $_lmTime = null;
protected $_etag = null;
protected $_stripEtag = false;
/**
* @param string $hash
*
* @param string $scope
*/
protected function _setEtag($hash, $scope)
{
$this->_etag = '"' . substr($scope, 0, 3) . $hash . '"';
$this->_headers['ETag'] = $this->_etag;
}
/**
* @param int $time
*/
protected function _setLastModified($time)
{
$this->_lmTime = (int)$time;
$this->_headers['Last-Modified'] = self::gmtDate($time);
}
/**
* Determine validity of client cache and queue 304 header if valid
*
* @return bool
*/
protected function _isCacheValid()
{
if (null === $this->_etag) {
// lmTime is copied to ETag, so this condition implies that the
// server sent neither ETag nor Last-Modified, so the client can't
// possibly has a valid cache.
return false;
}
$isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified());
if ($isValid) {
$this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified';
}
return $isValid;
}
/**
* @return bool
*/
protected function resourceMatchedEtag()
{
if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
return false;
}
$clientEtagList = get_magic_quotes_gpc()
? stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])
: $_SERVER['HTTP_IF_NONE_MATCH'];
$clientEtags = explode(',', $clientEtagList);
$compareTo = $this->normalizeEtag($this->_etag);
foreach ($clientEtags as $clientEtag) {
if ($this->normalizeEtag($clientEtag) === $compareTo) {
// respond with the client's matched ETag, even if it's not what
// we would've sent by default
$this->_headers['ETag'] = trim($clientEtag);
return true;
}
}
return false;
}
/**
* @param string $etag
*
* @return string
*/
protected function normalizeEtag($etag) {
$etag = trim($etag);
return $this->_stripEtag
? preg_replace('/;\\w\\w"$/', '"', $etag)
: $etag;
}
/**
* @return bool
*/
protected function resourceNotModified()
{
if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
return false;
}
// strip off IE's extra data (semicolon)
list($ifModifiedSince) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2);
if (strtotime($ifModifiedSince) >= $this->_lmTime) {
// Apache 2.2's behavior. If there was no ETag match, send the
// non-encoded version of the ETag value.
$this->_headers['ETag'] = $this->normalizeEtag($this->_etag);
return true;
}
return false;
}
}

View File

@@ -0,0 +1,335 @@
<?php
/**
* Class HTTP_Encoder
* @package Minify
* @subpackage HTTP
*/
/**
* Encode and send gzipped/deflated content
*
* The "Vary: Accept-Encoding" header is sent. If the client allows encoding,
* Content-Encoding and Content-Length are added.
*
* <code>
* // Send a CSS file, compressed if possible
* $he = new HTTP_Encoder(array(
* 'content' => file_get_contents($cssFile)
* ,'type' => 'text/css'
* ));
* $he->encode();
* $he->sendAll();
* </code>
*
* <code>
* // Shortcut to encoding output
* header('Content-Type: text/css'); // needed if not HTML
* HTTP_Encoder::output($css);
* </code>
*
* <code>
* // Just sniff for the accepted encoding
* $encoding = HTTP_Encoder::getAcceptedEncoding();
* </code>
*
* For more control over headers, use getHeaders() and getData() and send your
* own output.
*
* Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate,
* and gzcompress functions for gzip, deflate, and compress-encoding
* respectively.
*
* @package Minify
* @subpackage HTTP
* @author Stephen Clay <steve@mrclay.org>
*/
class HTTP_Encoder {
/**
* Should the encoder allow HTTP encoding to IE6?
*
* If you have many IE6 users and the bandwidth savings is worth troubling
* some of them, set this to true.
*
* By default, encoding is only offered to IE7+. When this is true,
* getAcceptedEncoding() will return an encoding for IE6 if its user agent
* string contains "SV1". This has been documented in many places as "safe",
* but there seem to be remaining, intermittent encoding bugs in patched
* IE6 on the wild web.
*
* @var bool
*/
public static $encodeToIe6 = true;
/**
* Default compression level for zlib operations
*
* This level is used if encode() is not given a $compressionLevel
*
* @var int
*/
public static $compressionLevel = 6;
/**
* Get an HTTP Encoder object
*
* @param array $spec options
*
* 'content': (string required) content to be encoded
*
* 'type': (string) if set, the Content-Type header will have this value.
*
* 'method: (string) only set this if you are forcing a particular encoding
* method. If not set, the best method will be chosen by getAcceptedEncoding()
* The available methods are 'gzip', 'deflate', 'compress', and '' (no
* encoding)
*/
public function __construct($spec)
{
$this->_useMbStrlen = (function_exists('mb_strlen')
&& (ini_get('mbstring.func_overload') !== '')
&& ((int)ini_get('mbstring.func_overload') & 2));
$this->_content = $spec['content'];
$this->_headers['Content-Length'] = $this->_useMbStrlen
? (string)mb_strlen($this->_content, '8bit')
: (string)strlen($this->_content);
if (isset($spec['type'])) {
$this->_headers['Content-Type'] = $spec['type'];
}
if (isset($spec['method'])
&& in_array($spec['method'], array('gzip', 'deflate', 'compress', '')))
{
$this->_encodeMethod = array($spec['method'], $spec['method']);
} else {
$this->_encodeMethod = self::getAcceptedEncoding();
}
}
/**
* Get content in current form
*
* Call after encode() for encoded content.
*
* @return string
*/
public function getContent()
{
return $this->_content;
}
/**
* Get array of output headers to be sent
*
* E.g.
* <code>
* array(
* 'Content-Length' => '615'
* ,'Content-Encoding' => 'x-gzip'
* ,'Vary' => 'Accept-Encoding'
* )
* </code>
*
* @return array
*/
public function getHeaders()
{
return $this->_headers;
}
/**
* Send output headers
*
* You must call this before headers are sent and it probably cannot be
* used in conjunction with zlib output buffering / mod_gzip. Errors are
* not handled purposefully.
*
* @see getHeaders()
*/
public function sendHeaders()
{
foreach ($this->_headers as $name => $val) {
header($name . ': ' . $val);
}
}
/**
* Send output headers and content
*
* A shortcut for sendHeaders() and echo getContent()
*
* You must call this before headers are sent and it probably cannot be
* used in conjunction with zlib output buffering / mod_gzip. Errors are
* not handled purposefully.
*/
public function sendAll()
{
$this->sendHeaders();
echo $this->_content;
}
/**
* Determine the client's best encoding method from the HTTP Accept-Encoding
* header.
*
* If no Accept-Encoding header is set, or the browser is IE before v6 SP2,
* this will return ('', ''), the "identity" encoding.
*
* A syntax-aware scan is done of the Accept-Encoding, so the method must
* be non 0. The methods are favored in order of gzip, deflate, then
* compress. Deflate is always smallest and generally faster, but is
* rarely sent by servers, so client support could be buggier.
*
* @param bool $allowCompress allow the older compress encoding
*
* @param bool $allowDeflate allow the more recent deflate encoding
*
* @return array two values, 1st is the actual encoding method, 2nd is the
* alias of that method to use in the Content-Encoding header (some browsers
* call gzip "x-gzip" etc.)
*/
public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true)
{
// @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
|| self::isBuggyIe())
{
return array('', '');
}
$ae = $_SERVER['HTTP_ACCEPT_ENCODING'];
// gzip checks (quick)
if (0 === strpos($ae, 'gzip,') // most browsers
|| 0 === strpos($ae, 'deflate, gzip,') // opera
) {
return array('gzip', 'gzip');
}
// gzip checks (slow)
if (preg_match(
'@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
,$ae
,$m)) {
return array('gzip', $m[1]);
}
if ($allowDeflate) {
// deflate checks
$aeRev = strrev($ae);
if (0 === strpos($aeRev, 'etalfed ,') // ie, webkit
|| 0 === strpos($aeRev, 'etalfed,') // gecko
|| 0 === strpos($ae, 'deflate,') // opera
// slow parsing
|| preg_match(
'@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@', $ae)) {
return array('deflate', 'deflate');
}
}
if ($allowCompress && preg_match(
'@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
,$ae
,$m)) {
return array('compress', $m[1]);
}
return array('', '');
}
/**
* Encode (compress) the content
*
* If the encode method is '' (none) or compression level is 0, or the 'zlib'
* extension isn't loaded, we return false.
*
* Then the appropriate gz_* function is called to compress the content. If
* this fails, false is returned.
*
* The header "Vary: Accept-Encoding" is added. If encoding is successful,
* the Content-Length header is updated, and Content-Encoding is also added.
*
* @param int $compressionLevel given to zlib functions. If not given, the
* class default will be used.
*
* @return bool success true if the content was actually compressed
*/
public function encode($compressionLevel = null)
{
if (! self::isBuggyIe()) {
$this->_headers['Vary'] = 'Accept-Encoding';
}
if (null === $compressionLevel) {
$compressionLevel = self::$compressionLevel;
}
if ('' === $this->_encodeMethod[0]
|| ($compressionLevel == 0)
|| !extension_loaded('zlib'))
{
return false;
}
if ($this->_encodeMethod[0] === 'deflate') {
$encoded = gzdeflate($this->_content, $compressionLevel);
} elseif ($this->_encodeMethod[0] === 'gzip') {
$encoded = gzencode($this->_content, $compressionLevel);
} else {
$encoded = gzcompress($this->_content, $compressionLevel);
}
if (false === $encoded) {
return false;
}
$this->_headers['Content-Length'] = $this->_useMbStrlen
? (string)mb_strlen($encoded, '8bit')
: (string)strlen($encoded);
$this->_headers['Content-Encoding'] = $this->_encodeMethod[1];
$this->_content = $encoded;
return true;
}
/**
* Encode and send appropriate headers and content
*
* This is a convenience method for common use of the class
*
* @param string $content
*
* @param int $compressionLevel given to zlib functions. If not given, the
* class default will be used.
*
* @return bool success true if the content was actually compressed
*/
public static function output($content, $compressionLevel = null)
{
if (null === $compressionLevel) {
$compressionLevel = self::$compressionLevel;
}
$he = new HTTP_Encoder(array('content' => $content));
$ret = $he->encode($compressionLevel);
$he->sendAll();
return $ret;
}
/**
* Is the browser an IE version earlier than 6 SP2?
*
* @return bool
*/
public static function isBuggyIe()
{
if (empty($_SERVER['HTTP_USER_AGENT'])) {
return false;
}
$ua = $_SERVER['HTTP_USER_AGENT'];
// quick escape for non-IEs
if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ')
|| false !== strpos($ua, 'Opera')) {
return false;
}
// no regex = faaast
$version = (float)substr($ua, 30);
return self::$encodeToIe6
? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1')))
: ($version < 7);
}
protected $_content = '';
protected $_headers = array();
protected $_encodeMethod = array('', '');
protected $_useMbStrlen = false;
}

449
vendor/mrclay/minify/min/lib/JSMin.php vendored Normal file
View File

@@ -0,0 +1,449 @@
<?php
/**
* JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
*
* <code>
* $minifiedJs = JSMin::minify($js);
* </code>
*
* This is a modified port of jsmin.c. Improvements:
*
* Does not choke on some regexp literals containing quote characters. E.g. /'/
*
* Spaces are preserved after some add/sub operators, so they are not mistakenly
* converted to post-inc/dec. E.g. a + ++b -> a+ ++b
*
* Preserves multi-line comments that begin with /*!
*
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
* same terms as jsmin.c, which has the following license:
*
* --
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* The Software shall be used for Good, not Evil.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* --
*
* @package JSMin
* @author Ryan Grove <ryan@wonko.com> (PHP port)
* @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
* @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://code.google.com/p/jsmin-php/
*/
class JSMin {
const ORD_LF = 10;
const ORD_SPACE = 32;
const ACTION_KEEP_A = 1;
const ACTION_DELETE_A = 2;
const ACTION_DELETE_A_B = 3;
protected $a = "\n";
protected $b = '';
protected $input = '';
protected $inputIndex = 0;
protected $inputLength = 0;
protected $lookAhead = null;
protected $output = '';
protected $lastByteOut = '';
protected $keptComment = '';
/**
* Minify Javascript.
*
* @param string $js Javascript to be minified
*
* @return string
*/
public static function minify($js)
{
$jsmin = new JSMin($js);
return $jsmin->min();
}
/**
* @param string $input
*/
public function __construct($input)
{
$this->input = $input;
}
/**
* Perform minification, return result
*
* @return string
*/
public function min()
{
if ($this->output !== '') { // min already run
return $this->output;
}
$mbIntEnc = null;
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
$mbIntEnc = mb_internal_encoding();
mb_internal_encoding('8bit');
}
$this->input = str_replace("\r\n", "\n", $this->input);
$this->inputLength = strlen($this->input);
$this->action(self::ACTION_DELETE_A_B);
while ($this->a !== null) {
// determine next command
$command = self::ACTION_KEEP_A; // default
if ($this->a === ' ') {
if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
&& ($this->b === $this->lastByteOut)) {
// Don't delete this space. If we do, the addition/subtraction
// could be parsed as a post-increment
} elseif (! $this->isAlphaNum($this->b)) {
$command = self::ACTION_DELETE_A;
}
} elseif ($this->a === "\n") {
if ($this->b === ' ') {
$command = self::ACTION_DELETE_A_B;
// in case of mbstring.func_overload & 2, must check for null b,
// otherwise mb_strpos will give WARNING
} elseif ($this->b === null
|| (false === strpos('{[(+-!~', $this->b)
&& ! $this->isAlphaNum($this->b))) {
$command = self::ACTION_DELETE_A;
}
} elseif (! $this->isAlphaNum($this->a)) {
if ($this->b === ' '
|| ($this->b === "\n"
&& (false === strpos('}])+-"\'', $this->a)))) {
$command = self::ACTION_DELETE_A_B;
}
}
$this->action($command);
}
$this->output = trim($this->output);
if ($mbIntEnc !== null) {
mb_internal_encoding($mbIntEnc);
}
return $this->output;
}
/**
* ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
* ACTION_DELETE_A = Copy B to A. Get the next B.
* ACTION_DELETE_A_B = Get the next B.
*
* @param int $command
* @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
*/
protected function action($command)
{
// make sure we don't compress "a + ++b" to "a+++b", etc.
if ($command === self::ACTION_DELETE_A_B
&& $this->b === ' '
&& ($this->a === '+' || $this->a === '-')) {
// Note: we're at an addition/substraction operator; the inputIndex
// will certainly be a valid index
if ($this->input[$this->inputIndex] === $this->a) {
// This is "+ +" or "- -". Don't delete the space.
$command = self::ACTION_KEEP_A;
}
}
switch ($command) {
case self::ACTION_KEEP_A: // 1
$this->output .= $this->a;
if ($this->keptComment) {
$this->output = rtrim($this->output, "\n");
$this->output .= $this->keptComment;
$this->keptComment = '';
}
$this->lastByteOut = $this->a;
// fallthrough intentional
case self::ACTION_DELETE_A: // 2
$this->a = $this->b;
if ($this->a === "'" || $this->a === '"') { // string literal
$str = $this->a; // in case needed for exception
for(;;) {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
if ($this->a === $this->b) { // end quote
break;
}
if ($this->isEOF($this->a)) {
$byte = $this->inputIndex - 1;
throw new JSMin_UnterminatedStringException(
"JSMin: Unterminated String at byte {$byte}: {$str}");
}
$str .= $this->a;
if ($this->a === '\\') {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
$str .= $this->a;
}
}
}
// fallthrough intentional
case self::ACTION_DELETE_A_B: // 3
$this->b = $this->next();
if ($this->b === '/' && $this->isRegexpLiteral()) {
$this->output .= $this->a . $this->b;
$pattern = '/'; // keep entire pattern in case we need to report it in the exception
for(;;) {
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === '[') {
for(;;) {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === ']') {
break;
}
if ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
}
if ($this->isEOF($this->a)) {
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated set in RegExp at byte "
. $this->inputIndex .": {$pattern}");
}
}
}
if ($this->a === '/') { // end pattern
break; // while (true)
} elseif ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
} elseif ($this->isEOF($this->a)) {
$byte = $this->inputIndex - 1;
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated RegExp at byte {$byte}: {$pattern}");
}
$this->output .= $this->a;
$this->lastByteOut = $this->a;
}
$this->b = $this->next();
}
// end case ACTION_DELETE_A_B
}
}
/**
* @return bool
*/
protected function isRegexpLiteral()
{
if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
// we obviously aren't dividing
return true;
}
// we have to check for a preceding keyword, and we don't need to pattern
// match over the whole output.
$recentOutput = substr($this->output, -10);
// check if return/typeof directly precede a pattern without a space
foreach (array('return', 'typeof') as $keyword) {
if ($this->a !== substr($keyword, -1)) {
// certainly wasn't keyword
continue;
}
if (preg_match("~(^|[\\s\\S])" . substr($keyword, 0, -1) . "$~", $recentOutput, $m)) {
if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
return true;
}
}
}
// check all keywords
if ($this->a === ' ' || $this->a === "\n") {
if (preg_match('~(^|[\\s\\S])(?:case|else|in|return|typeof)$~', $recentOutput, $m)) {
if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
return true;
}
}
}
return false;
}
/**
* Return the next character from stdin. Watch out for lookahead. If the character is a control character,
* translate it to a space or linefeed.
*
* @return string
*/
protected function get()
{
$c = $this->lookAhead;
$this->lookAhead = null;
if ($c === null) {
// getc(stdin)
if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex];
$this->inputIndex += 1;
} else {
$c = null;
}
}
if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
return $c;
}
if ($c === "\r") {
return "\n";
}
return ' ';
}
/**
* Does $a indicate end of input?
*
* @param string $a
* @return bool
*/
protected function isEOF($a)
{
return ord($a) <= self::ORD_LF;
}
/**
* Get next char (without getting it). If is ctrl character, translate to a space or newline.
*
* @return string
*/
protected function peek()
{
$this->lookAhead = $this->get();
return $this->lookAhead;
}
/**
* Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
*
* @param string $c
*
* @return bool
*/
protected function isAlphaNum($c)
{
return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
}
/**
* Consume a single line comment from input (possibly retaining it)
*/
protected function consumeSingleLineComment()
{
$comment = '';
while (true) {
$get = $this->get();
$comment .= $get;
if (ord($get) <= self::ORD_LF) { // end of line reached
// if IE conditional comment
if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
$this->keptComment .= "/{$comment}";
}
return;
}
}
}
/**
* Consume a multiple line comment from input (possibly retaining it)
*
* @throws JSMin_UnterminatedCommentException
*/
protected function consumeMultipleLineComment()
{
$this->get();
$comment = '';
for(;;) {
$get = $this->get();
if ($get === '*') {
if ($this->peek() === '/') { // end of comment reached
$this->get();
if (0 === strpos($comment, '!')) {
// preserved by YUI Compressor
if (!$this->keptComment) {
// don't prepend a newline if two comments right after one another
$this->keptComment = "\n";
}
$this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
} else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
// IE conditional
$this->keptComment .= "/*{$comment}*/";
}
return;
}
} elseif ($get === null) {
throw new JSMin_UnterminatedCommentException(
"JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
}
$comment .= $get;
}
}
/**
* Get the next character, skipping over comments. Some comments may be preserved.
*
* @return string
*/
protected function next()
{
$get = $this->get();
if ($get === '/') {
switch ($this->peek()) {
case '/':
$this->consumeSingleLineComment();
$get = "\n";
break;
case '*':
$this->consumeMultipleLineComment();
$get = ' ';
break;
}
}
return $get;
}
}
class JSMin_UnterminatedStringException extends Exception {}
class JSMin_UnterminatedCommentException extends Exception {}
class JSMin_UnterminatedRegExpException extends Exception {}

2086
vendor/mrclay/minify/min/lib/JSMinPlus.php vendored Normal file

File diff suppressed because it is too large Load Diff

608
vendor/mrclay/minify/min/lib/Minify.php vendored Normal file
View File

@@ -0,0 +1,608 @@
<?php
/**
* Class Minify
* @package Minify
*/
/**
* Minify - Combines, minifies, and caches JavaScript and CSS files on demand.
*
* See README for usage instructions (for now).
*
* This library was inspired by {@link mailto:flashkot@mail.ru jscsscomp by Maxim Martynyuk}
* and by the article {@link http://www.hunlock.com/blogs/Supercharged_Javascript "Supercharged JavaScript" by Patrick Hunlock}.
*
* Requires PHP 5.1.0.
* Tested on PHP 5.1.6.
*
* @package Minify
* @author Ryan Grove <ryan@wonko.com>
* @author Stephen Clay <steve@mrclay.org>
* @copyright 2008 Ryan Grove, Stephen Clay. All rights reserved.
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @link http://code.google.com/p/minify/
*/
class Minify {
const VERSION = '2.2.0';
const TYPE_CSS = 'text/css';
const TYPE_HTML = 'text/html';
// there is some debate over the ideal JS Content-Type, but this is the
// Apache default and what Yahoo! uses..
const TYPE_JS = 'application/x-javascript';
const URL_DEBUG = 'http://code.google.com/p/minify/wiki/Debugging';
/**
* How many hours behind are the file modification times of uploaded files?
*
* If you upload files from Windows to a non-Windows server, Windows may report
* incorrect mtimes for the files. Immediately after modifying and uploading a
* file, use the touch command to update the mtime on the server. If the mtime
* jumps ahead by a number of hours, set this variable to that number. If the mtime
* moves back, this should not be needed.
*
* @var int $uploaderHoursBehind
*/
public static $uploaderHoursBehind = 0;
/**
* If this string is not empty AND the serve() option 'bubbleCssImports' is
* NOT set, then serve() will check CSS files for @import declarations that
* appear too late in the combined stylesheet. If found, serve() will prepend
* the output with this warning.
*
* @var string $importWarning
*/
public static $importWarning = "/* See http://code.google.com/p/minify/wiki/CommonProblems#@imports_can_appear_in_invalid_locations_in_combined_CSS_files */\n";
/**
* Has the DOCUMENT_ROOT been set in user code?
*
* @var bool
*/
public static $isDocRootSet = false;
/**
* Specify a cache object (with identical interface as Minify_Cache_File) or
* a path to use with Minify_Cache_File.
*
* If not called, Minify will not use a cache and, for each 200 response, will
* need to recombine files, minify and encode the output.
*
* @param mixed $cache object with identical interface as Minify_Cache_File or
* a directory path, or null to disable caching. (default = '')
*
* @param bool $fileLocking (default = true) This only applies if the first
* parameter is a string.
*
* @return null
*/
public static function setCache($cache = '', $fileLocking = true)
{
if (is_string($cache)) {
self::$_cache = new Minify_Cache_File($cache, $fileLocking);
} else {
self::$_cache = $cache;
}
}
/**
* Serve a request for a minified file.
*
* Here are the available options and defaults in the base controller:
*
* 'isPublic' : send "public" instead of "private" in Cache-Control
* headers, allowing shared caches to cache the output. (default true)
*
* 'quiet' : set to true to have serve() return an array rather than sending
* any headers/output (default false)
*
* 'encodeOutput' : set to false to disable content encoding, and not send
* the Vary header (default true)
*
* 'encodeMethod' : generally you should let this be determined by
* HTTP_Encoder (leave null), but you can force a particular encoding
* to be returned, by setting this to 'gzip' or '' (no encoding)
*
* 'encodeLevel' : level of encoding compression (0 to 9, default 9)
*
* 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey
* value to remove. (default 'utf-8')
*
* 'maxAge' : set this to the number of seconds the client should use its cache
* before revalidating with the server. This sets Cache-Control: max-age and the
* Expires header. Unlike the old 'setExpires' setting, this setting will NOT
* prevent conditional GETs. Note this has nothing to do with server-side caching.
*
* 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir'
* minifier option to enable URI rewriting in CSS files (default true)
*
* 'bubbleCssImports' : If true, all @import declarations in combined CSS
* files will be move to the top. Note this may alter effective CSS values
* due to a change in order. (default false)
*
* 'debug' : set to true to minify all sources with the 'Lines' controller, which
* eases the debugging of combined files. This also prevents 304 responses.
* @see Minify_Lines::minify()
*
* 'minifiers' : to override Minify's default choice of minifier function for
* a particular content-type, specify your callback under the key of the
* content-type:
* <code>
* // call customCssMinifier($css) for all CSS minification
* $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier';
*
* // don't minify Javascript at all
* $options['minifiers'][Minify::TYPE_JS] = '';
* </code>
*
* 'minifierOptions' : to send options to the minifier function, specify your options
* under the key of the content-type. E.g. To send the CSS minifier an option:
* <code>
* // give CSS minifier array('optionName' => 'optionValue') as 2nd argument
* $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue';
* </code>
*
* 'contentType' : (optional) this is only needed if your file extension is not
* js/css/html. The given content-type will be sent regardless of source file
* extension, so this should not be used in a Groups config with other
* Javascript/CSS files.
*
* Any controller options are documented in that controller's setupSources() method.
*
* @param mixed $controller instance of subclass of Minify_Controller_Base or string
* name of controller. E.g. 'Files'
*
* @param array $options controller/serve options
*
* @return null|array if the 'quiet' option is set to true, an array
* with keys "success" (bool), "statusCode" (int), "content" (string), and
* "headers" (array).
*
* @throws Exception
*/
public static function serve($controller, $options = array())
{
if (! self::$isDocRootSet && 0 === stripos(PHP_OS, 'win')) {
self::setDocRoot();
}
if (is_string($controller)) {
// make $controller into object
$class = 'Minify_Controller_' . $controller;
$controller = new $class();
/* @var Minify_Controller_Base $controller */
}
// set up controller sources and mix remaining options with
// controller defaults
$options = $controller->setupSources($options);
$options = $controller->analyzeSources($options);
self::$_options = $controller->mixInDefaultOptions($options);
// check request validity
if (! $controller->sources) {
// invalid request!
if (! self::$_options['quiet']) {
self::_errorExit(self::$_options['badRequestHeader'], self::URL_DEBUG);
} else {
list(,$statusCode) = explode(' ', self::$_options['badRequestHeader']);
return array(
'success' => false
,'statusCode' => (int)$statusCode
,'content' => ''
,'headers' => array()
);
}
}
self::$_controller = $controller;
if (self::$_options['debug']) {
self::_setupDebug($controller->sources);
self::$_options['maxAge'] = 0;
}
// determine encoding
if (self::$_options['encodeOutput']) {
$sendVary = true;
if (self::$_options['encodeMethod'] !== null) {
// controller specifically requested this
$contentEncoding = self::$_options['encodeMethod'];
} else {
// sniff request header
// depending on what the client accepts, $contentEncoding may be
// 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
// getAcceptedEncoding(false, false) leaves out compress and deflate as options.
list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false);
$sendVary = ! HTTP_Encoder::isBuggyIe();
}
} else {
self::$_options['encodeMethod'] = ''; // identity (no encoding)
}
// check client cache
$cgOptions = array(
'lastModifiedTime' => self::$_options['lastModifiedTime']
,'isPublic' => self::$_options['isPublic']
,'encoding' => self::$_options['encodeMethod']
);
if (self::$_options['maxAge'] > 0) {
$cgOptions['maxAge'] = self::$_options['maxAge'];
} elseif (self::$_options['debug']) {
$cgOptions['invalidate'] = true;
}
$cg = new HTTP_ConditionalGet($cgOptions);
if ($cg->cacheIsValid) {
// client's cache is valid
if (! self::$_options['quiet']) {
$cg->sendHeaders();
return;
} else {
return array(
'success' => true
,'statusCode' => 304
,'content' => ''
,'headers' => $cg->getHeaders()
);
}
} else {
// client will need output
$headers = $cg->getHeaders();
unset($cg);
}
if (self::$_options['contentType'] === self::TYPE_CSS
&& self::$_options['rewriteCssUris']) {
foreach($controller->sources as $key => $source) {
if ($source->filepath
&& !isset($source->minifyOptions['currentDir'])
&& !isset($source->minifyOptions['prependRelativePath'])
) {
$source->minifyOptions['currentDir'] = dirname($source->filepath);
}
}
}
// check server cache
if (null !== self::$_cache && ! self::$_options['debug']) {
// using cache
// the goal is to use only the cache methods to sniff the length and
// output the content, as they do not require ever loading the file into
// memory.
$cacheId = self::_getCacheId();
$fullCacheId = (self::$_options['encodeMethod'])
? $cacheId . '.gz'
: $cacheId;
// check cache for valid entry
$cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']);
if ($cacheIsReady) {
$cacheContentLength = self::$_cache->getSize($fullCacheId);
} else {
// generate & cache content
try {
$content = self::_combineMinify();
} catch (Exception $e) {
self::$_controller->log($e->getMessage());
if (! self::$_options['quiet']) {
self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
}
throw $e;
}
self::$_cache->store($cacheId, $content);
if (function_exists('gzencode') && self::$_options['encodeMethod']) {
self::$_cache->store($cacheId . '.gz', gzencode($content, self::$_options['encodeLevel']));
}
}
} else {
// no cache
$cacheIsReady = false;
try {
$content = self::_combineMinify();
} catch (Exception $e) {
self::$_controller->log($e->getMessage());
if (! self::$_options['quiet']) {
self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
}
throw $e;
}
}
if (! $cacheIsReady && self::$_options['encodeMethod']) {
// still need to encode
$content = gzencode($content, self::$_options['encodeLevel']);
}
// add headers
$headers['Content-Length'] = $cacheIsReady
? $cacheContentLength
: ((function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
? mb_strlen($content, '8bit')
: strlen($content)
);
$headers['Content-Type'] = self::$_options['contentTypeCharset']
? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset']
: self::$_options['contentType'];
if (self::$_options['encodeMethod'] !== '') {
$headers['Content-Encoding'] = $contentEncoding;
}
if (self::$_options['encodeOutput'] && $sendVary) {
$headers['Vary'] = 'Accept-Encoding';
}
if (! self::$_options['quiet']) {
// output headers & content
foreach ($headers as $name => $val) {
header($name . ': ' . $val);
}
if ($cacheIsReady) {
self::$_cache->display($fullCacheId);
} else {
echo $content;
}
} else {
return array(
'success' => true
,'statusCode' => 200
,'content' => $cacheIsReady
? self::$_cache->fetch($fullCacheId)
: $content
,'headers' => $headers
);
}
}
/**
* Return combined minified content for a set of sources
*
* No internal caching will be used and the content will not be HTTP encoded.
*
* @param array $sources array of filepaths and/or Minify_Source objects
*
* @param array $options (optional) array of options for serve. By default
* these are already set: quiet = true, encodeMethod = '', lastModifiedTime = 0.
*
* @return string
*/
public static function combine($sources, $options = array())
{
$cache = self::$_cache;
self::$_cache = null;
$options = array_merge(array(
'files' => (array)$sources
,'quiet' => true
,'encodeMethod' => ''
,'lastModifiedTime' => 0
), $options);
$out = self::serve('Files', $options);
self::$_cache = $cache;
return $out['content'];
}
/**
* Set $_SERVER['DOCUMENT_ROOT']. On IIS, the value is created from SCRIPT_FILENAME and SCRIPT_NAME.
*
* @param string $docRoot value to use for DOCUMENT_ROOT
*/
public static function setDocRoot($docRoot = '')
{
self::$isDocRootSet = true;
if ($docRoot) {
$_SERVER['DOCUMENT_ROOT'] = $docRoot;
} elseif (isset($_SERVER['SERVER_SOFTWARE'])
&& 0 === strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/')) {
$_SERVER['DOCUMENT_ROOT'] = substr(
$_SERVER['SCRIPT_FILENAME']
,0
,strlen($_SERVER['SCRIPT_FILENAME']) - strlen($_SERVER['SCRIPT_NAME']));
$_SERVER['DOCUMENT_ROOT'] = rtrim($_SERVER['DOCUMENT_ROOT'], '\\');
}
}
/**
* Any Minify_Cache_* object or null (i.e. no server cache is used)
*
* @var Minify_Cache_File
*/
private static $_cache = null;
/**
* Active controller for current request
*
* @var Minify_Controller_Base
*/
protected static $_controller = null;
/**
* Options for current request
*
* @var array
*/
protected static $_options = null;
/**
* @param string $header
*
* @param string $url
*/
protected static function _errorExit($header, $url)
{
$url = htmlspecialchars($url, ENT_QUOTES);
list(,$h1) = explode(' ', $header, 2);
$h1 = htmlspecialchars($h1);
// FastCGI environments require 3rd arg to header() to be set
list(, $code) = explode(' ', $header, 3);
header($header, true, $code);
header('Content-Type: text/html; charset=utf-8');
echo "<h1>$h1</h1>";
echo "<p>Please see <a href='$url'>$url</a>.</p>";
exit();
}
/**
* Set up sources to use Minify_Lines
*
* @param Minify_Source[] $sources Minify_Source instances
*/
protected static function _setupDebug($sources)
{
foreach ($sources as $source) {
$source->minifier = array('Minify_Lines', 'minify');
$id = $source->getId();
$source->minifyOptions = array(
'id' => (is_file($id) ? basename($id) : $id)
);
}
}
/**
* Combines sources and minifies the result.
*
* @return string
*
* @throws Exception
*/
protected static function _combineMinify()
{
$type = self::$_options['contentType']; // ease readability
// when combining scripts, make sure all statements separated and
// trailing single line comment is terminated
$implodeSeparator = ($type === self::TYPE_JS)
? "\n;"
: '';
// allow the user to pass a particular array of options to each
// minifier (designated by type). source objects may still override
// these
$defaultOptions = isset(self::$_options['minifierOptions'][$type])
? self::$_options['minifierOptions'][$type]
: array();
// if minifier not set, default is no minification. source objects
// may still override this
$defaultMinifier = isset(self::$_options['minifiers'][$type])
? self::$_options['minifiers'][$type]
: false;
// process groups of sources with identical minifiers/options
$content = array();
$i = 0;
$l = count(self::$_controller->sources);
$groupToProcessTogether = array();
$lastMinifier = null;
$lastOptions = null;
do {
// get next source
$source = null;
if ($i < $l) {
$source = self::$_controller->sources[$i];
/* @var Minify_Source $source */
$sourceContent = $source->getContent();
// allow the source to override our minifier and options
$minifier = (null !== $source->minifier)
? $source->minifier
: $defaultMinifier;
$options = (null !== $source->minifyOptions)
? array_merge($defaultOptions, $source->minifyOptions)
: $defaultOptions;
}
// do we need to process our group right now?
if ($i > 0 // yes, we have at least the first group populated
&& (
! $source // yes, we ran out of sources
|| $type === self::TYPE_CSS // yes, to process CSS individually (avoiding PCRE bugs/limits)
|| $minifier !== $lastMinifier // yes, minifier changed
|| $options !== $lastOptions) // yes, options changed
)
{
// minify previous sources with last settings
$imploded = implode($implodeSeparator, $groupToProcessTogether);
$groupToProcessTogether = array();
if ($lastMinifier) {
try {
$content[] = call_user_func($lastMinifier, $imploded, $lastOptions);
} catch (Exception $e) {
throw new Exception("Exception in minifier: " . $e->getMessage());
}
} else {
$content[] = $imploded;
}
}
// add content to the group
if ($source) {
$groupToProcessTogether[] = $sourceContent;
$lastMinifier = $minifier;
$lastOptions = $options;
}
$i++;
} while ($source);
$content = implode($implodeSeparator, $content);
if ($type === self::TYPE_CSS && false !== strpos($content, '@import')) {
$content = self::_handleCssImports($content);
}
// do any post-processing (esp. for editing build URIs)
if (self::$_options['postprocessorRequire']) {
require_once self::$_options['postprocessorRequire'];
}
if (self::$_options['postprocessor']) {
$content = call_user_func(self::$_options['postprocessor'], $content, $type);
}
return $content;
}
/**
* Make a unique cache id for for this request.
*
* Any settings that could affect output are taken into consideration
*
* @param string $prefix
*
* @return string
*/
protected static function _getCacheId($prefix = 'minify')
{
$name = preg_replace('/[^a-zA-Z0-9\\.=_,]/', '', self::$_controller->selectionId);
$name = preg_replace('/\\.+/', '.', $name);
$name = substr($name, 0, 100 - 34 - strlen($prefix));
$md5 = md5(serialize(array(
Minify_Source::getDigest(self::$_controller->sources)
,self::$_options['minifiers']
,self::$_options['minifierOptions']
,self::$_options['postprocessor']
,self::$_options['bubbleCssImports']
,self::VERSION
)));
return "{$prefix}_{$name}_{$md5}";
}
/**
* Bubble CSS @imports to the top or prepend a warning if an import is detected not at the top.
*
* @param string $css
*
* @return string
*/
protected static function _handleCssImports($css)
{
if (self::$_options['bubbleCssImports']) {
// bubble CSS imports
preg_match_all('/@import.*?;/', $css, $imports);
$css = implode('', $imports[0]) . preg_replace('/@import.*?;/', '', $css);
} else if ('' !== self::$importWarning) {
// remove comments so we don't mistake { in a comment as a block
$noCommentCss = preg_replace('@/\\*[\\s\\S]*?\\*/@', '', $css);
$lastImportPos = strrpos($noCommentCss, '@import');
$firstBlockPos = strpos($noCommentCss, '{');
if (false !== $lastImportPos
&& false !== $firstBlockPos
&& $firstBlockPos < $lastImportPos
) {
// { appears before @import : prepend warning
$css = self::$importWarning . $css;
}
}
return $css;
}
}

View File

@@ -0,0 +1,101 @@
<?php
/**
* Class Minify_Build
* @package Minify
*/
/**
* Maintain a single last modification time for a group of Minify sources to
* allow use of far off Expires headers in Minify.
*
* <code>
* // in config file
* $groupSources = array(
* 'js' => array('file1.js', 'file2.js')
* ,'css' => array('file1.css', 'file2.css', 'file3.css')
* )
*
* // during HTML generation
* $jsBuild = new Minify_Build($groupSources['js']);
* $cssBuild = new Minify_Build($groupSources['css']);
*
* $script = "<script type='text/javascript' src='"
* . $jsBuild->uri('/min.php/js') . "'></script>";
* $link = "<link rel='stylesheet' type='text/css' href='"
* . $cssBuild->uri('/min.php/css') . "'>";
*
* // in min.php
* Minify::serve('Groups', array(
* 'groups' => $groupSources
* ,'setExpires' => (time() + 86400 * 365)
* ));
* </code>
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_Build {
/**
* Last modification time of all files in the build
*
* @var int
*/
public $lastModified = 0;
/**
* String to use as ampersand in uri(). Set this to '&' if
* you are not HTML-escaping URIs.
*
* @var string
*/
public static $ampersand = '&amp;';
/**
* Get a time-stamped URI
*
* <code>
* echo $b->uri('/site.js');
* // outputs "/site.js?1678242"
*
* echo $b->uri('/scriptaculous.js?load=effects');
* // outputs "/scriptaculous.js?load=effects&amp1678242"
* </code>
*
* @param string $uri
* @param boolean $forceAmpersand (default = false) Force the use of ampersand to
* append the timestamp to the URI.
* @return string
*/
public function uri($uri, $forceAmpersand = false) {
$sep = ($forceAmpersand || strpos($uri, '?') !== false)
? self::$ampersand
: '?';
return "{$uri}{$sep}{$this->lastModified}";
}
/**
* Create a build object
*
* @param array $sources array of Minify_Source objects and/or file paths
*
* @return null
*/
public function __construct($sources)
{
$max = 0;
foreach ((array)$sources as $source) {
if ($source instanceof Minify_Source) {
$max = max($max, $source->lastModified);
} elseif (is_string($source)) {
if (0 === strpos($source, '//')) {
$source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1);
}
if (is_file($source)) {
$max = max($max, filemtime($source));
}
}
}
$this->lastModified = $max;
}
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* Class Minify_CSS
* @package Minify
*/
/**
* Minify CSS
*
* This class uses Minify_CSS_Compressor and Minify_CSS_UriRewriter to
* minify CSS and rewrite relative URIs.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
*/
class Minify_CSS {
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options available options:
*
* 'preserveComments': (default true) multi-line comments that begin
* with "/*!" will be preserved with newlines before and after to
* enhance readability.
*
* 'removeCharsets': (default true) remove all @charset at-rules
*
* 'prependRelativePath': (default null) if given, this string will be
* prepended to all relative URIs in import/url declarations
*
* 'currentDir': (default null) if given, this is assumed to be the
* directory of the current CSS file. Using this, minify will rewrite
* all relative URIs in import/url declarations to correctly point to
* the desired files. For this to work, the files *must* exist and be
* visible by the PHP process.
*
* 'symlinks': (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>
*
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
* see Minify_CSS_UriRewriter::rewrite
*
* @return string
*/
public static function minify($css, $options = array())
{
$options = array_merge(array(
'compress' => true,
'removeCharsets' => true,
'preserveComments' => true,
'currentDir' => null,
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
'prependRelativePath' => null,
'symlinks' => array(),
), $options);
if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
}
if ($options['compress']) {
if (! $options['preserveComments']) {
$css = Minify_CSS_Compressor::process($css, $options);
} else {
$css = Minify_CommentPreserver::process(
$css
,array('Minify_CSS_Compressor', 'process')
,array($options)
);
}
}
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css;
}
if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite(
$css
,$options['currentDir']
,$options['docRoot']
,$options['symlinks']
);
} else {
return Minify_CSS_UriRewriter::prepend(
$css
,$options['prependRelativePath']
);
}
}
}

View File

@@ -0,0 +1,249 @@
<?php
/**
* Class Minify_CSS_Compressor
* @package Minify
*/
/**
* Compress CSS
*
* This is a heavy regex-based removal of whitespace, unnecessary
* comments and tokens, and some CSS value minimization, where practical.
* Many steps have been taken to avoid breaking comment-based hacks,
* including the ie5/mac filter (and its inversion), but expect tricky
* hacks involving comment tokens in 'content' value strings to break
* minimization badly. A test suite is available.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
*/
class Minify_CSS_Compressor {
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options (currently ignored)
*
* @return string
*/
public static function process($css, $options = array())
{
$obj = new Minify_CSS_Compressor($options);
return $obj->_process($css);
}
/**
* @var array
*/
protected $_options = null;
/**
* Are we "in" a hack? I.e. are some browsers targetted until the next comment?
*
* @var bool
*/
protected $_inHack = false;
/**
* Constructor
*
* @param array $options (currently ignored)
*/
private function __construct($options) {
$this->_options = $options;
}
/**
* Minify a CSS string
*
* @param string $css
*
* @return string
*/
protected function _process($css)
{
$css = str_replace("\r\n", "\n", $css);
// preserve empty comment after '>'
// http://www.webdevout.net/css-hacks#in_css-selectors
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
// preserve empty comment between property and value
// http://css-discuss.incutio.com/?page=BoxModelHack
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
// apply callback to all valid comments (and strip out surrounding ws
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
,array($this, '_commentCB'), $css);
// remove ws around { } and last semicolon in declaration block
$css = preg_replace('/\\s*{\\s*/', '{', $css);
$css = preg_replace('/;?\\s*}\\s*/', '}', $css);
// remove ws surrounding semicolons
$css = preg_replace('/\\s*;\\s*/', ';', $css);
// remove ws around urls
$css = preg_replace('/
url\\( # url(
\\s*
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
\\s*
\\) # )
/x', 'url($1)', $css);
// remove ws between rules and colons
$css = preg_replace('/
\\s*
([{;]) # 1 = beginning of block or rule separator
\\s*
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
\\s*
:
\\s*
(\\b|[#\'"-]) # 3 = first character of a value
/x', '$1$2:$3', $css);
// remove ws in selectors
$css = preg_replace_callback('/
(?: # non-capture
\\s*
[^~>+,\\s]+ # selector part
\\s*
[,>+~] # combinators
)+
\\s*
[^~>+,\\s]+ # selector part
{ # open declaration block
/x'
,array($this, '_selectorsCB'), $css);
// minimize hex colors
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
, '$1#$2$3$4$5', $css);
// remove spaces between font families
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
,array($this, '_fontFamilyCB'), $css);
$css = preg_replace('/@import\\s+url/', '@import url', $css);
// replace any ws involving newlines with a single newline
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
// separate common descendent selectors w/ newlines (to limit line lengths)
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
// Use newline after 1st numeric value (to limit line lengths).
$css = preg_replace('/
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
\\s+
/x'
,"$1\n", $css);
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
return trim($css);
}
/**
* Replace what looks like a set of selectors
*
* @param array $m regex matches
*
* @return string
*/
protected function _selectorsCB($m)
{
// remove ws around the combinators
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
}
/**
* Process a comment and return a replacement
*
* @param array $m regex matches
*
* @return string
*/
protected function _commentCB($m)
{
$hasSurroundingWs = (trim($m[0]) !== $m[1]);
$m = $m[1];
// $m is the comment content w/o the surrounding tokens,
// but the return value will replace the entire comment.
if ($m === 'keep') {
return '/**/';
}
if ($m === '" "') {
// component of http://tantek.com/CSS/Examples/midpass.html
return '/*" "*/';
}
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
// component of http://tantek.com/CSS/Examples/midpass.html
return '/*";}}/* */';
}
if ($this->_inHack) {
// inversion: feeding only to one browser
if (preg_match('@
^/ # comment started like /*/
\\s*
(\\S[\\s\\S]+?) # has at least some non-ws content
\\s*
/\\* # ends like /*/ or /**/
@x', $m, $n)) {
// end hack mode after this comment, but preserve the hack and comment content
$this->_inHack = false;
return "/*/{$n[1]}/**/";
}
}
if (substr($m, -1) === '\\') { // comment ends like \*/
// begin hack mode and preserve hack
$this->_inHack = true;
return '/*\\*/';
}
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
// begin hack mode and preserve hack
$this->_inHack = true;
return '/*/*/';
}
if ($this->_inHack) {
// a regular comment ends hack mode but should be preserved
$this->_inHack = false;
return '/**/';
}
// Issue 107: if there's any surrounding whitespace, it may be important, so
// replace the comment with a single space
return $hasSurroundingWs // remove all other comments
? ' '
: '';
}
/**
* Process a font-family listing and return a replacement
*
* @param array $m regex matches
*
* @return string
*/
protected function _fontFamilyCB($m)
{
// Issue 210: must not eliminate WS between words in unquoted families
$pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$out = 'font-family:';
while (null !== ($piece = array_shift($pieces))) {
if ($piece[0] !== '"' && $piece[0] !== "'") {
$piece = preg_replace('/\\s+/', ' ', $piece);
$piece = preg_replace('/\\s?,\\s?/', ',', $piece);
}
$out .= $piece;
}
return $out . $m[2];
}
}

View File

@@ -0,0 +1,307 @@
<?php
/**
* Class Minify_CSS_UriRewriter
* @package Minify
*/
/**
* Rewrite file-relative URIs as root-relative in CSS files
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_CSS_UriRewriter {
/**
* rewrite() and rewriteRelative() append debugging information here
*
* @var string
*/
public static $debugText = '';
/**
* In CSS content, rewrite file relative URIs as root relative
*
* @param string $css
*
* @param string $currentDir The directory of the current CSS file.
*
* @param string $docRoot The document root of the web site in which
* the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
*
* @param array $symlinks (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>
*
* @return string
*/
public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array())
{
self::$_docRoot = self::_realpath(
$docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
);
self::$_currentDir = self::_realpath($currentDir);
self::$_symlinks = array();
// normalize symlinks
foreach ($symlinks as $link => $target) {
$link = ($link === '//')
? self::$_docRoot
: str_replace('//', self::$_docRoot . '/', $link);
$link = strtr($link, '/', DIRECTORY_SEPARATOR);
self::$_symlinks[$link] = self::_realpath($target);
}
self::$debugText .= "docRoot : " . self::$_docRoot . "\n"
. "currentDir : " . self::$_currentDir . "\n";
if (self::$_symlinks) {
self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
}
self::$debugText .= "\n";
$css = self::_trimUrls($css);
// rewrite
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
,array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css);
return $css;
}
/**
* In CSS content, prepend a path to relative URIs
*
* @param string $css
*
* @param string $path The path to prepend.
*
* @return string
*/
public static function prepend($css, $path)
{
self::$_prependPath = $path;
$css = self::_trimUrls($css);
// append
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
,array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css);
self::$_prependPath = null;
return $css;
}
/**
* Get a root relative URI from a file relative URI
*
* <code>
* Minify_CSS_UriRewriter::rewriteRelative(
* '../img/hello.gif'
* , '/home/user/www/css' // path of CSS file
* , '/home/user/www' // doc root
* );
* // returns '/img/hello.gif'
*
* // example where static files are stored in a symlinked directory
* Minify_CSS_UriRewriter::rewriteRelative(
* 'hello.gif'
* , '/var/staticFiles/theme'
* , '/home/user/www'
* , array('/home/user/www/static' => '/var/staticFiles')
* );
* // returns '/static/theme/hello.gif'
* </code>
*
* @param string $uri file relative URI
*
* @param string $realCurrentDir realpath of the current file's directory.
*
* @param string $realDocRoot realpath of the site document root.
*
* @param array $symlinks (default = array()) If the file is stored in
* a symlink-ed directory, provide an array of link paths to
* real target paths, where the link paths "appear" to be within the document
* root. E.g.:
* <code>
* array('/home/foo/www/not/real/path' => '/real/target/path') // unix
* array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
* </code>
*
* @return string
*/
public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
{
// prepend path with current dir separator (OS-independent)
$path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)
. DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
self::$debugText .= "file-relative URI : {$uri}\n"
. "path prepended : {$path}\n";
// "unresolve" a symlink back to doc root
foreach ($symlinks as $link => $target) {
if (0 === strpos($path, $target)) {
// replace $target with $link
$path = $link . substr($path, strlen($target));
self::$debugText .= "symlink unresolved : {$path}\n";
break;
}
}
// strip doc root
$path = substr($path, strlen($realDocRoot));
self::$debugText .= "docroot stripped : {$path}\n";
// fix to root-relative URI
$uri = strtr($path, '/\\', '//');
$uri = self::removeDots($uri);
self::$debugText .= "traversals removed : {$uri}\n\n";
return $uri;
}
/**
* Remove instances of "./" and "../" where possible from a root-relative URI
*
* @param string $uri
*
* @return string
*/
public static function removeDots($uri)
{
$uri = str_replace('/./', '/', $uri);
// inspired by patch from Oleg Cherniy
do {
$uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
} while ($changed);
return $uri;
}
/**
* Defines which class to call as part of callbacks, change this
* if you extend Minify_CSS_UriRewriter
*
* @var string
*/
protected static $className = 'Minify_CSS_UriRewriter';
/**
* Get realpath with any trailing slash removed. If realpath() fails,
* just remove the trailing slash.
*
* @param string $path
*
* @return mixed path with no trailing slash
*/
protected static function _realpath($path)
{
$realPath = realpath($path);
if ($realPath !== false) {
$path = $realPath;
}
return rtrim($path, '/\\');
}
/**
* Directory of this stylesheet
*
* @var string
*/
private static $_currentDir = '';
/**
* DOC_ROOT
*
* @var string
*/
private static $_docRoot = '';
/**
* directory replacements to map symlink targets back to their
* source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
*
* @var array
*/
private static $_symlinks = array();
/**
* Path to prepend
*
* @var string
*/
private static $_prependPath = null;
/**
* @param string $css
*
* @return string
*/
private static function _trimUrls($css)
{
return preg_replace('/
url\\( # url(
\\s*
([^\\)]+?) # 1 = URI (assuming does not contain ")")
\\s*
\\) # )
/x', 'url($1)', $css);
}
/**
* @param array $m
*
* @return string
*/
private static function _processUriCB($m)
{
// $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
$isImport = ($m[0][0] === '@');
// determine URI and the quote character (if any)
if ($isImport) {
$quoteChar = $m[1];
$uri = $m[2];
} else {
// $m[1] is either quoted or not
$quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
? $m[1][0]
: '';
$uri = ($quoteChar === '')
? $m[1]
: substr($m[1], 1, strlen($m[1]) - 2);
}
// if not root/scheme relative and not starts with scheme
if (!preg_match('~^(/|[a-z]+\:)~', $uri)) {
// URI is file-relative: rewrite depending on options
if (self::$_prependPath === null) {
$uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
} else {
$uri = self::$_prependPath . $uri;
if ($uri[0] === '/') {
$root = '';
$rootRelative = $uri;
$uri = $root . self::removeDots($rootRelative);
} elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) {
$root = $m[1];
$rootRelative = substr($uri, strlen($root));
$uri = $root . self::removeDots($rootRelative);
}
}
}
return $isImport
? "@import {$quoteChar}{$uri}{$quoteChar}"
: "url({$quoteChar}{$uri}{$quoteChar})";
}
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* Class Minify_CSSmin
* @package Minify
*/
/**
* Wrapper for CSSmin
*
* This class uses CSSmin and Minify_CSS_UriRewriter to minify CSS and rewrite relative URIs.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_CSSmin {
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options available options:
*
* 'removeCharsets': (default true) remove all @charset at-rules
*
* 'prependRelativePath': (default null) if given, this string will be
* prepended to all relative URIs in import/url declarations
*
* 'currentDir': (default null) if given, this is assumed to be the
* directory of the current CSS file. Using this, minify will rewrite
* all relative URIs in import/url declarations to correctly point to
* the desired files. For this to work, the files *must* exist and be
* visible by the PHP process.
*
* 'symlinks': (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>
*
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
* see Minify_CSS_UriRewriter::rewrite
*
* @return string
*/
public static function minify($css, $options = array())
{
$options = array_merge(array(
'compress' => true,
'removeCharsets' => true,
'currentDir' => null,
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
'prependRelativePath' => null,
'symlinks' => array(),
), $options);
if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
}
if ($options['compress']) {
$obj = new CSSmin();
$css = $obj->run($css);
}
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css;
}
if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite(
$css
,$options['currentDir']
,$options['docRoot']
,$options['symlinks']
);
} else {
return Minify_CSS_UriRewriter::prepend(
$css
,$options['prependRelativePath']
);
}
}
}

Some files were not shown because too many files have changed in this diff Show More