Initial v2 framework

* Remove vendor library
* Update dependencies
* Add initial commands (not yet updated for this project)
* Move empir and linter into place
* Add pakefile
This commit is contained in:
Craig Davis 2014-01-11 15:22:10 -06:00
parent c6170ca243
commit 0995f3d061
103 changed files with 1562 additions and 18592 deletions

14
.gitignore vendored
View File

@ -1,7 +1,17 @@
vendor
resume/_*.md
output/*.html
output/*.pdf
resume/sample_long.md
# OSX files
.DS_Store
.AppleDouble
.LSOverride
Icon
/vendor/
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes

View File

@ -2,11 +2,11 @@
2.0 Work in progress, unstable and not yet ready for use.
[ ] Update composer for symfony dependencies
[ ] Add pake and phar generator
[ ] Update bin with new generated phar
[ ] Convert to new command structure
[ ] Update help files
- [ ] Update composer for symfony dependencies
- [ ] Add pake and phar generator
- [ ] Update bin with new generated phar
- [ ] Convert to new command structure
- [ ] Update help files
## Description

34
bin/resume2.php Executable file
View File

@ -0,0 +1,34 @@
<?php
error_reporting(E_ALL | E_STRICT);
// If the dependencies aren't installed, we have to bail and offer some help.
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
exit("\nPlease run `composer install` to install dependencies.\n\n");
}
// Bootstrap our Silex application with the Composer autoloader
$app = require __DIR__ . '/vendor/autoload.php';
// Setup the namespace for our own namespace
$app->add('FogBugz', __DIR__ . '/src');
// Instantiate our Console application
$console = new FogBugz\Cli\Working();
// If we're running from phar, we get these values from the stub
if (!defined("IN_PHAR")) {
$project = json_decode(file_get_contents(__DIR__ . '/composer.json'));
}
// Config path can be set with a an ENV var
$configFile = getenv("FOGBUGZ_CONFIG") ? getenv("FOGBUGZ_CONFIG") : getenv("HOME") . "/.fogbugz.yml";
$templatePath = __DIR__ . '/templates';
// Init the app with these params
$console->initialize($configFile, $templatePath, $project);
// Execute the console app.
$console->run();
/* End of working.php */

496
build/empir Executable file
View File

@ -0,0 +1,496 @@
#!/usr/bin/env php
<?php
/*
* Empir
*
Copyright (c) 2010 Jeremy Perret <j.perret.27@gmail.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.
*/
//if colors make any damages on your terminal deactivate them by setting to false this option
define('ACTIVATE_COLORS', true);
//only run Empir automatically when this file is called directly from the command line
if (isset($argv[0])) {
if (version_compare(phpversion(), '5.3.0', '<')) {
echo "ERROR: Empir require php >= 5.3.0 (Your PHP version: ".phpversion().")\n";
exit(1);
}
$empir = new Empir($argv);
exit($empir->run());
}
/**
* Command line interface Helper.
*
* @package Empir
* @author Jeremy Perret <j.perret.27@gmail.com>
*/
class CLI_Interface
{
protected function execCommand()
{
if (isset($this->commands[$this->command])) {
$method = $this->commands[$this->command];
$rcode = $this->$method();
}else
$rcode = $this->error("Command <$this->command> doesn't exist. Try <help>");
return ($rcode == null) ? 0 : $rcode;
}
protected function gopt($no)
{
if(isset($this->options[$no]))
return $this->options[$no];
return null;
}
protected function glopt($opt)
{
foreach ($this->options as $option) {
if(strpos($option, "--$opt=") !== false)
return trim(end(explode('=', $option)), '"');
}
return null;
}
protected function reqopt($no, $name)
{
if($this->gopt($no) == null)
exit($this->error("Param $name is required. Try <help>"));
return $this->gopt($no);
}
protected function error($message = '', $errno = 1)
{
if($message != '')
echo Color::str("ERROR: $message\n", Empir::ERR_COLOR);
return $errno;
}
protected function success($message)
{
echo Color::str("$message\n", Empir::SUCCESS_COLOR);
}
protected function makeAbsolut($path='')
{
$current = getcwd().'/';
if($path == "" || $path == false)
$absolut_path = $current;
elseif(substr($path, 0, 2) == './')
$absolut_path = $current.substr($path,2);
elseif(strpos($path, ':') !== false || substr($path, 0, 2) == '\\\\' || substr($path, 0, 1) == '/')
$absolut_path = $path;
else
$absolut_path = $current.$path;
$absolut_path = str_replace('\\', '/', $absolut_path);
$absolut_path = rtrim($absolut_path, '/');
return $absolut_path;
}
private function find_opt($opt)
{
}
}
/**
* Manage phar
*
* @package Empir
* @author Jeremy Perret <j.perret.27@gmail.com>
*/
class Empir extends CLI_Interface
{
const VERSION = '1.0.0';
const ERR_COLOR = 'red';
const HELP_COLOR = 'green';
const PARAM_COLOR = 'purple';
const SUCCESS_COLOR = 'green';
public $options = array();
public $command;
public $commands = array(
'help' => 'help',
'?' => 'help',
'-h' => 'help',
'make' => 'make',
'convert' => 'convert',
'extract' => 'extract'
);
public $compression_types = array('gz', 'bz2', 'no');
public $format_types = array('phar', 'tar', 'zip');
public function __construct($argv)
{
$this->options = array_slice($argv, 1);
$vars = array(
'gz' => array(
'name' => 'gz',
'extension' => 'zlib',
'mime' => '.gz',
'int_value' => Phar::GZ,
),
'bz2' => array(
'name' => 'bz2',
'extension' => 'bzip2',
'mime' => '.bz2',
'int_value' => Phar::BZ2,
),
'no' => array(
'name' => 'no',
'extension' => 'not',
'mime' => '',
'int_value' => Phar::NONE,
),
'phar' => array(
'name' => 'phar',
'mime' => '.phar',
'int_value' => Phar::PHAR,
'compression' => true
),
'tar' => array(
'name' => 'tar',
'mime' => '.tar',
'int_value' => Phar::TAR,
'compression' => true
),
'zip' => array(
'name' => 'zip',
'mime' => '.zip',
'int_value' => Phar::ZIP,
'compression' => false
)
);
foreach($vars as $name => $attrs)
$this->$name = $this->array_to_object($attrs);
}
public function run()
{
$this->command = ($this->gopt(0) != null) ? $this->gopt(0) : 'help';
$this->options = array_slice($this->options, 1);
return $this->execCommand();
}
public function make()
{
$this->_is_phar_writable();
$phar = $this->makeAbsolut($this->reqopt(0, 'phar filename'));
$phar_name = end(explode('/', $phar));
$stub_file = trim($this->reqopt(1, 'stub file'), '/');
$root_app = $this->makeAbsolut($this->reqopt(2, 'root dir of your app'));
$_compression = $this->glopt('compress') ?: 'no';
$_format = $this->glopt('format') ?: 'phar';
$_exclude = $this->glopt('exclude');
$_fexclude = $this->glopt('fexclude');
if(!file_exists($root_app)) return $this->error("Root dir of your app doesn't exist.");
if(!empty($_compression) && !in_array($_compression, $this->compression_types)) return $this->error("Unrecognized compression: $_compression");
if(!empty($_format) && !in_array($_format, $this->format_types)) return $this->error("Unrecognized format: $_format");
if (!empty($_fexclude)) {
$_fexclude = $this->makeAbsolut($_fexclude);
if(!file_exists($_fexclude)) return $this->error("Exclude file: $_fexclude not found.");
$_fexclude = file_get_contents($_fexclude);
}
$shell_masks = explode('|', $_exclude);
$shell_masks = array_merge($shell_masks, explode("\n", $_fexclude));
$c = $this->get_var($_compression);
$f = $this->get_var($_format);
if (file_exists($phar)) {
unlink($phar);
}
try {
$p = new Phar($phar, Phar::CURRENT_AS_FILEINFO | Phar::KEY_AS_FILENAME, $phar_name);
echo "Make $phar_name : \n===================\n";
$project = json_decode(file_get_contents('composer.json'));
$p->setStub(
"#!/usr/bin/env php \n"
. "<?php\n"
. 'define("IN_PHAR", true);' . "\n"
. '$project = (object) array(' . "\n"
. " 'description' => '$project->description', " . "\n"
. " 'version' => '$project->version'," . "\n"
. " 'selfupdatepath' => '$project->selfupdatepath'," . "\n"
. " 'selfupdateversion' => '$project->selfupdateversion'," . "\n"
. ");" . "\n"
. "Phar::mapPhar(); " . "\n"
. "include 'phar://".$phar_name."/".$stub_file."'; " . "\n"
. "__HALT_COMPILER(); " . "\n"
. "?>" . "\n"
);
$files = $this->_scandir($root_app);
$i=0;
foreach ($files as $file) {
$file_buff = $file;
$file = str_replace('\\', '/', $file);
$file = str_replace($root_app.'/', '', $file);
if (!$this->_exclude($file, $shell_masks) && !$this->_exclude($file, array('*/'.$phar_name, $phar_name))) {
echo "add $file\n";
//$p[$file] = php_strip_whitespace($file_buff);
$p[$file] = file_get_contents($file_buff);
$i++;
}
}
echo "\nTotal: $i files added\n";
if ($f->name == 'phar' && $c->name='no') {
$this->success("CREATE $phar");
return;
}
if(!Phar::canCompress($c->int_value)) return $this->error("Unable to compress the phar with $c->name, extension $c->extension not found. But $phar is created.");
if(!$f->compression) $c->int_value = Phar::NONE;
$phar_copy = $phar.$f->mime.$c->mime;
@unlink($phar_copy);
$p = $p->convertToExecutable($f->int_value, $c->int_value);
$this->success("CREATE $phar_copy");
@unlink($phar);
} catch (Exception $e) {
return $this->error($e->getMessage());
}
}
public function help()
{
$help = new Help();
$command = $this->gopt(0);
if(empty($command)) return $help->main();
switch ($command) {
case 'make': $help->make(); break;
default: return $this->error("Command: $command doesn't exist."); break;
}
}
private function _is_phar_writable()
{
if(!Phar::canWrite()) exit($this->error("Unable to write phar, phar.readonly must be set to zero in your php.ini otherwise use: $ php -dphar.readonly=0 empir <command> ..."));
}
private function get_var($var)
{
if (is_string($var)) {
if(isset($this->$var))
return $this->$var;
} else {
foreach (array_merge($this->compression_types, $this->format_types) as $v) {
if($this->$v->int_value == $var)
return $this->$v;
}
}
}
private function _exclude($file, $shell_masks)
{
if (!empty($shell_masks)) {
foreach ($shell_masks as $mask) {
if(fnmatch(trim($mask), $file))
return true;
}
}
return false;
}
private function _scandir($path)
{
$items = array();
$path = rtrim($path, '/');
if (!$current_dir = opendir($path))
return $items;
while (false !== ($filename = readdir($current_dir))) {
if ($filename != "." && $filename != "..") {
if (is_dir($path.'/'.$filename)) {
$items = array_merge($items, $this->_scandir($path.'/'.$filename));
} else
$items[] = $path.'/'.$filename;
}
}
closedir($current_dir);
return $items;
}
private function array_to_object($array)
{
$object = new stdClass();
foreach ($array as $name => $value) {
$name = strtolower(trim($name));
if (!empty($name))
$object->$name = $value;
}
return $object;
}
}
/**
* Colorizer
*
* @package Empir
* @author Jeremy Perret <j.perret.27@gmail.com>
*/
class Color
{
public static $foreground_colors = array(
'black' => '0;30',
'dark_gray' => '1;30',
'blue' => '0;34',
'light_blue' => '1;34',
'green' => '0;32',
'light_green' => '1;32',
'cyan' => '0;36',
'light_cyan' => '1;36',
'red' => '0;31',
'light_red' => '1;31',
'purple' => '0;35',
'light_purple' => '1;35',
'brown' => '0;33',
'yellow' => '1;33',
'light_gray' => '0;37',
'white' => '1;37'
);
public static $background_colors = array(
'black' => '40',
'red' => '41',
'green' => '42',
'yellow' => '43',
'blue' => '44',
'magenta' => '45',
'cyan' => '46',
'light_gray' => '47'
);
public static function str($string, $foreground_color = null, $background_color = null)
{
if(!self::isTermSupportColor()) return $string;
$colored_string = "";
if (isset(self::$foreground_colors[$foreground_color])) {
$colored_string .= "\033[".self::$foreground_colors[$foreground_color]."m";
}
if (isset(self::$background_colors[$background_color])) {
$colored_string .= "\033[".self::$background_colors[$background_color]."m";
}
$colored_string .= $string."\033[0m";
return $colored_string;
}
public static function random($string)
{
$index_foreground = array_rand(self::$foreground_colors, 1);
$index_background= array_rand(self::$background_colors, 1);
return self::str($string, $index_foreground, $index_background);
}
public static function isTermSupportColor()
{
$term = getenv('TERM');
if($term && ACTIVATE_COLORS) return true;
return false;
}
}
/**
* All differents helps.
*
* @package Empir
* @author Jeremy Perret <j.perret.27@gmail.com>
*/
class Help
{
public function main()
{
echo "Empir v".Empir::VERSION." 2010 (c) Jeremy Perret <j.perret.27@gmail.com>
Empir is a php tool to manage phar.
The setting phar.readonly must be 0 in your php.ini,
otherwise use $ php -dphar.readonly=0 empir <command> ...
If you use Empir from PEAR installation don't care about this php option,
it used directly in the executable file.
".Color::str('Usage', Empir::HELP_COLOR).":
$ php empir <command> <parameters> [options]
".Color::str('Commands', Empir::HELP_COLOR).":
".Color::str('make', Empir::PARAM_COLOR)." Create a phar from an entire php application.
For more help on a command use 'empir help <command>'
";
}
public function make()
{
echo "Command make allows to create a phar file from an entire php application from its root directory.
".Color::str('Usage', Empir::HELP_COLOR).":
$ php empir make <phar_file> <stub_file> <root_app> [options]
".Color::str('Parameters', Empir::HELP_COLOR).":
".Color::str('phar_file', Empir::PARAM_COLOR)." Phar file that will be created, accept absolute or relative path.
".Color::str('stub_file', Empir::PARAM_COLOR)." Bootstrap file of your application, from your root app folder.
".Color::str('root_app', Empir::PARAM_COLOR)." Root folder of your application, accept absolute or relative path.
".Color::str('Options', Empir::HELP_COLOR).":
".Color::str('--exclude=PATTERN', Empir::PARAM_COLOR)." Exclude files match PATTERN, seperate several patterns with a pipe.
".Color::str('--fexclude=FILE', Empir::PARAM_COLOR)." Exclude patterns listed in FILE. One pattern per line.
".Color::str('--format=FORMAT', Empir::PARAM_COLOR)." Special phar format, FORMAT can be tar or zip. Don't specify format to keep normal phar.
".Color::str('--compress=TYPE', Empir::PARAM_COLOR)." Specify the phar compression type. TYPE can be gz or bz2.
";
}
}

150
build/lint Executable file
View File

@ -0,0 +1,150 @@
#!/usr/bin/php
<?php
// ===========
// = Globals =
// ===========
$count = 0; // total files checked
$errors = array();
$options = setOptions(array(
'quiet' => false,
'recurse' => false,
));
if ($options['quiet']) {
ob_start();
}
// =============
// = Scan path =
// =============
$files = getPipedFiles();
$path = $_SERVER['PWD']; // Default to execution directory
// Piped files present
if ($files) {
foreach ($files as $file) {
checkFile("$path/$file");
}
}
// Use arguments
else {
if ($_SERVER['argc'] > 1) {
$last = end($_SERVER['argv']);
if (substr($last, 0, 1) != '-') {
$path = $last; // snag last argument, if it wasn't an option switch
}
}
if (is_dir($path)) {
checkDirectoryContents($path);
}
elseif (is_file($path)) {
checkFile($path);
}
else {
echo "$path is not a file or directory.\n";
showHelp() AND exit(1);
}
}
if ($options['quiet']) {
ob_end_clean();
}
echo "\n$count files checked, " . count($errors) . ' errors.';
echo "\n", implode($errors,'');
function checkDirectoryContents($dir) {
global $options, $i, $errors, $count;
$contents = scandir($dir);
foreach($contents as $content) {
if ($content == '.' || $content == '..') {
continue;
}
$path = "$dir/$content";
// Recurse into directories
if (is_dir($path) && $options['recurse']) {
checkDirectoryContents($path);
} // if is_dir
else {
checkFile($path);
} // !is_dir
} // foreach
} // function checkDirectoryContents
function checkFile($path) {
global $count, $errors;
// echo "$path\n";
// Skip non-php files
if (substr($path, -4) != '.php') {
return false;
}
if (($count % 60 == 0)) {
echo "\n";
}
$error = `php -l $path 2>&1 1> /dev/null`;
if ($error) {
$errors[] = $error;
echo 'E';
}
else {
echo '.';
}
$count++;
}
function getPipedFiles() {
$files = array();
stream_set_blocking(STDIN,FALSE);
while ($line = trim(fgets(STDIN))) {
$files[] = $line;
}
return $files;
}
function setOptions($options) {
$args = array_keys(getopt('qRh', array('quiet', 'recursive', 'help')));
foreach ($args as $arg) {
switch ($arg) {
case 'q':
case 'quiet':
$options['quiet'] = true;
break;
case 'R':
case 'recursive':
$options['recurse'] = true;
break;
case 'h':
case 'help':
default:
showHelp() AND exit(0);
} // Switch
} // Foreach args
return $options;
} // function setOptions
function showHelp() {
echo <<<HELP
usage: lint [-qR] [path]
options:
-q, --quiet: disable verbose output
-R, --recursive: recurse into subdirectories
-h, --help: display this help screen
HELP;
return true;
}

View File

@ -25,5 +25,12 @@
],
"minimum-stability": "dev",
"require": {
},
"require-dev": {
"symfony/console": "v2.3.4",
"symfony/config": "v2.3.4",
"symfony/yaml": "v2.3.4",
"leafo/lessphp": "v0.4.0",
"michelf/php-markdown": "1.4.0"
}
}

312
composer.lock generated Normal file
View File

@ -0,0 +1,312 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "54ae642f6e4dd0e18885aed349b99457",
"packages": [
],
"packages-dev": [
{
"name": "leafo/lessphp",
"version": "v0.4.0",
"source": {
"type": "git",
"url": "https://github.com/leafo/lessphp.git",
"reference": "51f3f06f0fe78a722dabfd14578444bdd078d9de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/leafo/lessphp/zipball/51f3f06f0fe78a722dabfd14578444bdd078d9de",
"reference": "51f3f06f0fe78a722dabfd14578444bdd078d9de",
"shasum": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.3-dev"
}
},
"autoload": {
"classmap": [
"lessc.inc.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT",
"GPL-3.0"
],
"authors": [
{
"name": "Leaf Corcoran",
"email": "leafot@gmail.com",
"homepage": "http://leafo.net"
}
],
"description": "lessphp is a compiler for LESS written in PHP.",
"homepage": "http://leafo.net/lessphp/",
"time": "2013-08-09 17:09:19"
},
{
"name": "michelf/php-markdown",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-markdown.git",
"reference": "96d8150406f67e683ef4acc09fef91785fef1266"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/96d8150406f67e683ef4acc09fef91785fef1266",
"reference": "96d8150406f67e683ef4acc09fef91785fef1266",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-lib": "1.4.x-dev"
}
},
"autoload": {
"psr-0": {
"Michelf": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "http://michelf.ca/",
"role": "Developer"
},
{
"name": "John Gruber",
"homepage": "http://daringfireball.net/"
}
],
"description": "PHP Markdown",
"homepage": "http://michelf.ca/projects/php-markdown/",
"keywords": [
"markdown"
],
"time": "2013-11-29 17:09:24"
},
{
"name": "symfony/config",
"version": "v2.3.4",
"target-dir": "Symfony/Component/Config",
"source": {
"type": "git",
"url": "https://github.com/symfony/Config.git",
"reference": "65a927c15ca5a911ba2fa277a5457fa8129505b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Config/zipball/65a927c15ca5a911ba2fa277a5457fa8129505b0",
"reference": "65a927c15ca5a911ba2fa277a5457fa8129505b0",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/filesystem": "~2.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Config\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Config Component",
"homepage": "http://symfony.com",
"time": "2013-08-06 05:49:23"
},
{
"name": "symfony/console",
"version": "v2.3.4",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "db78f8ff7fc9e28d25ff9a0bf6ddf9f0e35fbbe3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/db78f8ff7fc9e28d25ff9a0bf6ddf9f0e35fbbe3",
"reference": "db78f8ff7fc9e28d25ff9a0bf6ddf9f0e35fbbe3",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/event-dispatcher": "~2.1"
},
"suggest": {
"symfony/event-dispatcher": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Console\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com",
"time": "2013-08-17 16:34:49"
},
{
"name": "symfony/filesystem",
"version": "dev-master",
"target-dir": "Symfony/Component/Filesystem",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
"reference": "e81f1b30eb9748c3f8e0de3a92ea210845cff0a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/e81f1b30eb9748c3f8e0de3a92ea210845cff0a9",
"reference": "e81f1b30eb9748c3f8e0de3a92ea210845cff0a9",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Filesystem\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Filesystem Component",
"homepage": "http://symfony.com",
"time": "2014-01-07 13:29:57"
},
{
"name": "symfony/yaml",
"version": "v2.3.4",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "5a279f1b5f5e1045a6c432354d9ea727ff3a9847"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/5a279f1b5f5e1045a6c432354d9ea727ff3a9847",
"reference": "5a279f1b5f5e1045a6c432354d9ea727ff3a9847",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Yaml\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2013-08-24 15:26:22"
}
],
"aliases": [
],
"minimum-stability": "dev",
"stability-flags": [
],
"platform": [
],
"platform-dev": [
]
}

115
pakefile Normal file
View File

@ -0,0 +1,115 @@
<?php
pake_desc('Run the unit tests');
pake_task('test');
pake_desc('Check the code for psr2 standards');
pake_task('sniff');
pake_desc('Run php-cs-fixer on the src directory');
pake_task('fixer');
pake_desc('Update the README with the latest command output');
pake_task('readme');
pake_desc('Build phar file');
pake_task('phar');
pake_desc('PHP Lint the src folder');
pake_task('lint');
pake_desc('Display the version');
pake_task('version');
pake_desc('Create the selfupdate version file');
pake_task('version_file');
pake_desc('Copy to ~/bin');
pake_task('mv');
pake_desc('Build the app for deployment');
pake_task('build', 'version', 'version_file', 'readme', 'lint', 'fixer', 'sniff', 'phar');
pake_alias('default', 'build');
function run_build() {}
function run_test() {
passthru("phpunit");
}
function run_version() {
$composer = json_decode(file_get_contents('composer.json'));
echo "\n Building FogBugz Command Line Client version " . $composer->version . "\n";
echo str_repeat("=", 80) . "\n";
}
function run_version_file() {
$composer = json_decode(file_get_contents('composer.json'));
file_put_contents('./version', $composer->version);
}
function run_lint() {
echo "\n * Linting files\n";
passthru("./build/lint -R ./src");
}
function run_phar()
{
echo " * Construction phar and moving to fb\n";
$command =
'rm -f fb && rm -f fb.phar &&'
. 'php -dphar.readonly=0 build/empir make fb.phar working.php . --exclude="'
. '*.git/*|*.gitignore|*test*|*Tests*|*.md|*/doc/*|*.lock|*token.txt|pakefile'
. '|.*|build/*|*.markdown|*.phar|*LICENSE|*AUTHORS|*CHANGELOG|*.dist|*.tpl'
. '" && chmod a+x fb.phar'
. ' && mv fb.phar fb';
passthru($command);
}
function run_sniff()
{
echo " * Checking files for PSR2\n";
passthru("phpcs -p --standard=PSR2 ./src/ ./working.php");
}
function run_fixer()
{
echo "\n * Running php-cs-fixer\n";
passthru(
"php-cs-fixer fix ./working.php"
. " && php-cs-fixer fix ./src/FogBugz/Cli/"
. " && php-cs-fixer fix ./src/FogBugz/Command/"
);
}
function run_readme()
{
echo " * Updating README documentation\n";
$readme = file("README.md");
$help = explode("\n", shell_exec("php ./working.php list --no-interaction"));
$helpStart = $helpEnd = 0;
foreach ($readme as $lineNumber => $line) {
if (trim($line) == "## Help") {
$helpStart = $lineNumber;
continue;
}
if ($helpStart && (substr(trim($line), 0, 2) == "##")) {
$helpEnd = $lineNumber;
break;
}
}
$output = join(array_slice($readme, 0, $helpStart + 1));
$output .= "\n " . implode("\n ", $help) . "\n";
$output .= join(array_slice($readme, $helpEnd));
file_put_contents("README.md", $output);
}
function run_mv() {
exec('cp ./fb ~/bin/fb');
}
/* End of pakefile */

9
phpunit.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="FogBugz Client Test Suite">
<directory suffix="Test.php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

127
resume/sample_long.md Normal file
View File

@ -0,0 +1,127 @@
# Craig Davis
## Senior PHP Developer, UX Director
> [Download PDF](resume.pdf)
> [craig@there4development.com](craig@there4development.com)
> (999) 888-7777
------
### Profile {#profile}
Progressively evolve cross-platform ideas before impactful infomediaries. Energistically visualize tactical initiatives before cross-media catalysts for change.
------
### Skills {#skills}
* Web Design
: Assertively exploit wireless initiatives rather than synergistic core competencies.
* Interface Design
: Credibly streamline mission-critical value with multifunctional functionalities.
* Project Direction
: Proven ability to lead and manage a wide variety of design and development projects in team and independent situations.
-------
### Technical {#technical}
1. XHTML
1. CSS
1. Javascript
1. Jquery
1. PHP
1. CVS / Subversion
1. OS X
1. Windows XP/Vista
1. Linux
------
### Experience {#experience}
Initrode Conglomerated
: *Principal and Creative Lead*
__2004-2005__
Intrinsicly transform flexible manufactured products without excellent intellectual capital. Energistically evisculate orthogonal architectures through covalent action items. Assertively incentivize sticky platforms without synergistic materials.
Gizmonic Institute Company (GIM)
: *Lead Web Designer*
__2001-2004__
Globally re-engineer cross-media schemas through viral methods of empowerment. Proactively grow long-term high-impact human capital and highly efficient innovation. Intrinsicly iterate excellent e-tailers with timely e-markets.
Initrode Conglomerated
: *Principal and Creative Lead*
__2004-2005__
Intrinsicly transform flexible manufactured products without excellent intellectual capital. Energistically evisculate orthogonal architectures through covalent action items. Assertively incentivize sticky platforms without synergistic materials.
Gizmonic Institute Company (GIM)
: *Lead Web Designer*
__2001-2004__
Globally re-engineer cross-media schemas through viral methods of empowerment. Proactively grow long-term high-impact human capital and highly efficient innovation. Intrinsicly iterate excellent e-tailers with timely e-markets.
Initrode Conglomerated
: *Principal and Creative Lead*
__2004-2005__
Intrinsicly transform flexible manufactured products without excellent intellectual capital. Energistically evisculate orthogonal architectures through covalent action items. Assertively incentivize sticky platforms without synergistic materials.
Gizmonic Institute Company (GIM)
: *Lead Web Designer*
__2001-2004__
Globally re-engineer cross-media schemas through viral methods of empowerment. Proactively grow long-term high-impact human capital and highly efficient innovation. Intrinsicly iterate excellent e-tailers with timely e-markets.
Initrode Conglomerated
: *Principal and Creative Lead*
__2004-2005__
Intrinsicly transform flexible manufactured products without excellent intellectual capital. Energistically evisculate orthogonal architectures through covalent action items. Assertively incentivize sticky platforms without synergistic materials.
Gizmonic Institute Company (GIM)
: *Lead Web Designer*
__2001-2004__
Globally re-engineer cross-media schemas through viral methods of empowerment. Proactively grow long-term high-impact human capital and highly efficient innovation. Intrinsicly iterate excellent e-tailers with timely e-markets.
Initrode Conglomerated
: *Principal and Creative Lead*
__2004-2005__
Intrinsicly transform flexible manufactured products without excellent intellectual capital. Energistically evisculate orthogonal architectures through covalent action items. Assertively incentivize sticky platforms without synergistic materials.
Gizmonic Institute Company (GIM)
: *Lead Web Designer*
__2001-2004__
Globally re-engineer cross-media schemas through viral methods of empowerment. Proactively grow long-term high-impact human capital and highly efficient innovation. Intrinsicly iterate excellent e-tailers with timely e-markets.
Initrode Conglomerated
: *Principal and Creative Lead*
__2004-2005__
Intrinsicly transform flexible manufactured products without excellent intellectual capital. Energistically evisculate orthogonal architectures through covalent action items. Assertively incentivize sticky platforms without synergistic materials.
Gizmonic Institute Company (GIM)
: *Lead Web Designer*
__2001-2004__
Globally re-engineer cross-media schemas through viral methods of empowerment. Proactively grow long-term high-impact human capital and highly efficient innovation. Intrinsicly iterate excellent e-tailers with timely e-markets.
Initrode Conglomerated
: *Principal and Creative Lead*
__2004-2005__
Intrinsicly transform flexible manufactured products without excellent intellectual capital. Energistically evisculate orthogonal architectures through covalent action items. Assertively incentivize sticky platforms without synergistic materials.
Gizmonic Institute Company (GIM)
: *Lead Web Designer*
__2001-2004__
Globally re-engineer cross-media schemas through viral methods of empowerment. Proactively grow long-term high-impact human capital and highly efficient innovation. Intrinsicly iterate excellent e-tailers with timely e-markets.
------
### Footer {#footer}
Craig Davis -- [craig@there4development.com](craig@there4development.com) -- (999) 888-7777
------

263
src/Resume/Cli/Resume.php Normal file
View File

@ -0,0 +1,263 @@
<?php
namespace FogBugz\Cli;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Yaml\Yaml;
use There4\FogBugz;
use FogBugz\Cli;
use FogBugz\Command;
class Working extends Application
{
public $recentCaseLimit = 10;
public $configFile;
public function initialize($configFile, $templatePath, $project)
{
$runSetup = false;
$this->configFile = $configFile;
// Add the composer information for use in version info and such.
$this->project = $project;
// Load our application config information
if (file_exists($configFile)) {
$this->config = Yaml::parse($configFile);
} else {
$runSetup = true;
$this->config = $this->getDefaultConfig();
}
// https://github.com/symfony/Console/blob/master/Output/Output.php
$this->outputFormat
= $this->config['UseColor']
? OutputInterface::OUTPUT_NORMAL
: OutputInterface::OUTPUT_PLAIN;
// We do this now because we've loaded the project info from the composer file
$this->setName($this->project->description);
$this->setVersion($this->project->version);
// Load our commands into the application
$this->add(new Command\AssignCommand());
$this->add(new Command\CasesCommand());
$this->add(new Command\CloseCommand());
$this->add(new Command\CurrentCommand());
$this->add(new Command\EstimateCommand());
$this->add(new Command\FiltersCommand());
$this->add(new Command\LoginCommand());
$this->add(new Command\LogoutCommand());
$this->add(new Command\NoteCommand());
$this->add(new Command\OpenCommand());
$this->add(new Command\ParentCommand());
$this->add(new Command\ReactivateCommand());
$this->add(new Command\RecentCommand());
$this->add(new Command\ReopenCommand());
$this->add(new Command\ResolveCommand());
$this->add(new Command\SearchCommand());
$this->add(new Command\SelfUpdateCommand());
$this->add(new Command\SetFilterCommand());
$this->add(new Command\SetupCommand());
$this->add(new Command\StartCommand());
$this->add(new Command\StarCommand());
$this->add(new Command\StopCommand());
$this->add(new Command\UnstarCommand());
$this->add(new Command\VersionCommand());
$this->add(new Command\ViewCommand());
// We'll use [Twig](http://twig.sensiolabs.org/) for template output
$loader = new \Twig_Loader_Filesystem($templatePath);
$this->twig = new \Twig_Environment(
$loader,
array(
"cache" => false,
"autoescape" => false,
"strict_variables" => false // SET TO TRUE WHILE DEBUGGING
)
);
// These are helpers that we use to format output on the cli: styling and padding and such
$this->twig->addFilter('pad', new \Twig_Filter_Function("FogBugz\Cli\TwigFormatters::strpad"));
$this->twig->addFilter('style', new \Twig_Filter_Function("FogBugz\Cli\TwigFormatters::style"));
$this->twig->addFilter('repeat', new \Twig_Filter_Function("str_repeat"));
$this->twig->addFilter('wrap', new \Twig_Filter_Function("wordwrap"));
// If the config file is empty, run the setup script here
// If the config file version is a different major number, run the setup script here
$currentVersion = explode('.', $this->project->version);
$configVersion = explode('.', $this->config['ConfigVersion']);
$majorVersionChange = $currentVersion[0] != $configVersion[0];
// We need to be able to skip setup for the list and help
$helpRequested = (
empty($_SERVER['argv'][1]) ||
($_SERVER['argv'][1] == 'list') ||
($_SERVER['argv'][1] == 'help')
);
if (($runSetup || $majorVersionChange) && !$helpRequested) {
$command = $this->find('setup');
$arguments = array(
'command' => 'setup'
);
$input = new ArrayInput($arguments);
$command->run($input, new ConsoleOutput());
}
}
public function getLongVersion()
{
return parent::getLongVersion().' by <comment>Craig Davis</comment>';
}
public function getDefaultConfig()
{
return array(
'ConfigVersion' => '0.0.1',
'UseColor' => true,
'Host' => '',
'User' => '',
'AuthToken' => '',
'RecentCases' => array()
);
}
public function getCurrent($user = '')
{
if ($user === '') {
$user = $this->fogbugz->user;
}
$xml = $this->fogbugz->viewPerson(array('sEmail' => $user));
return (int) $xml->people->person->ixBugWorkingOn;
}
public function getRecent()
{
return
is_array($this->config['RecentCases'])
? $this->config['RecentCases']
: array();
}
public function pushRecent($case, $title)
{
$recentCases = $this->getRecent();
array_push(
$recentCases,
array(
"id" => $case,
"title" => $title
)
);
// Only keep the last x number of cases in the list
$this->config['RecentCases'] = array_slice($recentCases, -1 * $this->recentCaseLimit);
$this->saveConfig();
return true;
}
public function saveConfig()
{
// the second param is the depth for starting yaml inline formatting
$yaml = Yaml::dump($this->config, 2);
return file_put_contents($this->configFile, $yaml);
}
public function registerStyles(&$output)
{
// https://github.com/symfony/Console/blob/master/Formatter/OutputFormatterStyle.php
// http://symfony.com/doc/2.0/components/console/introduction.html#coloring-the-output
//
// * <info></info> green
// * <comment></comment> yellow
// * <question></question> black text on a cyan background
// * <alert></alert> yellow
// * <error></error> white text on a red background
// * <fire></fire> red text on a yellow background
// * <notice></notice> blue
// * <heading></heading> black on white
$style = new OutputFormatterStyle('red', 'yellow', array('bold'));
$output->getFormatter()->setStyle('fire', $style);
$style = new OutputFormatterStyle('blue', 'black', array());
$output->getFormatter()->setStyle('notice', $style);
$style = new OutputFormatterStyle('red', 'black', array('bold'));
$output->getFormatter()->setStyle('alert', $style);
$style = new OutputFormatterStyle('white', 'black', array('bold'));
$output->getFormatter()->setStyle('bold', $style);
$style = new OutputFormatterStyle('black', 'white', array());
$output->getFormatter()->setStyle('heading', $style);
$style = new OutputFormatterStyle('blue', 'black', array('bold'));
$output->getFormatter()->setStyle('logo', $style);
return $output;
}
public function statusStyle($status)
{
switch (true) {
case (strpos(strtolower($status), 'closed') === 0):
return 'alert';
case (strpos(strtolower($status), 'open') === 0):
case (strpos(strtolower($status), 'active') === 0):
return 'logo';
// fallthrough to final return
}
return "info";
}
public function run(InputInterface $input = null, OutputInterface $output = null)
{
if (null === $input) {
$input = new ArgvInput();
}
if (null === $output) {
$output = new ConsoleOutput();
}
$this->registerStyles($output);
// Did they supply a command name?
$name = $this->getCommandName($input);
if ($name) {
// Does the command exist and is not ambiguous?
try {
$command = $this->find($name);
} catch (\Exception $e) {
exit($e->getMessage() . "\n");
}
// Does the command require authentication?
if (property_exists($command, "requireAuth") && $command->requireAuth) {
$simple_input = new ArgvInput(
array(
$_SERVER['argv'][0],
$_SERVER['argv'][1],
"--quiet"
)
);
$login = $this->find('login');
$returnCode = $login->run($simple_input, $output);
}
}
return parent::run($input, $output);
}
}
/* End of file Working.php */

View File

@ -0,0 +1,31 @@
<?php
namespace FogBugz\Command;
use FogBugz\Cli\AuthCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class VersionCommand extends AuthCommand
{
public function __construct()
{
parent::__construct();
}
protected function configure()
{
$this
->setName('version')
->setDescription('Show version information')
->requireAuth(false);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->app = $this->getApplication();
$output->writeln($this->app->project->version, $this->app->outputFormat);
}
}
/* End of file VersionCommand.php */

View File

@ -1,163 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
use Assetic\Cache\CacheInterface;
use Assetic\Filter\FilterInterface;
/**
* Caches an asset to avoid the cost of loading and dumping.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class AssetCache implements AssetInterface
{
private $asset;
private $cache;
public function __construct(AssetInterface $asset, CacheInterface $cache)
{
$this->asset = $asset;
$this->cache = $cache;
}
public function ensureFilter(FilterInterface $filter)
{
$this->asset->ensureFilter($filter);
}
public function getFilters()
{
return $this->asset->getFilters();
}
public function clearFilters()
{
$this->asset->clearFilters();
}
public function load(FilterInterface $additionalFilter = null)
{
$cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'load');
if ($this->cache->has($cacheKey)) {
$this->asset->setContent($this->cache->get($cacheKey));
return;
}
$this->asset->load($additionalFilter);
$this->cache->set($cacheKey, $this->asset->getContent());
}
public function dump(FilterInterface $additionalFilter = null)
{
$cacheKey = self::getCacheKey($this->asset, $additionalFilter, 'dump');
if ($this->cache->has($cacheKey)) {
return $this->cache->get($cacheKey);
}
$content = $this->asset->dump($additionalFilter);
$this->cache->set($cacheKey, $content);
return $content;
}
public function getContent()
{
return $this->asset->getContent();
}
public function setContent($content)
{
$this->asset->setContent($content);
}
public function getSourceRoot()
{
return $this->asset->getSourceRoot();
}
public function getSourcePath()
{
return $this->asset->getSourcePath();
}
public function getTargetPath()
{
return $this->asset->getTargetPath();
}
public function setTargetPath($targetPath)
{
$this->asset->setTargetPath($targetPath);
}
public function getLastModified()
{
return $this->asset->getLastModified();
}
public function getVars()
{
return $this->asset->getVars();
}
public function setValues(array $values)
{
$this->asset->setValues($values);
}
public function getValues()
{
return $this->asset->getValues();
}
/**
* Returns a cache key for the current asset.
*
* The key is composed of everything but an asset's content:
*
* * source root
* * source path
* * target url
* * last modified
* * filters
*
* @param AssetInterface $asset The asset
* @param FilterInterface $additionalFilter Any additional filter being applied
* @param string $salt Salt for the key
*
* @return string A key for identifying the current asset
*/
static private function getCacheKey(AssetInterface $asset, FilterInterface $additionalFilter = null, $salt = '')
{
if ($additionalFilter) {
$asset = clone $asset;
$asset->ensureFilter($additionalFilter);
}
$cacheKey = $asset->getSourceRoot();
$cacheKey .= $asset->getSourcePath();
$cacheKey .= $asset->getTargetPath();
$cacheKey .= $asset->getLastModified();
foreach ($asset->getFilters() as $filter) {
$cacheKey .= serialize($filter);
}
if ($values = $asset->getValues()) {
asort($values);
$cacheKey .= serialize($values);
}
return md5($cacheKey.$salt);
}
}

View File

@ -1,217 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
use Assetic\Asset\Iterator\AssetCollectionFilterIterator;
use Assetic\Asset\Iterator\AssetCollectionIterator;
use Assetic\Filter\FilterCollection;
use Assetic\Filter\FilterInterface;
/**
* A collection of assets.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class AssetCollection implements \IteratorAggregate, AssetCollectionInterface
{
private $assets;
private $filters;
private $sourceRoot;
private $targetPath;
private $content;
private $clones;
private $vars;
private $values;
/**
* Constructor.
*
* @param array $assets Assets for the current collection
* @param array $filters Filters for the current collection
* @param string $sourceRoot The root directory
*/
public function __construct($assets = array(), $filters = array(), $sourceRoot = null, array $vars = array())
{
$this->assets = array();
foreach ($assets as $asset) {
$this->add($asset);
}
$this->filters = new FilterCollection($filters);
$this->sourceRoot = $sourceRoot;
$this->clones = new \SplObjectStorage();
$this->vars = $vars;
$this->values = array();
}
public function all()
{
return $this->assets;
}
public function add(AssetInterface $asset)
{
$this->assets[] = $asset;
}
public function removeLeaf(AssetInterface $needle, $graceful = false)
{
foreach ($this->assets as $i => $asset) {
$clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null;
if (in_array($needle, array($asset, $clone), true)) {
unset($this->clones[$asset], $this->assets[$i]);
return true;
} elseif ($asset instanceof AssetCollectionInterface && $asset->removeLeaf($needle, true)) {
return true;
}
}
if ($graceful) {
return false;
}
throw new \InvalidArgumentException('Leaf not found.');
}
public function replaceLeaf(AssetInterface $needle, AssetInterface $replacement, $graceful = false)
{
foreach ($this->assets as $i => $asset) {
$clone = isset($this->clones[$asset]) ? $this->clones[$asset] : null;
if (in_array($needle, array($asset, $clone), true)) {
unset($this->clones[$asset]);
$this->assets[$i] = $replacement;
return true;
} elseif ($asset instanceof AssetCollectionInterface && $asset->replaceLeaf($needle, $replacement, true)) {
return true;
}
}
if ($graceful) {
return false;
}
throw new \InvalidArgumentException('Leaf not found.');
}
public function ensureFilter(FilterInterface $filter)
{
$this->filters->ensure($filter);
}
public function getFilters()
{
return $this->filters->all();
}
public function clearFilters()
{
$this->filters->clear();
}
public function load(FilterInterface $additionalFilter = null)
{
// loop through leaves and load each asset
$parts = array();
foreach ($this as $asset) {
$asset->load($additionalFilter);
$parts[] = $asset->getContent();
}
$this->content = implode("\n", $parts);
}
public function dump(FilterInterface $additionalFilter = null)
{
// loop through leaves and dump each asset
$parts = array();
foreach ($this as $asset) {
$parts[] = $asset->dump($additionalFilter);
}
return implode("\n", $parts);
}
public function getContent()
{
return $this->content;
}
public function setContent($content)
{
$this->content = $content;
}
public function getSourceRoot()
{
return $this->sourceRoot;
}
public function getSourcePath()
{
}
public function getTargetPath()
{
return $this->targetPath;
}
public function setTargetPath($targetPath)
{
$this->targetPath = $targetPath;
}
/**
* Returns the highest last-modified value of all assets in the current collection.
*
* @return integer|null A UNIX timestamp
*/
public function getLastModified()
{
if (!count($this->assets)) {
return;
}
$mapper = function (AssetInterface $asset)
{
return $asset->getLastModified();
};
return max(array_map($mapper, $this->assets));
}
/**
* Returns an iterator for looping recursively over unique leaves.
*/
public function getIterator()
{
return new \RecursiveIteratorIterator(new AssetCollectionFilterIterator(new AssetCollectionIterator($this, $this->clones)));
}
public function getVars()
{
return $this->vars;
}
public function setValues(array $values)
{
$this->values = $values;
foreach ($this as $asset) {
$asset->setValues(array_intersect_key($values, array_flip($asset->getVars())));
}
}
public function getValues()
{
return $this->values;
}
}

View File

@ -1,53 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
/**
* An asset collection.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
interface AssetCollectionInterface extends AssetInterface, \Traversable
{
/**
* Returns all child assets.
*
* @return array An array of AssetInterface objects
*/
function all();
/**
* Adds an asset to the current collection.
*
* @param AssetInterface $asset An asset
*/
function add(AssetInterface $asset);
/**
* Removes a leaf.
*
* @param AssetInterface $needle The leaf to remove
*
* @throws InvalidArgumentException If the asset cannot be found
*/
function removeLeaf(AssetInterface $leaf);
/**
* Replaces an existing leaf with a new one.
*
* @param AssetInterface $needle The current asset to replace
* @param AssetInterface $replacement The new asset
*
* @throws InvalidArgumentException If the asset cannot be found
*/
function replaceLeaf(AssetInterface $needle, AssetInterface $replacement);
}

View File

@ -1,156 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
use Assetic\Filter\FilterInterface;
/**
* An asset has a mutable URL and content and can be loaded and dumped.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
interface AssetInterface
{
/**
* Ensures the current asset includes the supplied filter.
*
* @param FilterInterface $filter A filter
*/
function ensureFilter(FilterInterface $filter);
/**
* Returns an array of filters currently applied.
*
* @return array An array of filters
*/
function getFilters();
/**
* Clears all filters from the current asset.
*/
function clearFilters();
/**
* Loads the asset into memory and applies load filters.
*
* You may provide an additional filter to apply during load.
*
* @param FilterInterface $additionalFilter An additional filter
*/
function load(FilterInterface $additionalFilter = null);
/**
* Applies dump filters and returns the asset as a string.
*
* You may provide an additional filter to apply during dump.
*
* Dumping an asset should not change its state.
*
* If the current asset has not been loaded yet, it should be
* automatically loaded at this time.
*
* @param FilterInterface $additionalFilter An additional filter
*
* @return string The filtered content of the current asset
*/
function dump(FilterInterface $additionalFilter = null);
/**
* Returns the loaded content of the current asset.
*
* @return string The content
*/
function getContent();
/**
* Sets the content of the current asset.
*
* Filters can use this method to change the content of the asset.
*
* @param string $content The asset content
*/
function setContent($content);
/**
* Returns an absolute path or URL to the source asset's root directory.
*
* This value should be an absolute path to a directory in the filesystem,
* an absolute URL with no path, or null.
*
* For example:
*
* * '/path/to/web'
* * 'http://example.com'
* * null
*
* @return string|null The asset's root
*/
function getSourceRoot();
/**
* Returns the relative path for the source asset.
*
* This value can be combined with the asset's source root (if both are
* non-null) to get something compatible with file_get_contents().
*
* For example:
*
* * 'js/main.js'
* * 'main.js'
* * null
*
* @return string|null The source asset path
*/
function getSourcePath();
/**
* Returns the URL for the current asset.
*
* @return string|null A web URL where the asset will be dumped
*/
function getTargetPath();
/**
* Sets the URL for the current asset.
*
* @param string $targetPath A web URL where the asset will be dumped
*/
function setTargetPath($targetPath);
/**
* Returns the time the current asset was last modified.
*
* @return integer|null A UNIX timestamp
*/
function getLastModified();
/**
* Returns an array of variable names for this asset.
*
* @return array
*/
function getVars();
/**
* Sets the values for the asset's variables.
*
* @param array $values
*/
function setValues(array $values);
/**
* Returns the current values for this asset.
*
* @return array an array of strings
*/
function getValues();
}

View File

@ -1,134 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
use Assetic\AssetManager;
use Assetic\Filter\FilterCollection;
use Assetic\Filter\FilterInterface;
/**
* A reference to an asset in the asset manager.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class AssetReference implements AssetInterface
{
private $am;
private $name;
private $filters = array();
public function __construct(AssetManager $am, $name)
{
$this->am = $am;
$this->name = $name;
}
public function ensureFilter(FilterInterface $filter)
{
$this->filters[] = $filter;
}
public function getFilters()
{
$this->flushFilters();
return $this->callAsset(__FUNCTION__);
}
public function clearFilters()
{
$this->filters = array();
$this->callAsset(__FUNCTION__);
}
public function load(FilterInterface $additionalFilter = null)
{
$this->flushFilters();
return $this->callAsset(__FUNCTION__, array($additionalFilter));
}
public function dump(FilterInterface $additionalFilter = null)
{
$this->flushFilters();
return $this->callAsset(__FUNCTION__, array($additionalFilter));
}
public function getContent()
{
return $this->callAsset(__FUNCTION__);
}
public function setContent($content)
{
$this->callAsset(__FUNCTION__, array($content));
}
public function getSourceRoot()
{
return $this->callAsset(__FUNCTION__);
}
public function getSourcePath()
{
return $this->callAsset(__FUNCTION__);
}
public function getTargetPath()
{
return $this->callAsset(__FUNCTION__);
}
public function setTargetPath($targetPath)
{
$this->callAsset(__FUNCTION__, array($targetPath));
}
public function getLastModified()
{
return $this->callAsset(__FUNCTION__);
}
public function getVars()
{
return $this->callAsset(__FUNCTION__);
}
public function getValues()
{
return $this->callAsset(__FUNCTION__);
}
public function setValues(array $values)
{
$this->callAsset(__FUNCTION__, array($values));
}
// private
private function callAsset($method, $arguments = array())
{
$asset = $this->am->get($this->name);
return call_user_func_array(array($asset, $method), $arguments);
}
private function flushFilters()
{
$asset = $this->am->get($this->name);
while ($filter = array_shift($this->filters)) {
$asset->ensureFilter($filter);
}
}
}

View File

@ -1,169 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
use Assetic\Filter\FilterCollection;
use Assetic\Filter\FilterInterface;
/**
* A base abstract asset.
*
* The methods load() and getLastModified() are left undefined, although a
* reusable doLoad() method is available to child classes.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
abstract class BaseAsset implements AssetInterface
{
private $filters;
private $sourceRoot;
private $sourcePath;
private $targetPath;
private $content;
private $loaded;
private $vars;
private $values;
/**
* Constructor.
*
* @param array $filters Filters for the asset
*/
public function __construct($filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array())
{
$this->filters = new FilterCollection($filters);
$this->sourceRoot = $sourceRoot;
$this->sourcePath = $sourcePath;
$this->vars = $vars;
$this->values = array();
$this->loaded = false;
}
public function __clone()
{
$this->filters = clone $this->filters;
}
public function ensureFilter(FilterInterface $filter)
{
$this->filters->ensure($filter);
}
public function getFilters()
{
return $this->filters->all();
}
public function clearFilters()
{
$this->filters->clear();
}
/**
* Encapsulates asset loading logic.
*
* @param string $content The asset content
* @param FilterInterface $additionalFilter An additional filter
*/
protected function doLoad($content, FilterInterface $additionalFilter = null)
{
$filter = clone $this->filters;
if ($additionalFilter) {
$filter->ensure($additionalFilter);
}
$asset = clone $this;
$asset->setContent($content);
$filter->filterLoad($asset);
$this->content = $asset->getContent();
$this->loaded = true;
}
public function dump(FilterInterface $additionalFilter = null)
{
if (!$this->loaded) {
$this->load();
}
$filter = clone $this->filters;
if ($additionalFilter) {
$filter->ensure($additionalFilter);
}
$asset = clone $this;
$filter->filterDump($asset);
return $asset->getContent();
}
public function getContent()
{
return $this->content;
}
public function setContent($content)
{
$this->content = $content;
}
public function getSourceRoot()
{
return $this->sourceRoot;
}
public function getSourcePath()
{
return $this->sourcePath;
}
public function getTargetPath()
{
return $this->targetPath;
}
public function setTargetPath($targetPath)
{
if ($this->vars) {
foreach ($this->vars as $var) {
if (false === strpos($targetPath, $var)) {
throw new \RuntimeException(sprintf('The asset target path "%s" must contain the variable "{%s}".', $targetPath, $var));
}
}
}
$this->targetPath = $targetPath;
}
public function getVars()
{
return $this->vars;
}
public function setValues(array $values)
{
foreach ($values as $var => $v) {
if (!in_array($var, $this->vars, true)) {
throw new \InvalidArgumentException(sprintf('The asset with source path "%s" has no variable named "%s".', $this->sourcePath, $var));
}
}
$this->values = $values;
$this->loaded = false;
}
public function getValues()
{
return $this->values;
}
}

View File

@ -1,78 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
use Assetic\Util\PathUtils;
use Assetic\Filter\FilterInterface;
/**
* Represents an asset loaded from a file.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class FileAsset extends BaseAsset
{
private $source;
/**
* Constructor.
*
* @param string $source An absolute path
* @param array $filters An array of filters
* @param string $sourceRoot The source asset root directory
* @param string $sourcePath The source asset path
*
* @throws InvalidArgumentException If the supplied root doesn't match the source when guessing the path
*/
public function __construct($source, $filters = array(), $sourceRoot = null, $sourcePath = null, array $vars = array())
{
if (null === $sourceRoot) {
$sourceRoot = dirname($source);
if (null === $sourcePath) {
$sourcePath = basename($source);
}
} elseif (null === $sourcePath) {
if (0 !== strpos($source, $sourceRoot)) {
throw new \InvalidArgumentException(sprintf('The source "%s" is not in the root directory "%s"', $source, $sourceRoot));
}
$sourcePath = substr($source, strlen($sourceRoot) + 1);
}
$this->source = $source;
parent::__construct($filters, $sourceRoot, $sourcePath, $vars);
}
public function load(FilterInterface $additionalFilter = null)
{
$source = PathUtils::resolvePath($this->source, $this->getVars(),
$this->getValues());
if (!is_file($source)) {
throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source));
}
$this->doLoad(file_get_contents($source), $additionalFilter);
}
public function getLastModified()
{
$source = PathUtils::resolvePath($this->source, $this->getVars(),
$this->getValues());
if (!is_file($source)) {
throw new \RuntimeException(sprintf('The source file "%s" does not exist.', $source));
}
return filemtime($source);
}
}

View File

@ -1,111 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
use Assetic\Util\PathUtils;
use Assetic\Filter\FilterInterface;
/**
* A collection of assets loaded by glob.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class GlobAsset extends AssetCollection
{
private $globs;
private $initialized;
/**
* Constructor.
*
* @param string|array $globs A single glob path or array of paths
* @param array $filters An array of filters
* @param string $root The root directory
*/
public function __construct($globs, $filters = array(), $root = null, array $vars = array())
{
$this->globs = (array) $globs;
$this->initialized = false;
parent::__construct(array(), $filters, $root, $vars);
}
public function all()
{
if (!$this->initialized) {
$this->initialize();
}
return parent::all();
}
public function load(FilterInterface $additionalFilter = null)
{
if (!$this->initialized) {
$this->initialize();
}
parent::load($additionalFilter);
}
public function dump(FilterInterface $additionalFilter = null)
{
if (!$this->initialized) {
$this->initialize();
}
return parent::dump($additionalFilter);
}
public function getLastModified()
{
if (!$this->initialized) {
$this->initialize();
}
return parent::getLastModified();
}
public function getIterator()
{
if (!$this->initialized) {
$this->initialize();
}
return parent::getIterator();
}
public function setValues(array $values)
{
parent::setValues($values);
$this->initialized = false;
}
/**
* Initializes the collection based on the glob(s) passed in.
*/
private function initialize()
{
foreach ($this->globs as $glob) {
$glob = PathUtils::resolvePath($glob, $this->getVars(), $this->getValues());
if (false !== $paths = glob($glob)) {
foreach ($paths as $path) {
$this->add(new FileAsset($path, array(), $this->getSourceRoot()));
}
}
}
$this->initialized = true;
}
}

View File

@ -1,79 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
use Assetic\Util\PathUtils;
use Assetic\Filter\FilterInterface;
/**
* Represents an asset loaded via an HTTP request.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class HttpAsset extends BaseAsset
{
private $sourceUrl;
private $ignoreErrors;
/**
* Constructor.
*
* @param string $sourceUrl The source URL
* @param array $filters An array of filters
*
* @throws InvalidArgumentException If the first argument is not an URL
*/
public function __construct($sourceUrl, $filters = array(), $ignoreErrors = false, array $vars = array())
{
if (0 === strpos($sourceUrl, '//')) {
$sourceUrl = 'http:'.$sourceUrl;
} elseif (false === strpos($sourceUrl, '://')) {
throw new \InvalidArgumentException(sprintf('"%s" is not a valid URL.', $sourceUrl));
}
$this->sourceUrl = $sourceUrl;
$this->ignoreErrors = $ignoreErrors;
list($scheme, $url) = explode('://', $sourceUrl, 2);
list($host, $path) = explode('/', $url, 2);
parent::__construct($filters, $scheme.'://'.$host, $path, $vars);
}
public function load(FilterInterface $additionalFilter = null)
{
if (false === $content = @file_get_contents(PathUtils::resolvePath(
$this->sourceUrl, $this->getVars(), $this->getValues()))) {
if ($this->ignoreErrors) {
return;
} else {
throw new \RuntimeException(sprintf('Unable to load asset from URL "%s"', $this->sourceUrl));
}
}
$this->doLoad($content, $additionalFilter);
}
public function getLastModified()
{
if (false !== @file_get_contents($this->sourceUrl, false, stream_context_create(array('http' => array('method' => 'HEAD'))))) {
foreach ($http_response_header as $header) {
if (0 === stripos($header, 'Last-Modified: ')) {
list(, $mtime) = explode(':', $header, 2);
return strtotime(trim($mtime));
}
}
}
}
}

View File

@ -1,84 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset\Iterator;
/**
* Asset collection filter iterator.
*
* The filter iterator is responsible for de-duplication of leaf assets based
* on both strict equality and source URL.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class AssetCollectionFilterIterator extends \RecursiveFilterIterator
{
private $visited;
private $sources;
/**
* Constructor.
*
* @param AssetCollectionIterator $iterator The inner iterator
* @param array $visited An array of visited asset objects
* @param array $sources An array of visited source strings
*/
public function __construct(AssetCollectionIterator $iterator, array $visited = array(), array $sources = array())
{
parent::__construct($iterator);
$this->visited = $visited;
$this->sources = $sources;
}
/**
* Determines whether the current asset is a duplicate.
*
* De-duplication is performed based on either strict equality or by
* matching sources.
*
* @return Boolean Returns true if we have not seen this asset yet
*/
public function accept()
{
$asset = $this->getInnerIterator()->current(true);
$duplicate = false;
// check strict equality
if (in_array($asset, $this->visited, true)) {
$duplicate = true;
} else {
$this->visited[] = $asset;
}
// check source
$sourceRoot = $asset->getSourceRoot();
$sourcePath = $asset->getSourcePath();
if ($sourceRoot && $sourcePath) {
$source = $sourceRoot.'/'.$sourcePath;
if (in_array($source, $this->sources)) {
$duplicate = true;
} else {
$this->sources[] = $source;
}
}
return !$duplicate;
}
/**
* Passes visited objects and source URLs to the child iterator.
*/
public function getChildren()
{
return new self($this->getInnerIterator()->getChildren(), $this->visited, $this->sources);
}
}

View File

@ -1,109 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset\Iterator;
use Assetic\Asset\AssetCollectionInterface;
/**
* Iterates over an asset collection.
*
* The iterator is responsible for cascading filters and target URL patterns
* from parent to child assets.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class AssetCollectionIterator implements \RecursiveIterator
{
private $assets;
private $filters;
private $output;
private $clones;
public function __construct(AssetCollectionInterface $coll, \SplObjectStorage $clones)
{
$this->assets = $coll->all();
$this->filters = $coll->getFilters();
$this->output = $coll->getTargetPath();
$this->clones = $clones;
if (false === $pos = strpos($this->output, '.')) {
$this->output .= '_*';
} else {
$this->output = substr($this->output, 0, $pos).'_*'.substr($this->output, $pos);
}
}
/**
* Returns a copy of the current asset with filters and a target URL applied.
*
* @param Boolean $raw Returns the unmodified asset if true
*/
public function current($raw = false)
{
$asset = current($this->assets);
if ($raw) {
return $asset;
}
// clone once
if (!isset($this->clones[$asset])) {
$clone = $this->clones[$asset] = clone $asset;
// generate a target path based on asset name
$name = sprintf('%s_%d', pathinfo($asset->getSourcePath(), PATHINFO_FILENAME) ?: 'part', $this->key() + 1);
$clone->setTargetPath(str_replace('*', $name, $this->output));
} else {
$clone = $this->clones[$asset];
}
// cascade filters
foreach ($this->filters as $filter) {
$clone->ensureFilter($filter);
}
return $clone;
}
public function key()
{
return key($this->assets);
}
public function next()
{
return next($this->assets);
}
public function rewind()
{
return reset($this->assets);
}
public function valid()
{
return false !== current($this->assets);
}
public function hasChildren()
{
return current($this->assets) instanceof AssetCollectionInterface;
}
/**
* @uses current()
*/
public function getChildren()
{
return new self($this->current(), $this->clones);
}
}

View File

@ -1,55 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Asset;
use Assetic\Filter\FilterInterface;
/**
* Represents a string asset.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class StringAsset extends BaseAsset
{
private $content;
private $lastModified;
/**
* Constructor.
*
* @param string $content The content of the asset
* @param array $filters Filters for the asset
* @param string $sourceRoot The source asset root directory
* @param string $sourcePath The source asset path
*/
public function __construct($content, $filters = array(), $sourceRoot = null, $sourcePath = null)
{
$this->content = $content;
parent::__construct($filters, $sourceRoot, $sourcePath);
}
public function load(FilterInterface $additionalFilter = null)
{
$this->doLoad($this->content, $additionalFilter);
}
public function setLastModified($lastModified)
{
$this->lastModified = $lastModified;
}
public function getLastModified()
{
return $this->lastModified;
}
}

View File

@ -1,79 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic;
use Assetic\Asset\AssetInterface;
/**
* Manages assets.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class AssetManager
{
private $assets = array();
/**
* Gets an asset by name.
*
* @param string $name The asset name
*
* @return AssetInterface The asset
*
* @throws InvalidArgumentException If there is no asset by that name
*/
public function get($name)
{
if (!isset($this->assets[$name])) {
throw new \InvalidArgumentException(sprintf('There is no "%s" asset.', $name));
}
return $this->assets[$name];
}
/**
* Checks if the current asset manager has a certain asset.
*
* @param string $name an asset name
*
* @return Boolean True if the asset has been set, false if not
*/
public function has($name)
{
return isset($this->assets[$name]);
}
/**
* Registers an asset to the current asset manager.
*
* @param string $name The asset name
* @param AssetInterface $asset The asset
*/
public function set($name, AssetInterface $asset)
{
if (!ctype_alnum(str_replace('_', '', $name))) {
throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name));
}
$this->assets[$name] = $asset;
}
/**
* Returns an array of asset names.
*
* @return array An array of asset names
*/
public function getNames()
{
return array_keys($this->assets);
}
}

View File

@ -1,107 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic;
use Assetic\Util\PathUtils;
use Assetic\Asset\AssetInterface;
/**
* Writes assets to the filesystem.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AssetWriter
{
private $dir;
private $varValues;
/**
* Constructor.
*
* @param string $dir The base web directory
*/
public function __construct($dir, array $varValues = array())
{
foreach ($varValues as $var => $values) {
foreach ($values as $value) {
if (!is_string($value)) {
throw new \InvalidArgumentException(sprintf('All variable values must be strings, but got %s for variable "%s".', json_encode($value), $var));
}
}
}
$this->dir = $dir;
$this->varValues = $varValues;
}
public function writeManagerAssets(AssetManager $am)
{
foreach ($am->getNames() as $name) {
$this->writeAsset($am->get($name));
}
}
public function writeAsset(AssetInterface $asset)
{
foreach ($this->getCombinations($asset->getVars()) as $combination) {
$asset->setValues($combination);
static::write($this->dir.'/'.PathUtils::resolvePath(
$asset->getTargetPath(), $asset->getVars(), $asset->getValues()),
$asset->dump());
}
}
private function getCombinations(array $vars)
{
if (!$vars) {
return array(array());
}
$combinations = array();
$nbValues = array();
foreach ($this->varValues as $var => $values) {
if (!in_array($var, $vars, true)) {
continue;
}
$nbValues[$var] = count($values);
}
for ($i=array_product($nbValues),$c=$i*2; $i<$c; $i++) {
$k = $i;
$combination = array();
foreach ($vars as $var) {
$combination[$var] = $this->varValues[$var][$k % $nbValues[$var]];
$k = intval($k/$nbValues[$var]);
}
$combinations[] = $combination;
}
return $combinations;
}
static protected function write($path, $contents)
{
if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) {
throw new \RuntimeException('Unable to create directory '.$dir);
}
if (false === @file_put_contents($path, $contents)) {
throw new \RuntimeException('Unable to write file '.$path);
}
}
}

View File

@ -1,53 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Cache;
/**
* Interface for a cache backend.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
interface CacheInterface
{
/**
* Checks if the cache has a value for a key.
*
* @param string $key A unique key
*
* @return Boolean Whether the cache has a value for this key
*/
function has($key);
/**
* Returns the value for a key.
*
* @param string $key A unique key
*
* @return string|null The value in the cache
*/
function get($key);
/**
* Sets a value in the cache.
*
* @param string $key A unique key
* @param string $value The value to cache
*/
function set($key, $value);
/**
* Removes a value from the cache.
*
* @param string $key A unique key
*/
function remove($key);
}

View File

@ -1,123 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Cache;
/**
* A config cache stores values using var_export() and include.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class ConfigCache
{
private $dir;
/**
* Construct.
*
* @param string $dir The cache directory
*/
public function __construct($dir)
{
$this->dir = $dir;
}
/**
* Checks of the cache has a file.
*
* @param string $resource A cache key
*
* @return Boolean True if a file exists
*/
public function has($resource)
{
return file_exists($this->getSourcePath($resource));
}
/**
* Writes a value to a file.
*
* @param string $resource A cache key
* @param mixed $value A value to cache
*/
public function set($resource, $value)
{
$path = $this->getSourcePath($resource);
if (!is_dir($dir = dirname($path)) && false === @mkdir($dir, 0777, true)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to create directory '.$dir);
// @codeCoverageIgnoreEnd
}
if (false === @file_put_contents($path, sprintf("<?php\n\n// $resource\nreturn %s;\n", var_export($value, true)))) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to write file '.$path);
// @codeCoverageIgnoreEnd
}
}
/**
* Loads and returns the value for the supplied cache key.
*
* @param string $resource A cache key
*
* @return mixed The cached value
*/
public function get($resource)
{
$path = $this->getSourcePath($resource);
if (!file_exists($path)) {
throw new \RuntimeException('There is no cached value for '.$resource);
}
return include $path;
}
/**
* Returns a timestamp for when the cache was created.
*
* @param string $resource A cache key
*
* @return integer A UNIX timestamp
*/
public function getTimestamp($resource)
{
$path = $this->getSourcePath($resource);
if (!file_exists($path)) {
throw new \RuntimeException('There is no cached value for '.$resource);
}
if (false === $mtime = @filemtime($path)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to determine file mtime for '.$path);
// @codeCoverageIgnoreEnd
}
return $mtime;
}
/**
* Returns the path where the file corresponding to the supplied cache key can be included from.
*
* @param string $resource A cache key
*
* @return string A file path
*/
private function getSourcePath($resource)
{
$key = md5($resource);
return $this->dir.'/'.$key[0].'/'.$key.'.php';
}
}

View File

@ -1,60 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Cache;
/**
* Adds expiration to a cache backend.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class ExpiringCache implements CacheInterface
{
private $cache;
private $lifetime;
public function __construct(CacheInterface $cache, $lifetime)
{
$this->cache = $cache;
$this->lifetime = $lifetime;
}
public function has($key)
{
if ($this->cache->has($key)) {
if (time() < $this->cache->get($key.'.expires')) {
return true;
}
$this->cache->remove($key.'.expires');
$this->cache->remove($key);
}
return false;
}
public function get($key)
{
return $this->cache->get($key);
}
public function set($key, $value)
{
$this->cache->set($key.'.expires', time() + $this->lifetime);
$this->cache->set($key, $value);
}
public function remove($key)
{
$this->cache->remove($key.'.expires');
$this->cache->remove($key);
}
}

View File

@ -1,65 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Cache;
/**
* A simple filesystem cache.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class FilesystemCache implements CacheInterface
{
private $dir;
public function __construct($dir)
{
$this->dir = $dir;
}
public function has($key)
{
return file_exists($this->dir.'/'.$key);
}
public function get($key)
{
$path = $this->dir.'/'.$key;
if (!file_exists($path)) {
throw new \RuntimeException('There is no cached value for '.$key);
}
return file_get_contents($path);
}
public function set($key, $value)
{
if (!is_dir($this->dir) && false === @mkdir($this->dir, 0777, true)) {
throw new \RuntimeException('Unable to create directory '.$this->dir);
}
$path = $this->dir.'/'.$key;
if (false === @file_put_contents($path, $value)) {
throw new \RuntimeException('Unable to write file '.$path);
}
}
public function remove($key)
{
$path = $this->dir.'/'.$key;
if (file_exists($path) && false === @unlink($path)) {
throw new \RuntimeException('Unable to remove file '.$path);
}
}
}

View File

@ -1,21 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Exception;
/**
* Marker.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface Exception
{
}

View File

@ -1,73 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Exception;
use Symfony\Component\Process\Process;
/**
* Describes an exception that occurred within a filter.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class FilterException extends \RuntimeException implements Exception
{
private $originalMessage;
private $input;
public static function fromProcess(Process $proc)
{
$message = sprintf("An error occurred while running:\n%s", $proc->getCommandLine());
$errorOutput = $proc->getErrorOutput();
if (!empty($errorOutput)) {
$message .= "\n\nError Output:\n".str_replace("\r", '', $errorOutput);
}
$output = $proc->getOutput();
if (!empty($output)) {
$message .= "\n\nOutput:\n".str_replace("\r", '', $output);
}
return new self($message);
}
public function __construct($message, $code = 0, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
$this->originalMessage = $message;
}
public function setInput($input)
{
$this->input = $input;
$this->updateMessage();
return $this;
}
public function getInput()
{
return $this->input;
}
private function updateMessage()
{
$message = $this->originalMessage;
if (!empty($this->input)) {
$message .= "\n\nInput:\n".$this->input;
}
$this->message = $message;
}
}

View File

@ -1,76 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Extension\Twig;
use Assetic\ValueSupplierInterface;
use Assetic\Factory\AssetFactory;
class AsseticExtension extends \Twig_Extension
{
protected $factory;
protected $functions;
protected $valueSupplier;
public function __construct(AssetFactory $factory, $functions = array(), ValueSupplierInterface $valueSupplier = null)
{
$this->factory = $factory;
$this->functions = array();
$this->valueSupplier = $valueSupplier;
foreach ($functions as $function => $options) {
if (is_integer($function) && is_string($options)) {
$this->functions[$options] = array('filter' => $options);
} else {
$this->functions[$function] = $options + array('filter' => $function);
}
}
}
public function getTokenParsers()
{
return array(
new AsseticTokenParser($this->factory, 'javascripts', 'js/*.js'),
new AsseticTokenParser($this->factory, 'stylesheets', 'css/*.css'),
new AsseticTokenParser($this->factory, 'image', 'images/*', true),
);
}
public function getFunctions()
{
$functions = array();
foreach ($this->functions as $function => $filter) {
$functions[$function] = new AsseticFilterFunction($function);
}
return $functions;
}
public function getGlobals()
{
return array(
'assetic' => array(
'debug' => $this->factory->isDebug(),
'vars' => null !== $this->valueSupplier ? $this->valueSupplier->getValues() : array(),
),
);
}
public function getFilterInvoker($function)
{
return new AsseticFilterInvoker($this->factory, $this->functions[$function]);
}
public function getName()
{
return 'assetic';
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Extension\Twig;
class AsseticFilterFunction extends \Twig_Function
{
private $filter;
public function __construct($filter, $options = array())
{
$this->filter = $filter;
parent::__construct($options);
}
public function compile()
{
return sprintf('$this->env->getExtension(\'assetic\')->getFilterInvoker(\'%s\')->invoke', $this->filter);
}
}

View File

@ -1,61 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Extension\Twig;
use Assetic\Factory\AssetFactory;
/**
* Filters a single asset.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class AsseticFilterInvoker
{
private $factory;
private $filters;
private $options;
public function __construct($factory, $filter)
{
$this->factory = $factory;
if (is_array($filter) && isset($filter['filter'])) {
$this->filters = (array) $filter['filter'];
$this->options = isset($filter['options']) ? (array) $filter['options'] : array();
} else {
$this->filters = (array) $filter;
$this->options = array();
}
}
public function getFactory()
{
return $this->factory;
}
public function getFilters()
{
return $this->filters;
}
public function getOptions()
{
return $this->options;
}
public function invoke($input, array $options = array())
{
$asset = $this->factory->createAsset($input, $this->filters, $options + $this->options);
return $asset->getTargetPath();
}
}

View File

@ -1,166 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Extension\Twig;
use Assetic\Asset\AssetInterface;
class AsseticNode extends \Twig_Node
{
/**
* Constructor.
*
* Available attributes:
*
* * debug: The debug mode
* * combine: Whether to combine assets
* * var_name: The name of the variable to expose to the body node
*
* @param AssetInterface $asset The asset
* @param Twig_NodeInterface $body The body node
* @param array $inputs An array of input strings
* @param array $filters An array of filter strings
* @param string $name The name of the asset
* @param array $attributes An array of attributes
* @param integer $lineno The line number
* @param string $tag The tag name
*/
public function __construct(AssetInterface $asset, \Twig_NodeInterface $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null)
{
$nodes = array('body' => $body);
$attributes = array_replace(
array('debug' => null, 'combine' => null, 'var_name' => 'asset_url'),
$attributes,
array('asset' => $asset, 'inputs' => $inputs, 'filters' => $filters, 'name' => $name)
);
parent::__construct($nodes, $attributes, $lineno, $tag);
}
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$combine = $this->getAttribute('combine');
$debug = $this->getAttribute('debug');
if (null === $combine && null !== $debug) {
$combine = !$debug;
}
if (null === $combine) {
$compiler
->write("if (isset(\$context['assetic']['debug']) && \$context['assetic']['debug']) {\n")
->indent()
;
$this->compileDebug($compiler);
$compiler
->outdent()
->write("} else {\n")
->indent()
;
$this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name'));
$compiler
->outdent()
->write("}\n")
;
} elseif ($combine) {
$this->compileAsset($compiler, $this->getAttribute('asset'), $this->getAttribute('name'));
} else {
$this->compileDebug($compiler);
}
$compiler
->write('unset($context[')
->repr($this->getAttribute('var_name'))
->raw("]);\n")
;
}
protected function compileDebug(\Twig_Compiler $compiler)
{
$i = 0;
foreach ($this->getAttribute('asset') as $leaf) {
$leafName = $this->getAttribute('name').'_'.$i++;
$this->compileAsset($compiler, $leaf, $leafName);
}
}
protected function compileAsset(\Twig_Compiler $compiler, AssetInterface $asset, $name)
{
if ($vars = $asset->getVars()) {
$compiler->write("// check variable conditions\n");
foreach ($vars as $var) {
$compiler
->write("if (!isset(\$context['assetic']['vars']['$var'])) {\n")
->indent()
->write("throw new \RuntimeException(sprintf('The asset \"".$name."\" expected variable \"".$var."\" to be set, but got only these vars: %s. Did you set-up a value supplier?', isset(\$context['assetic']['vars']) && \$context['assetic']['vars'] ? implode(', ', \$context['assetic']['vars']) : '# none #'));\n")
->outdent()
->write("}\n")
;
}
$compiler->raw("\n");
}
$compiler
->write("// asset \"$name\"\n")
->write('$context[')
->repr($this->getAttribute('var_name'))
->raw('] = ')
;
$this->compileAssetUrl($compiler, $asset, $name);
$compiler
->raw(";\n")
->subcompile($this->getNode('body'))
;
}
protected function compileAssetUrl(\Twig_Compiler $compiler, AssetInterface $asset, $name)
{
if (!$vars = $asset->getVars()) {
$compiler->repr($asset->getTargetPath());
return;
}
$compiler
->raw("strtr(")
->string($asset->getTargetPath())
->raw(", array(");
;
$first = true;
foreach ($vars as $var) {
if (!$first) {
$compiler->raw(", ");
}
$first = false;
$compiler
->string("{".$var."}")
->raw(" => \$context['assetic']['vars']['$var']")
;
}
$compiler
->raw("))")
;
}
}

View File

@ -1,150 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Extension\Twig;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\AssetFactory;
class AsseticTokenParser extends \Twig_TokenParser
{
private $factory;
private $tag;
private $output;
private $single;
private $extensions;
/**
* Constructor.
*
* Attributes can be added to the tag by passing names as the options
* array. These values, if found, will be passed to the factory and node.
*
* @param AssetFactory $factory The asset factory
* @param string $tag The tag name
* @param string $output The default output string
* @param Boolean $single Whether to force a single asset
* @param array $extensions Additional attribute names to look for
*/
public function __construct(AssetFactory $factory, $tag, $output, $single = false, array $extensions = array())
{
$this->factory = $factory;
$this->tag = $tag;
$this->output = $output;
$this->single = $single;
$this->extensions = $extensions;
}
public function parse(\Twig_Token $token)
{
$inputs = array();
$filters = array();
$name = null;
$attributes = array(
'output' => $this->output,
'var_name' => 'asset_url',
'vars' => array(),
);
$stream = $this->parser->getStream();
while (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) {
if ($stream->test(\Twig_Token::STRING_TYPE)) {
// '@jquery', 'js/src/core/*', 'js/src/extra.js'
$inputs[] = $stream->next()->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'filter')) {
// filter='yui_js'
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$filters = array_merge($filters, array_filter(array_map('trim', explode(',', $stream->expect(\Twig_Token::STRING_TYPE)->getValue()))));
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'output')) {
// output='js/packed/*.js' OR output='js/core.js'
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes['output'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'name')) {
// name='core_js'
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$name = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'as')) {
// as='the_url'
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes['var_name'] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'debug')) {
// debug=true
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes['debug'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'combine')) {
// combine=true
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes['combine'] = 'true' == $stream->expect(\Twig_Token::NAME_TYPE, array('true', 'false'))->getValue();
} elseif ($stream->test(\Twig_Token::NAME_TYPE, 'vars')) {
// vars=['locale','browser']
$stream->next();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$stream->expect(\Twig_Token::PUNCTUATION_TYPE, '[');
while ($stream->test(\Twig_Token::STRING_TYPE)) {
$attributes['vars'][] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
if (!$stream->test(\Twig_Token::PUNCTUATION_TYPE, ',')) {
break;
}
$stream->next();
}
$stream->expect(\Twig_Token::PUNCTUATION_TYPE, ']');
} elseif ($stream->test(\Twig_Token::NAME_TYPE, $this->extensions)) {
// an arbitrary configured attribute
$key = $stream->next()->getValue();
$stream->expect(\Twig_Token::OPERATOR_TYPE, '=');
$attributes[$key] = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
} else {
$token = $stream->getCurrent();
throw new \Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', \Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine());
}
}
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
$endtag = 'end'.$this->getTag();
$test = function(\Twig_Token $token) use($endtag) { return $token->test($endtag); };
$body = $this->parser->subparse($test, true);
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
if ($this->single && 1 < count($inputs)) {
$inputs = array_slice($inputs, -1);
}
if (!$name) {
$name = $this->factory->generateAssetName($inputs, $filters, $attributes);
}
$asset = $this->factory->createAsset($inputs, $filters, $attributes + array('name' => $name));
return $this->createNode($asset, $body, $inputs, $filters, $name, $attributes, $token->getLine(), $this->getTag());
}
public function getTag()
{
return $this->tag;
}
protected function createNode(AssetInterface $asset, \Twig_NodeInterface $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null)
{
return new AsseticNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag);
}
}

View File

@ -1,97 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Extension\Twig;
use Assetic\Factory\Loader\FormulaLoaderInterface;
use Assetic\Factory\Resource\ResourceInterface;
/**
* Loads asset formulae from Twig templates.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class TwigFormulaLoader implements FormulaLoaderInterface
{
private $twig;
public function __construct(\Twig_Environment $twig)
{
$this->twig = $twig;
}
public function load(ResourceInterface $resource)
{
try {
$tokens = $this->twig->tokenize($resource->getContent(), (string) $resource);
$nodes = $this->twig->parse($tokens);
} catch (\Exception $e) {
return array();
}
return $this->loadNode($nodes);
}
/**
* Loads assets from the supplied node.
*
* @return array An array of asset formulae indexed by name
*/
private function loadNode(\Twig_Node $node)
{
$formulae = array();
if ($node instanceof AsseticNode) {
$formulae[$node->getAttribute('name')] = array(
$node->getAttribute('inputs'),
$node->getAttribute('filters'),
array(
'output' => $node->getAttribute('asset')->getTargetPath(),
'name' => $node->getAttribute('name'),
'debug' => $node->getAttribute('debug'),
'combine' => $node->getAttribute('combine'),
'vars' => $node->getAttribute('vars'),
),
);
} elseif ($node instanceof \Twig_Node_Expression_Function) {
$name = version_compare(\Twig_Environment::VERSION, '1.2.0-DEV', '<')
? $node->getNode('name')->getAttribute('name')
: $node->getAttribute('name');
if ($this->twig->getFunction($name) instanceof AsseticFilterFunction) {
$arguments = array();
foreach ($node->getNode('arguments') as $argument) {
$arguments[] = eval('return '.$this->twig->compile($argument).';');
}
$invoker = $this->twig->getExtension('assetic')->getFilterInvoker($name);
$inputs = isset($arguments[0]) ? (array) $arguments[0] : array();
$filters = $invoker->getFilters();
$options = array_replace($invoker->getOptions(), isset($arguments[1]) ? $arguments[1] : array());
if (!isset($options['name'])) {
$options['name'] = $invoker->getFactory()->generateAssetName($inputs, $filters, $options);
}
$formulae[$options['name']] = array($inputs, $filters, $options);
}
}
foreach ($node as $child) {
if ($child instanceof \Twig_Node) {
$formulae += $this->loadNode($child);
}
}
return $formulae;
}
}

View File

@ -1,54 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Extension\Twig;
use Assetic\Factory\Resource\ResourceInterface;
/**
* A Twig template resource.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class TwigResource implements ResourceInterface
{
private $loader;
private $name;
public function __construct(\Twig_LoaderInterface $loader, $name)
{
$this->loader = $loader;
$this->name = $name;
}
public function getContent()
{
try {
return $this->loader->getSource($this->name);
} catch (\Twig_Error_Loader $e) {
return '';
}
}
public function isFresh($timestamp)
{
try {
return $this->loader->isFresh($this->name, $timestamp);
} catch (\Twig_Error_Loader $e) {
return false;
}
}
public function __toString()
{
return $this->name;
}
}

View File

@ -1,386 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory;
use Assetic\Asset\AssetCollection;
use Assetic\Asset\AssetCollectionInterface;
use Assetic\Asset\AssetInterface;
use Assetic\Asset\AssetReference;
use Assetic\Asset\FileAsset;
use Assetic\Asset\GlobAsset;
use Assetic\Asset\HttpAsset;
use Assetic\AssetManager;
use Assetic\Factory\Worker\WorkerInterface;
use Assetic\FilterManager;
/**
* The asset factory creates asset objects.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class AssetFactory
{
private $root;
private $debug;
private $output;
private $workers;
private $am;
private $fm;
/**
* Constructor.
*
* @param string $root The default root directory
* @param string $output The default output string
* @param Boolean $debug Filters prefixed with a "?" will be omitted in debug mode
*/
public function __construct($root, $debug = false)
{
$this->root = rtrim($root, '/');
$this->debug = $debug;
$this->output = 'assetic/*';
$this->workers = array();
}
/**
* Sets debug mode for the current factory.
*
* @param Boolean $debug Debug mode
*/
public function setDebug($debug)
{
$this->debug = $debug;
}
/**
* Checks if the factory is in debug mode.
*
* @return Boolean Debug mode
*/
public function isDebug()
{
return $this->debug;
}
/**
* Sets the default output string.
*
* @param string $output The default output string
*/
public function setDefaultOutput($output)
{
$this->output = $output;
}
/**
* Adds a factory worker.
*
* @param WorkerInterface $worker A worker
*/
public function addWorker(WorkerInterface $worker)
{
$this->workers[] = $worker;
}
/**
* Returns the current asset manager.
*
* @return AssetManager|null The asset manager
*/
public function getAssetManager()
{
return $this->am;
}
/**
* Sets the asset manager to use when creating asset references.
*
* @param AssetManager $am The asset manager
*/
public function setAssetManager(AssetManager $am)
{
$this->am = $am;
}
/**
* Returns the current filter manager.
*
* @return FilterManager|null The filter manager
*/
public function getFilterManager()
{
return $this->fm;
}
/**
* Sets the filter manager to use when adding filters.
*
* @param FilterManager $fm The filter manager
*/
public function setFilterManager(FilterManager $fm)
{
$this->fm = $fm;
}
/**
* Creates a new asset.
*
* Prefixing a filter name with a question mark will cause it to be
* omitted when the factory is in debug mode.
*
* Available options:
*
* * output: An output string
* * name: An asset name for interpolation in output patterns
* * debug: Forces debug mode on or off for this asset
* * root: An array or string of more root directories
*
* @param array|string $inputs An array of input strings
* @param array|string $filters An array of filter names
* @param array $options An array of options
*
* @return AssetCollection An asset collection
*/
public function createAsset($inputs = array(), $filters = array(), array $options = array())
{
if (!is_array($inputs)) {
$inputs = array($inputs);
}
if (!is_array($filters)) {
$filters = array($filters);
}
if (!isset($options['output'])) {
$options['output'] = $this->output;
}
if (!isset($options['vars'])) {
$options['vars'] = array();
}
if (!isset($options['debug'])) {
$options['debug'] = $this->debug;
}
if (!isset($options['root'])) {
$options['root'] = array($this->root);
} else {
if (!is_array($options['root'])) {
$options['root'] = array($options['root']);
}
$options['root'][] = $this->root;
}
if (!isset($options['name'])) {
$options['name'] = $this->generateAssetName($inputs, $filters, $options);
}
$asset = $this->createAssetCollection(array(), $options);
$extensions = array();
// inner assets
foreach ($inputs as $input) {
if (is_array($input)) {
// nested formula
$asset->add(call_user_func_array(array($this, 'createAsset'), $input));
} else {
$asset->add($this->parseInput($input, $options));
$extensions[pathinfo($input, PATHINFO_EXTENSION)] = true;
}
}
// filters
foreach ($filters as $filter) {
if ('?' != $filter[0]) {
$asset->ensureFilter($this->getFilter($filter));
} elseif (!$options['debug']) {
$asset->ensureFilter($this->getFilter(substr($filter, 1)));
}
}
// append variables
if (!empty($options['vars'])) {
$toAdd = array();
foreach ($options['vars'] as $var) {
if (false !== strpos($options['output'], '{'.$var.'}')) {
continue;
}
$toAdd[] = '{'.$var.'}';
}
if ($toAdd) {
$options['output'] = str_replace('*', '*.'.implode('.', $toAdd), $options['output']);
}
}
// append consensus extension if missing
if (1 == count($extensions) && !pathinfo($options['output'], PATHINFO_EXTENSION) && $extension = key($extensions)) {
$options['output'] .= '.'.$extension;
}
// output --> target url
$asset->setTargetPath(str_replace('*', $options['name'], $options['output']));
// apply workers and return
return $this->applyWorkers($asset);
}
public function generateAssetName($inputs, $filters, $options = array())
{
foreach (array_diff(array_keys($options), array('output', 'debug', 'root')) as $key) {
unset($options[$key]);
}
ksort($options);
return substr(sha1(serialize($inputs).serialize($filters).serialize($options)), 0, 7);
}
/**
* Parses an input string string into an asset.
*
* The input string can be one of the following:
*
* * A reference: If the string starts with an "at" sign it will be interpreted as a reference to an asset in the asset manager
* * An absolute URL: If the string contains "://" or starts with "//" it will be interpreted as an HTTP asset
* * A glob: If the string contains a "*" it will be interpreted as a glob
* * A path: Otherwise the string is interpreted as a filesystem path
*
* Both globs and paths will be absolutized using the current root directory.
*
* @param string $input An input string
* @param array $options An array of options
*
* @return AssetInterface An asset
*/
protected function parseInput($input, array $options = array())
{
if ('@' == $input[0]) {
return $this->createAssetReference(substr($input, 1));
}
if (false !== strpos($input, '://') || 0 === strpos($input, '//')) {
return $this->createHttpAsset($input, $options['vars']);
}
if (self::isAbsolutePath($input)) {
if ($root = self::findRootDir($input, $options['root'])) {
$path = ltrim(substr($input, strlen($root)), '/');
} else {
$path = null;
}
} else {
$root = $this->root;
$path = $input;
$input = $this->root.'/'.$path;
}
if (false !== strpos($input, '*')) {
return $this->createGlobAsset($input, $root, $options['vars']);
} else {
return $this->createFileAsset($input, $root, $path, $options['vars']);
}
}
protected function createAssetCollection(array $assets = array(), array $options = array())
{
return new AssetCollection($assets, array(), null, isset($options['vars']) ? $options['vars'] : array());
}
protected function createAssetReference($name)
{
if (!$this->am) {
throw new \LogicException('There is no asset manager.');
}
return new AssetReference($this->am, $name);
}
protected function createHttpAsset($sourceUrl, $vars)
{
return new HttpAsset($sourceUrl, array(), false, $vars);
}
protected function createGlobAsset($glob, $root = null, $vars)
{
return new GlobAsset($glob, array(), $root, $vars);
}
protected function createFileAsset($source, $root = null, $path = null, $vars)
{
return new FileAsset($source, array(), $root, $path, $vars);
}
protected function getFilter($name)
{
if (!$this->fm) {
throw new \LogicException('There is no filter manager.');
}
return $this->fm->get($name);
}
/**
* Filters an asset collection through the factory workers.
*
* Each leaf asset will be processed first, followed by the asset
* collection itself.
*
* @param AssetCollectionInterface $asset An asset collection
*/
private function applyWorkers(AssetCollectionInterface $asset)
{
foreach ($asset as $leaf) {
foreach ($this->workers as $worker) {
$retval = $worker->process($leaf);
if ($retval instanceof AssetInterface && $leaf !== $retval) {
$asset->replaceLeaf($leaf, $retval);
}
}
}
foreach ($this->workers as $worker) {
$retval = $worker->process($asset);
if ($retval instanceof AssetInterface) {
$asset = $retval;
}
}
return $asset instanceof AssetCollectionInterface ? $asset : $this->createAssetCollection(array($asset));
}
static private function isAbsolutePath($path)
{
return '/' == $path[0] || '\\' == $path[0] || (3 < strlen($path) && ctype_alpha($path[0]) && $path[1] == ':' && ('\\' == $path[2] || '/' == $path[2]));
}
/**
* Loops through the root directories and returns the first match.
*
* @param string $path An absolute path
* @param array $roots An array of root directories
*
* @return string|null The matching root directory, if found
*/
static private function findRootDir($path, array $roots)
{
foreach ($roots as $root) {
if (0 === strpos($path, $root)) {
return $root;
}
}
}
}

View File

@ -1,204 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory;
use Assetic\AssetManager;
use Assetic\Factory\Loader\FormulaLoaderInterface;
use Assetic\Factory\Resource\ResourceInterface;
/**
* A lazy asset manager is a composition of a factory and many formula loaders.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class LazyAssetManager extends AssetManager
{
private $factory;
private $loaders;
private $resources;
private $formulae;
private $loaded;
private $loading;
/**
* Constructor.
*
* @param AssetFactory $factory The asset factory
* @param array $loaders An array of loaders indexed by alias
*/
public function __construct(AssetFactory $factory, $loaders = array())
{
$this->factory = $factory;
$this->loaders = array();
$this->resources = array();
$this->formulae = array();
$this->loaded = false;
$this->loading = false;
foreach ($loaders as $alias => $loader) {
$this->setLoader($alias, $loader);
}
}
/**
* Adds a loader to the asset manager.
*
* @param string $alias An alias for the loader
* @param FormulaLoaderInterface $loader A loader
*/
public function setLoader($alias, FormulaLoaderInterface $loader)
{
$this->loaders[$alias] = $loader;
$this->loaded = false;
}
/**
* Adds a resource to the asset manager.
*
* @param ResourceInterface $resource A resource
* @param string $loader The loader alias for this resource
*/
public function addResource(ResourceInterface $resource, $loader)
{
$this->resources[$loader][] = $resource;
$this->loaded = false;
}
/**
* Returns an array of resources.
*
* @return array An array of resources
*/
public function getResources()
{
$resources = array();
foreach ($this->resources as $r) {
$resources = array_merge($resources, $r);
}
return $resources;
}
/**
* Checks for an asset formula.
*
* @param string $name An asset name
*
* @return Boolean If there is a formula
*/
public function hasFormula($name)
{
if (!$this->loaded) {
$this->load();
}
return isset($this->formulae[$name]);
}
/**
* Returns an asset's formula.
*
* @param string $name An asset name
*
* @return array The formula
*
* @throws InvalidArgumentException If there is no formula by that name
*/
public function getFormula($name)
{
if (!$this->loaded) {
$this->load();
}
if (!isset($this->formulae[$name])) {
throw new \InvalidArgumentException(sprintf('There is no "%s" formula.', $name));
}
return $this->formulae[$name];
}
/**
* Sets a formula on the asset manager.
*
* @param string $name An asset name
* @param array $formula A formula
*/
public function setFormula($name, array $formula)
{
$this->formulae[$name] = $formula;
}
/**
* Loads formulae from resources.
*
* @throws LogicException If a resource has been added to an invalid loader
*/
public function load()
{
if ($this->loading) {
return;
}
if ($diff = array_diff(array_keys($this->resources), array_keys($this->loaders))) {
throw new \LogicException('The following loader(s) are not registered: '.implode(', ', $diff));
}
$this->loading = true;
foreach ($this->resources as $loader => $resources) {
foreach ($resources as $resource) {
$this->formulae = array_replace($this->formulae, $this->loaders[$loader]->load($resource));
}
}
$this->loaded = true;
$this->loading = false;
}
public function get($name)
{
if (!$this->loaded) {
$this->load();
}
if (!parent::has($name) && isset($this->formulae[$name])) {
list($inputs, $filters, $options) = $this->formulae[$name];
$options['name'] = $name;
parent::set($name, $this->factory->createAsset($inputs, $filters, $options));
}
return parent::get($name);
}
public function has($name)
{
if (!$this->loaded) {
$this->load();
}
return isset($this->formulae[$name]) || parent::has($name);
}
public function getNames()
{
if (!$this->loaded) {
$this->load();
}
return array_unique(array_merge(parent::getNames(), array_keys($this->formulae)));
}
public function isDebug()
{
return $this->factory->isDebug();
}
}

View File

@ -1,159 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Loader;
use Assetic\Factory\AssetFactory;
use Assetic\Factory\Resource\ResourceInterface;
/**
* Loads asset formulae from PHP files.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
abstract class BasePhpFormulaLoader implements FormulaLoaderInterface
{
protected $factory;
protected $prototypes;
public function __construct(AssetFactory $factory)
{
$this->factory = $factory;
$this->prototypes = array();
foreach ($this->registerPrototypes() as $prototype => $options) {
$this->addPrototype($prototype, $options);
}
}
public function addPrototype($prototype, array $options = array())
{
$tokens = token_get_all('<?php '.$prototype);
array_shift($tokens);
$this->prototypes[$prototype] = array($tokens, $options);
}
public function load(ResourceInterface $resource)
{
if (!$nbProtos = count($this->prototypes)) {
throw new \LogicException('There are no prototypes registered.');
}
$buffers = array_fill(0, $nbProtos, '');
$bufferLevels = array_fill(0, $nbProtos, 0);
$buffersInWildcard = array();
$tokens = token_get_all($resource->getContent());
$calls = array();
while ($token = array_shift($tokens)) {
$current = self::tokenToString($token);
// loop through each prototype (by reference)
foreach (array_keys($this->prototypes) as $i) {
$prototype =& $this->prototypes[$i][0];
$options = $this->prototypes[$i][1];
$buffer =& $buffers[$i];
$level =& $bufferLevels[$i];
if (isset($buffersInWildcard[$i])) {
switch ($current) {
case '(': ++$level; break;
case ')': --$level; break;
}
$buffer .= $current;
if (!$level) {
$calls[] = array($buffer.';', $options);
$buffer = '';
unset($buffersInWildcard[$i]);
}
} elseif ($current == self::tokenToString(current($prototype))) {
$buffer .= $current;
if ('*' == self::tokenToString(next($prototype))) {
$buffersInWildcard[$i] = true;
++$level;
}
} else {
reset($prototype);
unset($buffersInWildcard[$i]);
$buffer = '';
}
}
}
$formulae = array();
foreach ($calls as $call) {
$formulae += call_user_func_array(array($this, 'processCall'), $call);
}
return $formulae;
}
private function processCall($call, array $protoOptions = array())
{
$tmp = tempnam(sys_get_temp_dir(), 'assetic');
file_put_contents($tmp, implode("\n", array(
'<?php',
$this->registerSetupCode(),
$call,
'echo serialize($_call);',
)));
$args = unserialize(shell_exec('php '.escapeshellarg($tmp)));
unlink($tmp);
$inputs = isset($args[0]) ? self::argumentToArray($args[0]) : array();
$filters = isset($args[1]) ? self::argumentToArray($args[1]) : array();
$options = isset($args[2]) ? $args[2] : array();
if (!isset($options['debug'])) {
$options['debug'] = $this->factory->isDebug();
}
if (!is_array($options)) {
throw new \RuntimeException('The third argument must be omitted, null or an array.');
}
// apply the prototype options
$options += $protoOptions;
if (!isset($options['name'])) {
$options['name'] = $this->factory->generateAssetName($inputs, $filters, $options);
}
return array($options['name'] => array($inputs, $filters, $options));
}
/**
* Returns an array of prototypical calls and options.
*
* @return array Prototypes and options
*/
abstract protected function registerPrototypes();
/**
* Returns setup code for the reflection scriptlet.
*
* @return string Some PHP setup code
*/
abstract protected function registerSetupCode();
static protected function tokenToString($token)
{
return is_array($token) ? $token[1] : $token;
}
static protected function argumentToArray($argument)
{
return is_array($argument) ? $argument : array_filter(array_map('trim', explode(',', $argument)));
}
}

View File

@ -1,68 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Loader;
use Assetic\Cache\ConfigCache;
use Assetic\Factory\Resource\IteratorResourceInterface;
use Assetic\Factory\Resource\ResourceInterface;
/**
* Adds a caching layer to a loader.
*
* A cached formula loader is a composition of a formula loader and a cache.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CachedFormulaLoader implements FormulaLoaderInterface
{
private $loader;
private $configCache;
private $debug;
/**
* Constructor.
*
* When the loader is in debug mode it will ensure the cached formulae
* are fresh before returning them.
*
* @param FormulaLoaderInterface $loader A formula loader
* @param ConfigCache $configCache A config cache
* @param Boolean $debug The debug mode
*/
public function __construct(FormulaLoaderInterface $loader, ConfigCache $configCache, $debug = false)
{
$this->loader = $loader;
$this->configCache = $configCache;
$this->debug = $debug;
}
public function load(ResourceInterface $resources)
{
if (!$resources instanceof IteratorResourceInterface) {
$resources = array($resources);
}
$formulae = array();
foreach ($resources as $resource) {
$id = (string) $resource;
if (!$this->configCache->has($id) || ($this->debug && !$resource->isFresh($this->configCache->getTimestamp($id)))) {
$formulae += $this->loader->load($resource);
$this->configCache->set($id, $formulae);
} else {
$formulae += $this->configCache->get($id);
}
}
return $formulae;
}
}

View File

@ -1,34 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Loader;
use Assetic\Factory\Resource\ResourceInterface;
/**
* Loads formulae.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
interface FormulaLoaderInterface
{
/**
* Loads formulae from a resource.
*
* Formulae should be loaded the same regardless of the current debug
* mode. Debug considerations should happen downstream.
*
* @param ResourceInterface $resource A resource
*
* @return array An array of formulae
*/
function load(ResourceInterface $resource);
}

View File

@ -1,53 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Loader;
/**
* Loads asset formulae from PHP files.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class FunctionCallsFormulaLoader extends BasePhpFormulaLoader
{
protected function registerPrototypes()
{
return array(
'assetic_javascripts(*)' => array('output' => 'js/*.js'),
'assetic_stylesheets(*)' => array('output' => 'css/*.css'),
'assetic_image(*)' => array('output' => 'images/*'),
);
}
protected function registerSetupCode()
{
return <<<'EOF'
function assetic_javascripts()
{
global $_call;
$_call = func_get_args();
}
function assetic_stylesheets()
{
global $_call;
$_call = func_get_args();
}
function assetic_image()
{
global $_call;
$_call = func_get_args();
}
EOF;
}
}

View File

@ -1,112 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Resource;
/**
* Coalesces multiple directories together into one merged resource.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CoalescingDirectoryResource implements IteratorResourceInterface
{
private $directories;
public function __construct($directories)
{
$this->directories = array();
foreach ($directories as $directory) {
$this->addDirectory($directory);
}
}
public function addDirectory(IteratorResourceInterface $directory)
{
$this->directories[] = $directory;
}
public function isFresh($timestamp)
{
foreach ($this->getFileResources() as $file) {
if (!$file->isFresh($timestamp)) {
return false;
}
}
return true;
}
public function getContent()
{
$parts = array();
foreach ($this->getFileResources() as $file) {
$parts[] = $file->getContent();
}
return implode("\n", $parts);
}
/**
* Returns a string to uniquely identify the current resource.
*
* @return string An identifying string
*/
public function __toString()
{
$parts = array();
foreach ($this->directories as $directory) {
$parts[] = (string) $directory;
}
return implode(',', $parts);
}
public function getIterator()
{
return new \ArrayIterator($this->getFileResources());
}
/**
* Returns the relative version of a filename.
*
* @param ResourceInterface $file The file
* @param ResourceInterface $directory The directory
*
* @return string The name to compare with files from other directories
*/
protected function getRelativeName(ResourceInterface $file, ResourceInterface $directory)
{
return substr((string) $file, strlen((string) $directory));
}
/**
* Performs the coalesce.
*
* @return array An array of file resources
*/
private function getFileResources()
{
$paths = array();
foreach ($this->directories as $directory) {
foreach ($directory as $file) {
$relative = $this->getRelativeName($file, $directory);
if (!isset($paths[$relative])) {
$paths[$relative] = $file;
}
}
}
return array_values($paths);
}
}

View File

@ -1,133 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Resource;
/**
* A resource is something formulae can be loaded from.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class DirectoryResource implements IteratorResourceInterface
{
private $path;
private $pattern;
/**
* Constructor.
*
* @param string $path A directory path
* @param string $pattern A filename pattern
*/
public function __construct($path, $pattern = null)
{
if (DIRECTORY_SEPARATOR != substr($path, -1)) {
$path .= DIRECTORY_SEPARATOR;
}
$this->path = $path;
$this->pattern = $pattern;
}
public function isFresh($timestamp)
{
if (!is_dir($this->path) || filemtime($this->path) > $timestamp) {
return false;
}
foreach ($this as $resource) {
if (!$resource->isFresh($timestamp)) {
return false;
}
}
return true;
}
/**
* Returns the combined content of all inner resources.
*/
public function getContent()
{
$content = array();
foreach ($this as $resource) {
$content[] = $resource->getContent();
}
return implode("\n", $content);
}
public function __toString()
{
return $this->path;
}
public function getIterator()
{
return is_dir($this->path)
? new DirectoryResourceIterator($this->getInnerIterator())
: new \EmptyIterator();
}
protected function getInnerIterator()
{
return new DirectoryResourceFilterIterator(new \RecursiveDirectoryIterator($this->path), $this->pattern);
}
}
/**
* An iterator that converts file objects into file resources.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
* @access private
*/
class DirectoryResourceIterator extends \RecursiveIteratorIterator
{
public function current()
{
return new FileResource(parent::current()->getPathname());
}
}
/**
* Filters files by a basename pattern.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
* @access private
*/
class DirectoryResourceFilterIterator extends \RecursiveFilterIterator
{
protected $pattern;
public function __construct(\RecursiveDirectoryIterator $iterator, $pattern = null)
{
parent::__construct($iterator);
$this->pattern = $pattern;
}
public function accept()
{
$file = $this->current();
$name = $file->getBasename();
if ($file->isDir()) {
return '.' != $name[0];
} else {
return null === $this->pattern || 0 < preg_match($this->pattern, $name);
}
}
public function getChildren()
{
return new self(new \RecursiveDirectoryIterator($this->current()->getPathname()), $this->pattern);
}
}

View File

@ -1,47 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Resource;
/**
* A resource is something formulae can be loaded from.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class FileResource implements ResourceInterface
{
private $path;
/**
* Constructor.
*
* @param string $path The path to a file
*/
public function __construct($path)
{
$this->path = $path;
}
public function isFresh($timestamp)
{
return file_exists($this->path) && filemtime($this->path) <= $timestamp;
}
public function getContent()
{
return file_exists($this->path) ? file_get_contents($this->path) : '';
}
public function __toString()
{
return $this->path;
}
}

View File

@ -1,21 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Resource;
/**
* A resource is something formulae can be loaded from.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
interface IteratorResourceInterface extends ResourceInterface, \IteratorAggregate
{
}

View File

@ -1,43 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Resource;
/**
* A resource is something formulae can be loaded from.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
interface ResourceInterface
{
/**
* Checks if a timestamp represents the latest resource.
*
* @param integer $timestamp A UNIX timestamp
*
* @return Boolean True if the timestamp is up to date
*/
function isFresh($timestamp);
/**
* Returns the content of the resource.
*
* @return string The content
*/
function getContent();
/**
* Returns a unique string for the current resource.
*
* @return string A unique string to identity the current resource
*/
function __toString();
}

View File

@ -1,60 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Worker;
use Assetic\Asset\AssetInterface;
use Assetic\Filter\FilterInterface;
/**
* Applies a filter to an asset based on a source and/or target path match.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
* @todo A better asset-matcher mechanism
*/
class EnsureFilterWorker implements WorkerInterface
{
const CHECK_SOURCE = 1;
const CHECK_TARGET = 2;
private $pattern;
private $filter;
private $flags;
/**
* Constructor.
*
* @param string $pattern A regex for checking the asset's target URL
* @param FilterInterface $filter A filter to apply if the regex matches
* @param integer $flags Flags for what to check
*/
public function __construct($pattern, FilterInterface $filter, $flags = null)
{
if (null === $flags) {
$flags = self::CHECK_SOURCE | self::CHECK_TARGET;
}
$this->pattern = $pattern;
$this->filter = $filter;
$this->flags = $flags;
}
public function process(AssetInterface $asset)
{
if (
(self::CHECK_SOURCE === (self::CHECK_SOURCE & $this->flags) && preg_match($this->pattern, $asset->getSourcePath()))
||
(self::CHECK_TARGET === (self::CHECK_TARGET & $this->flags) && preg_match($this->pattern, $asset->getTargetPath()))
) {
$asset->ensureFilter($this->filter);
}
}
}

View File

@ -1,31 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Factory\Worker;
use Assetic\Asset\AssetInterface;
/**
* Assets are passed through factory workers before leaving the factory.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
interface WorkerInterface
{
/**
* Processes an asset.
*
* @param AssetInterface $asset An asset
*
* @return AssetInterface|null May optionally return a replacement asset
*/
function process(AssetInterface $asset);
}

View File

@ -1,71 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
/**
* An abstract filter for dealing with CSS.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
abstract class BaseCssFilter implements FilterInterface
{
/**
* Filters all references -- url() and "@import" -- through a callable.
*
* @param string $content The CSS
* @param mixed $callback A PHP callable
*
* @return string The filtered CSS
*/
protected function filterReferences($content, $callback, $limit = -1, & $count = 0)
{
$content = $this->filterUrls($content, $callback, $limit, $count);
$content = $this->filterImports($content, $callback, $limit, $count, false);
return $content;
}
/**
* Filters all CSS url()'s through a callable.
*
* @param string $content The CSS
* @param mixed $callback A PHP callable
* @param integer $limit Limit the number of replacements
* @param integer $count Will be populated with the count
*
* @return string The filtered CSS
*/
protected function filterUrls($content, $callback, $limit = -1, & $count = 0)
{
return preg_replace_callback('/url\((["\']?)(?<url>.*?)(\\1)\)/', $callback, $content, $limit, $count);
}
/**
* Filters all CSS imports through a callable.
*
* @param string $content The CSS
* @param mixed $callback A PHP callable
* @param integer $limit Limit the number of replacements
* @param integer $count Will be populated with the count
* @param Boolean $includeUrl Whether to include url() in the pattern
*
* @return string The filtered CSS
*/
protected function filterImports($content, $callback, $limit = -1, & $count = 0, $includeUrl = true)
{
$pattern = $includeUrl
? '/@import (?:url\()?(\'|"|)(?<url>[^\'"\)\n\r]*)\1\)?;?/'
: '/@import (?!url\()(\'|"|)(?<url>[^\'"\)\n\r]*)\1;?/';
return preg_replace_callback($pattern, $callback, $content, $limit, $count);
}
}

View File

@ -1,45 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
/**
* A filter that wraps callables.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CallablesFilter implements FilterInterface
{
private $loader;
private $dumper;
public function __construct($loader = null, $dumper = null)
{
$this->loader = $loader;
$this->dumper = $dumper;
}
public function filterLoad(AssetInterface $asset)
{
if (null !== $callable = $this->loader) {
$callable($asset);
}
}
public function filterDump(AssetInterface $asset)
{
if (null !== $callable = $this->dumper) {
$callable($asset);
}
}
}

View File

@ -1,73 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Compiles CoffeeScript into Javascript.
*
* @link http://coffeescript.org/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CoffeeScriptFilter implements FilterInterface
{
private $coffeePath;
private $nodePath;
// coffee options
private $bare;
public function __construct($coffeePath = '/usr/bin/coffee', $nodePath = '/usr/bin/node')
{
$this->coffeePath = $coffeePath;
$this->nodePath = $nodePath;
}
public function setBare($bare)
{
$this->bare = $bare;
}
public function filterLoad(AssetInterface $asset)
{
$input = tempnam(sys_get_temp_dir(), 'assetic_coffeescript');
file_put_contents($input, $asset->getContent());
$pb = new ProcessBuilder(array(
$this->nodePath,
$this->coffeePath,
'-cp',
));
if ($this->bare) {
$pb->add('--bare');
}
$pb->add($input);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 < $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
}

View File

@ -1,329 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Exception\FilterException;
use Assetic\Asset\AssetInterface;
use Assetic\Filter\FilterInterface;
use Symfony\Component\Process\ProcessBuilder;
/**
* Loads Compass files.
*
* @link http://compass-style.org/
* @author Maxime Thirouin <maxime.thirouin@gmail.com>
*/
class CompassFilter implements FilterInterface
{
private $compassPath;
private $scss;
// sass options
private $unixNewlines;
private $debugInfo;
private $cacheLocation;
private $noCache;
// compass options
private $force;
private $style;
private $quiet;
private $boring;
private $noLineComments;
private $imagesDir;
private $javascriptsDir;
// compass configuration file options
private $plugins = array();
private $loadPaths = array();
private $httpPath;
private $httpImagesPath;
private $httpJavascriptsPath;
public function __construct($compassPath = '/usr/bin/compass')
{
$this->compassPath = $compassPath;
$this->cacheLocation = sys_get_temp_dir();
if ('cli' !== php_sapi_name()) {
$this->boring = true;
}
}
public function setScss($scss)
{
$this->scss = $scss;
}
// sass options setters
public function setUnixNewlines($unixNewlines)
{
$this->unixNewlines = $unixNewlines;
}
public function setDebugInfo($debugInfo)
{
$this->debugInfo = $debugInfo;
}
public function setCacheLocation($cacheLocation)
{
$this->cacheLocation = $cacheLocation;
}
public function setNoCache($noCache)
{
$this->noCache = $noCache;
}
// compass options setters
public function setForce($force)
{
$this->force = $force;
}
public function setStyle($style)
{
$this->style = $style;
}
public function setQuiet($quiet)
{
$this->quiet = $quiet;
}
public function setBoring($boring)
{
$this->boring = $boring;
}
public function setNoLineComments($noLineComments)
{
$this->noLineComments = $noLineComments;
}
public function setImagesDir($imagesDir)
{
$this->imagesDir = $imagesDir;
}
public function setJavascriptsDir($javascriptsDir)
{
$this->javascriptsDir = $javascriptsDir;
}
// compass configuration file options setters
public function setPlugins(array $plugins)
{
$this->plugins = $plugins;
}
public function addPlugin($plugin)
{
$this->plugins[] = $plugin;
}
public function addLoadPath($loadPath)
{
$this->loadPaths[] = $loadPath;
}
public function setHttpPath($httpPath)
{
$this->httpPath = $httpPath;
}
public function setHttpImagesPath($httpImagesPath)
{
$this->httpImagesPath = $httpImagesPath;
}
public function setHttpJavascriptsPath($httpJavascriptsPath)
{
$this->httpJavascriptsPath = $httpJavascriptsPath;
}
public function filterLoad(AssetInterface $asset)
{
$root = $asset->getSourceRoot();
$path = $asset->getSourcePath();
$loadPaths = $this->loadPaths;
if ($root && $path) {
$loadPaths[] = dirname($root.'/'.$path);
}
// compass does not seems to handle symlink, so we use realpath()
$tempDir = realpath(sys_get_temp_dir());
$pb = new ProcessBuilder(array(
$this->compassPath,
'compile',
$tempDir,
));
$pb->inheritEnvironmentVariables();
if ($this->force) {
$pb->add('--force');
}
if ($this->style) {
$pb->add('--output-style')->add($this->style);
}
if ($this->quiet) {
$pb->add('--quiet');
}
if ($this->boring) {
$pb->add('--boring');
}
if ($this->noLineComments) {
$pb->add('--no-line-comments');
}
// these two options are not passed into the config file
// because like this, compass adapts this to be xxx_dir or xxx_path
// whether it's an absolute path or not
if ($this->imagesDir) {
$pb->add('--images-dir')->add($this->imagesDir);
}
if ($this->javascriptsDir) {
$pb->add('--javascripts-dir')->add($this->javascriptsDir);
}
// options in config file
$optionsConfig = array();
if (!empty($loadPaths)) {
$optionsConfig['additional_import_paths'] = $loadPaths;
}
if ($this->unixNewlines) {
$optionsConfig['sass_options']['unix_newlines'] = true;
}
if ($this->debugInfo) {
$optionsConfig['sass_options']['debug_info'] = true;
}
if ($this->cacheLocation) {
$optionsConfig['sass_options']['cache_location'] = $this->cacheLocation;
}
if ($this->noCache) {
$optionsConfig['sass_options']['no_cache'] = true;
}
if ($this->httpPath) {
$optionsConfig['http_path'] = $this->httpPath;
}
if ($this->httpImagesPath) {
$optionsConfig['http_images_path'] = $this->httpImagesPath;
}
if ($this->httpJavascriptsPath) {
$optionsConfig['http_javascripts_path'] = $this->httpJavascriptsPath;
}
// options in configuration file
if (count($optionsConfig)) {
$config = array();
foreach ($this->plugins as $plugin) {
$config[] = sprintf("require '%s'", addcslashes($plugin, '\\'));
}
foreach ($optionsConfig as $name => $value) {
if (!is_array($value)) {
$config[] = sprintf('%s = "%s"', $name, addcslashes($value, '\\'));
} elseif (!empty($value)) {
$config[] = sprintf('%s = %s', $name, $this->formatArrayToRuby($value));
}
}
$configFile = tempnam($tempDir, 'assetic_compass');
file_put_contents($configFile, implode("\n", $config)."\n");
$pb->add('--config')->add($configFile);
}
$pb->add('--sass-dir')->add('')->add('--css-dir')->add('');
// compass choose the type (sass or scss from the filename)
if (null !== $this->scss) {
$type = $this->scss ? 'scss' : 'sass';
} elseif ($path) {
// FIXME: what if the extension is something else?
$type = pathinfo($path, PATHINFO_EXTENSION);
} else {
$type = 'scss';
}
$tempName = tempnam($tempDir, 'assetic_compass');
unlink($tempName); // FIXME: don't use tempnam() here
// input
$pb->add($input = $tempName.'.'.$type);
file_put_contents($input, $asset->getContent());
// output
$output = $tempName.'.css';
// it's not really usefull but... https://github.com/chriseppstein/compass/issues/376
$pb->setEnv('HOME', sys_get_temp_dir());
$proc = $pb->getProcess();
$code = $proc->run();
if (0 < $code) {
unlink($input);
if (isset($configFile)) {
unlink($configFile);
}
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent(file_get_contents($output));
unlink($input);
unlink($output);
if (isset($configFile)) {
unlink($configFile);
}
}
public function filterDump(AssetInterface $asset)
{
}
private function formatArrayToRuby($array)
{
$output = array();
// does we have an associative array ?
if (count(array_filter(array_keys($array), "is_numeric")) != count($array)) {
foreach($array as $name => $value) {
$output[] = sprintf(' :%s => "%s"', $name, addcslashes($value, '\\'));
}
$output = "{\n".implode(",\n", $output)."\n}";
} else {
foreach($array as $name => $value) {
$output[] = sprintf(' "%s"', addcslashes($value, '\\'));
}
$output = "[\n".implode(",\n", $output)."\n]";
}
return $output;
}
}

View File

@ -1,139 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* CSSEmbed filter
*
* @link https://github.com/nzakas/cssembed
* @author Maxime Thirouin <maxime.thirouin@gmail.com>
*/
class CssEmbedFilter implements FilterInterface
{
private $jarPath;
private $javaPath;
private $charset;
private $mhtml; // Enable MHTML mode.
private $mhtmlRoot; // Use <root> as the MHTML root for the file.
private $root; // Prepends <root> to all relative URLs.
private $skipMissing; // Don't throw an error for missing image files.
private $maxUriLength; // Maximum length for a data URI. Defaults to 32768.
private $maxImageSize; // Maximum image size (in bytes) to convert.
public function __construct($jarPath, $javaPath = '/usr/bin/java')
{
$this->jarPath = $jarPath;
$this->javaPath = $javaPath;
}
public function setCharset($charset)
{
$this->charset = $charset;
}
public function setMhtml($mhtml)
{
$this->mhtml = $mhtml;
}
public function setMhtmlRoot($mhtmlRoot)
{
$this->mhtmlRoot = $mhtmlRoot;
}
public function setRoot($root)
{
$this->root = $root;
}
public function setSkipMissing($skipMissing)
{
$this->skipMissing = $skipMissing;
}
public function setMaxUriLength($maxUriLength)
{
$this->maxUriLength = $maxUriLength;
}
public function setMaxImageSize($maxImageSize)
{
$this->maxImageSize = $maxImageSize;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = new ProcessBuilder(array(
$this->javaPath,
'-jar',
$this->jarPath,
));
if (null !== $this->charset) {
$pb->add('--charset')->add($this->charset);
}
if ($this->mhtml) {
$pb->add('--mhtml');
}
if (null !== $this->mhtmlRoot) {
$pb->add('--mhtmlroot')->add($this->mhtmlRoot);
}
// automatically define root if not already defined
if (null === $this->root) {
$root = $asset->getSourceRoot();
$path = $asset->getSourcePath();
if ($root && $path) {
$pb->add('--root')->add(dirname($root.'/'.$path));
}
} else {
$pb->add('--root')->add($this->root);
}
if ($this->skipMissing) {
$pb->add('--skip-missing');
}
if (null !== $this->maxUriLength) {
$pb->add('--max-uri-length')->add($this->maxUriLength);
}
if (null !== $this->maxImageSize) {
$pb->add('--max-image-size')->add($this->maxImageSize);
}
// input
$pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_cssembed'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 < $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
}

View File

@ -1,107 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Asset\FileAsset;
use Assetic\Asset\HttpAsset;
/**
* Inlines imported stylesheets.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CssImportFilter extends BaseCssFilter
{
private $importFilter;
/**
* Constructor.
*
* @param FilterInterface $importFilter Filter for each imported asset
*/
public function __construct(FilterInterface $importFilter = null)
{
$this->importFilter = $importFilter ?: new CssRewriteFilter();
}
public function filterLoad(AssetInterface $asset)
{
$importFilter = $this->importFilter;
$sourceRoot = $asset->getSourceRoot();
$sourcePath = $asset->getSourcePath();
$callback = function($matches) use($importFilter, $sourceRoot, $sourcePath)
{
if (!$matches['url'] || null === $sourceRoot) {
return $matches[0];
}
$importRoot = $sourceRoot;
if (false !== strpos($matches['url'], '://')) {
// absolute
list($importScheme, $tmp) = explode('://', $matches['url'], 2);
list($importHost, $importPath) = explode('/', $tmp, 2);
$importRoot = $importScheme.'://'.$importHost;
} elseif (0 === strpos($matches['url'], '//')) {
// protocol-relative
list($importHost, $importPath) = explode('/', substr($matches['url'], 2), 2);
$importHost = '//'.$importHost;
} elseif ('/' == $matches['url'][0]) {
// root-relative
$importPath = substr($matches['url'], 1);
} elseif (null !== $sourcePath) {
// document-relative
$importPath = $matches['url'];
if ('.' != $sourceDir = dirname($sourcePath)) {
$importPath = $sourceDir.'/'.$importPath;
}
} else {
return $matches[0];
}
// ignore other imports
if ('css' != pathinfo($importPath, PATHINFO_EXTENSION)) {
return $matches[0];
}
$importSource = $importRoot.'/'.$importPath;
if (false !== strpos($importSource, '://') || 0 === strpos($importSource, '//')) {
$import = new HttpAsset($importSource, array($importFilter), true);
} elseif (!file_exists($importSource)) {
// ignore not found imports
return $matches[0];
} else {
$import = new FileAsset($importSource, array($importFilter), $importRoot, $importPath);
}
$import->setTargetPath($sourcePath);
return $import->dump();
};
$content = $asset->getContent();
$lastHash = md5($content);
do {
$content = $this->filterImports($content, $callback);
$hash = md5($content);
} while ($lastHash != $hash && $lastHash = $hash);
$asset->setContent($content);
}
public function filterDump(AssetInterface $asset)
{
}
}

View File

@ -1,74 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
/**
* Filters assets through CssMin.
*
* @link http://code.google.com/p/cssmin
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CssMinFilter implements FilterInterface
{
private $filters;
private $plugins;
public function __construct()
{
$this->filters = array();
$this->plugins = array();
}
public function setFilters(array $filters)
{
$this->filters = $filters;
}
public function setFilter($name, $value)
{
$this->filters[$name] = $value;
}
public function setPlugins(array $plugins)
{
$this->plugins = $plugins;
}
public function setPlugin($name, $value)
{
$this->plugins[$name] = $value;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$filters = $this->filters;
$plugins = $this->plugins;
if (isset($filters['ImportImports']) && true === $filters['ImportImports']) {
$root = $asset->getSourceRoot();
$path = $asset->getSourcePath();
if ($root && $path) {
$filters['ImportImports'] = array('BasePath' => dirname($root.'/'.$path));
} else {
unset($filters['ImportImports']);
}
}
$asset->setContent(\CssMin::minify($asset->getContent(), $filters, $plugins));
}
}

View File

@ -1,94 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
/**
* Fixes relative CSS urls.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CssRewriteFilter extends BaseCssFilter
{
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$sourceBase = $asset->getSourceRoot();
$sourcePath = $asset->getSourcePath();
$targetPath = $asset->getTargetPath();
if (null === $sourcePath || null === $targetPath || $sourcePath == $targetPath) {
return;
}
// learn how to get from the target back to the source
if (false !== strpos($sourceBase, '://')) {
list($scheme, $url) = explode('://', $sourceBase.'/'.$sourcePath, 2);
list($host, $path) = explode('/', $url, 2);
$host = $scheme.'://'.$host.'/';
$path = false === strpos($path, '/') ? '' : dirname($path);
$path .= '/';
} else {
// assume source and target are on the same host
$host = '';
// pop entries off the target until it fits in the source
if ('.' == dirname($sourcePath)) {
$path = str_repeat('../', substr_count($targetPath, '/'));
} elseif ('.' == $targetDir = dirname($targetPath)) {
$path = dirname($sourcePath).'/';
} else {
$path = '';
while (0 !== strpos($sourcePath, $targetDir)) {
if (false !== $pos = strrpos($targetDir, '/')) {
$targetDir = substr($targetDir, 0, $pos);
$path .= '../';
} else {
$targetDir = '';
$path .= '../';
break;
}
}
$path .= ltrim(substr(dirname($sourcePath).'/', strlen($targetDir)), '/');
}
}
$content = $this->filterReferences($asset->getContent(), function($matches) use($host, $path)
{
if (false !== strpos($matches['url'], '://') || 0 === strpos($matches['url'], '//') || 0 === strpos($matches['url'], 'data:')) {
// absolute or protocol-relative or data uri
return $matches[0];
}
if ('/' == $matches['url'][0]) {
// root relative
return str_replace($matches['url'], $host.$matches['url'], $matches[0]);
}
// document relative
$url = $matches['url'];
while (0 === strpos($url, '../') && 2 <= substr_count($path, '/')) {
$path = substr($path, 0, strrpos(rtrim($path, '/'), '/') + 1);
$url = substr($url, 3);
}
return str_replace($matches['url'], $host.$path.$url, $matches[0]);
});
$asset->setContent($content);
}
}

View File

@ -1,82 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
/**
* A collection of filters.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class FilterCollection implements FilterInterface, \IteratorAggregate, \Countable
{
private $filters = array();
public function __construct($filters = array())
{
foreach ($filters as $filter) {
$this->ensure($filter);
}
}
/**
* Checks that the current collection contains the supplied filter.
*
* If the supplied filter is another filter collection, each of its
* filters will be checked.
*/
public function ensure(FilterInterface $filter)
{
if ($filter instanceof \Traversable) {
foreach ($filter as $f) {
$this->ensure($f);
}
} elseif (!in_array($filter, $this->filters, true)) {
$this->filters[] = $filter;
}
}
public function all()
{
return $this->filters;
}
public function clear()
{
$this->filters = array();
}
public function filterLoad(AssetInterface $asset)
{
foreach ($this->filters as $filter) {
$filter->filterLoad($asset);
}
}
public function filterDump(AssetInterface $asset)
{
foreach ($this->filters as $filter) {
$filter->filterDump($asset);
}
}
public function getIterator()
{
return new \ArrayIterator($this->filters);
}
public function count()
{
return count($this->filters);
}
}

View File

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
/**
* A filter manipulates an asset at load and dump.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
interface FilterInterface
{
/**
* Filters an asset after it has been loaded.
*
* @param AssetInterface $asset An asset
*/
function filterLoad(AssetInterface $asset);
/**
* Filters an asset just before it's dumped.
*
* @param AssetInterface $asset An asset
*/
function filterDump(AssetInterface $asset);
}

View File

@ -1,84 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter\GoogleClosure;
use Assetic\Asset\AssetInterface;
use Assetic\Filter\FilterInterface;
/**
* Base filter for the Google Closure Compiler implementations.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
abstract class BaseCompilerFilter implements FilterInterface
{
// compilation levels
const COMPILE_WHITESPACE_ONLY = 'WHITESPACE_ONLY';
const COMPILE_SIMPLE_OPTIMIZATIONS = 'SIMPLE_OPTIMIZATIONS';
const COMPILE_ADVANCED_OPTIMIZATIONS = 'ADVANCED_OPTIMIZATIONS';
// formatting modes
const FORMAT_PRETTY_PRINT = 'pretty_print';
const FORMAT_PRINT_INPUT_DELIMITER = 'print_input_delimiter';
// warning levels
const LEVEL_QUIET = 'QUIET';
const LEVEL_DEFAULT = 'DEFAULT';
const LEVEL_VERBOSE = 'VERBOSE';
protected $compilationLevel;
protected $jsExterns;
protected $externsUrl;
protected $excludeDefaultExterns;
protected $formatting;
protected $useClosureLibrary;
protected $warningLevel;
public function setCompilationLevel($compilationLevel)
{
$this->compilationLevel = $compilationLevel;
}
public function setJsExterns($jsExterns)
{
$this->jsExterns = $jsExterns;
}
public function setExternsUrl($externsUrl)
{
$this->externsUrl = $externsUrl;
}
public function setExcludeDefaultExterns($excludeDefaultExterns)
{
$this->excludeDefaultExterns = $excludeDefaultExterns;
}
public function setFormatting($formatting)
{
$this->formatting = $formatting;
}
public function setUseClosureLibrary($useClosureLibrary)
{
$this->useClosureLibrary = $useClosureLibrary;
}
public function setWarningLevel($warningLevel)
{
$this->warningLevel = $warningLevel;
}
public function filterLoad(AssetInterface $asset)
{
}
}

View File

@ -1,83 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter\GoogleClosure;
use Assetic\Asset\AssetInterface;
/**
* Filter for the Google Closure Compiler API.
*
* @link https://developers.google.com/closure/compiler/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CompilerApiFilter extends BaseCompilerFilter
{
public function filterDump(AssetInterface $asset)
{
$query = array(
'js_code' => $asset->getContent(),
'output_format' => 'json',
'output_info' => 'compiled_code',
);
if (null !== $this->compilationLevel) {
$query['compilation_level'] = $this->compilationLevel;
}
if (null !== $this->jsExterns) {
$query['js_externs'] = $this->jsExterns;
}
if (null !== $this->externsUrl) {
$query['externs_url'] = $this->externsUrl;
}
if (null !== $this->excludeDefaultExterns) {
$query['exclude_default_externs'] = $this->excludeDefaultExterns ? 'true' : 'false';
}
if (null !== $this->formatting) {
$query['formatting'] = $this->formatting;
}
if (null !== $this->useClosureLibrary) {
$query['use_closure_library'] = $this->useClosureLibrary ? 'true' : 'false';
}
if (null !== $this->warningLevel) {
$query['warning_level'] = $this->warningLevel;
}
$context = stream_context_create(array('http' => array(
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => http_build_query($query),
)));
$response = file_get_contents('http://closure-compiler.appspot.com/compile', false, $context);
$data = json_decode($response);
if (isset($data->serverErrors) && 0 < count($data->serverErrors)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some server errors: '.print_r($data->serverErrors, true)));
// @codeCoverageIgnoreEnd
}
if (isset($data->errors) && 0 < count($data->errors)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException(sprintf('The Google Closure Compiler API threw some errors: '.print_r($data->errors, true)));
// @codeCoverageIgnoreEnd
}
$asset->setContent($data->compiledCode);
}
}

View File

@ -1,90 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter\GoogleClosure;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Filter for the Google Closure Compiler JAR.
*
* @link https://developers.google.com/closure/compiler/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CompilerJarFilter extends BaseCompilerFilter
{
private $jarPath;
private $javaPath;
public function __construct($jarPath, $javaPath = '/usr/bin/java')
{
$this->jarPath = $jarPath;
$this->javaPath = $javaPath;
}
public function filterDump(AssetInterface $asset)
{
$cleanup = array();
$pb = new ProcessBuilder(array(
$this->javaPath,
'-jar',
$this->jarPath,
));
if (null !== $this->compilationLevel) {
$pb->add('--compilation_level')->add($this->compilationLevel);
}
if (null !== $this->jsExterns) {
$cleanup[] = $externs = tempnam(sys_get_temp_dir(), 'assetic_google_closure_compiler');
file_put_contents($externs, $this->jsExterns);
$pb->add('--externs')->add($externs);
}
if (null !== $this->externsUrl) {
$cleanup[] = $externs = tempnam(sys_get_temp_dir(), 'assetic_google_closure_compiler');
file_put_contents($externs, file_get_contents($this->externsUrl));
$pb->add('--externs')->add($externs);
}
if (null !== $this->excludeDefaultExterns) {
$pb->add('--use_only_custom_externs');
}
if (null !== $this->formatting) {
$pb->add('--formatting')->add($this->formatting);
}
if (null !== $this->useClosureLibrary) {
$pb->add('--manage_closure_dependencies');
}
if (null !== $this->warningLevel) {
$pb->add('--warning_level')->add($this->warningLevel);
}
$pb->add('--js')->add($cleanup[] = $input = tempnam(sys_get_temp_dir(), 'assetic_google_closure_compiler'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
array_map('unlink', $cleanup);
if (0 < $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
}

View File

@ -1,142 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Filter for the Google Closure Stylesheets Compiler JAR.
*
* @link http://code.google.com/p/closure-stylesheets/
* @author Matthias Krauser <matthias@krauser.eu>
*/
class GssFilter implements FilterInterface
{
private $jarPath;
private $javaPath;
private $allowUnrecognizedFunctions;
private $allowedNonStandardFunctions;
private $copyrightNotice;
private $define;
private $gssFunctionMapProvider;
private $inputOrientation;
private $outputOrientation;
private $prettyPrint;
public function __construct($jarPath, $javaPath = '/usr/bin/java')
{
$this->jarPath = $jarPath;
$this->javaPath = $javaPath;
}
public function setAllowUnrecognizedFunctions($allowUnrecognizedFunctions)
{
$this->allowUnrecognizedFunctions = $allowUnrecognizedFunctions;
}
public function setAllowedNonStandardFunctions($allowNonStandardFunctions)
{
$this->allowedNonStandardFunctions = $allowNonStandardFunctions;
}
public function setCopyrightNotice($copyrightNotice)
{
$this->copyrightNotice = $copyrightNotice;
}
public function setDefine($define)
{
$this->define = $define;
}
public function setGssFunctionMapProvider($gssFunctionMapProvider)
{
$this->gssFunctionMapProvider = $gssFunctionMapProvider;
}
public function setInputOrientation($inputOrientation)
{
$this->inputOrientation = $inputOrientation;
}
public function setOutputOrientation($outputOrientation)
{
$this->outputOrientation = $outputOrientation;
}
public function setPrettyPrint($prettyPrint)
{
$this->prettyPrint = $prettyPrint;
}
public function filterLoad(AssetInterface $asset)
{
$cleanup = array();
$pb = new ProcessBuilder(array(
$this->javaPath,
'-jar',
$this->jarPath,
));
if (null !== $this->allowUnrecognizedFunctions) {
$pb->add('--allow-unrecognized-functions');
}
if (null !== $this->allowedNonStandardFunctions) {
$pb->add('--allowed_non_standard_functions')->add($this->allowedNonStandardFunctions);
}
if (null !== $this->copyrightNotice) {
$pb->add('--copyright-notice')->add($this->copyrightNotice);
}
if (null !== $this->define) {
$pb->add('--define')->add($this->define);
}
if (null !== $this->gssFunctionMapProvider) {
$pb->add('--gss-function-map-provider')->add($this->gssFunctionMapProvider);
}
if (null !== $this->inputOrientation) {
$pb->add('--input-orientation')->add($this->inputOrientation);
}
if (null !== $this->outputOrientation) {
$pb->add('--output-orientation')->add($this->outputOrientation);
}
if (null !== $this->prettyPrint) {
$pb->add('--pretty-print');
}
$pb->add($cleanup[] = $input = tempnam(sys_get_temp_dir(), 'assetic_google_closure_stylesheets_compiler'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
array_map('unlink', $cleanup);
if (0 < $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
}

View File

@ -1,81 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Runs assets through Jpegoptim.
*
* @link http://www.kokkonen.net/tjko/projects.html
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class JpegoptimFilter implements FilterInterface
{
private $jpegoptimBin;
private $stripAll;
private $max;
/**
* Constructor.
*
* @param string $jpegoptimBin Path to the jpegoptim binary
*/
public function __construct($jpegoptimBin = '/usr/bin/jpegoptim')
{
$this->jpegoptimBin = $jpegoptimBin;
}
public function setStripAll($stripAll)
{
$this->stripAll = $stripAll;
}
public function setMax($max)
{
$this->max = $max;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = new ProcessBuilder(array($this->jpegoptimBin));
if ($this->stripAll) {
$pb->add('--strip-all');
}
if ($this->max) {
$pb->add('--max='.$this->max);
}
$pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_jpegoptim'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$proc->run();
if (false !== strpos($proc->getOutput(), 'ERROR')) {
unlink($input);
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent(file_get_contents($input));
unlink($input);
}
}

View File

@ -1,103 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Runs assets through jpegtran.
*
* @link http://jpegclub.org/jpegtran/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class JpegtranFilter implements FilterInterface
{
const COPY_NONE = 'none';
const COPY_COMMENTS = 'comments';
const COPY_ALL = 'all';
private $jpegtranBin;
private $optimize;
private $copy;
private $progressive;
private $restart;
/**
* Constructor.
*
* @param string $jpegtranBin Path to the jpegtran binary
*/
public function __construct($jpegtranBin = '/usr/bin/jpegtran')
{
$this->jpegtranBin = $jpegtranBin;
}
public function setOptimize($optimize)
{
$this->optimize = $optimize;
}
public function setCopy($copy)
{
$this->copy = $copy;
}
public function setProgressive($progressive)
{
$this->progressive = $progressive;
}
public function setRestart($restart)
{
$this->restart = $restart;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = new ProcessBuilder(array($this->jpegtranBin));
if ($this->optimize) {
$pb->add('-optimize');
}
if ($this->copy) {
$pb->add('-copy')->add($this->copy);
}
if ($this->progressive) {
$pb->add('-progressive');
}
if (null !== $this->restart) {
$pb->add('-restart')->add($this->restart);
}
$pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_jpegtran'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 < $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
}

View File

@ -1,114 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Loads LESS files.
*
* @link http://lesscss.org/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class LessFilter implements FilterInterface
{
private $nodeBin;
private $nodePaths;
private $compress;
/**
* Constructor.
*
* @param string $nodeBin The path to the node binary
* @param array $nodePaths An array of node paths
*/
public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array())
{
$this->nodeBin = $nodeBin;
$this->nodePaths = $nodePaths;
}
public function setCompress($compress)
{
$this->compress = $compress;
}
public function filterLoad(AssetInterface $asset)
{
static $format = <<<'EOF'
var less = require('less');
var sys = require(process.binding('natives').util ? 'util' : 'sys');
new(less.Parser)(%s).parse(%s, function(e, tree) {
if (e) {
less.writeError(e);
process.exit(2);
}
try {
sys.print(tree.toCSS(%s));
} catch (e) {
less.writeError(e);
process.exit(3);
}
});
EOF;
$root = $asset->getSourceRoot();
$path = $asset->getSourcePath();
// parser options
$parserOptions = array();
if ($root && $path) {
$parserOptions['paths'] = array(dirname($root.'/'.$path));
$parserOptions['filename'] = basename($path);
}
// tree options
$treeOptions = array();
if (null !== $this->compress) {
$treeOptions['compress'] = $this->compress;
}
$pb = new ProcessBuilder();
$pb->inheritEnvironmentVariables();
// node.js configuration
if (0 < count($this->nodePaths)) {
$pb->setEnv('NODE_PATH', implode(':', $this->nodePaths));
}
$pb->add($this->nodeBin)->add($input = tempnam(sys_get_temp_dir(), 'assetic_less'));
file_put_contents($input, sprintf($format,
json_encode($parserOptions),
json_encode($asset->getContent()),
json_encode($treeOptions)
));
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 < $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
}

View File

@ -1,48 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
/**
* Loads LESS files using the PHP implementation of less, lessphp.
*
* Less files are mostly compatible, but there are slight differences.
*
* To use this, you need to clone https://github.com/leafo/lessphp and make
* sure to either include lessphp.inc.php or tell your autoloader that's where
* lessc is located.
*
* @link http://leafo.net/lessphp/
*
* @author David Buchmann <david@liip.ch>
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class LessphpFilter implements FilterInterface
{
public function filterLoad(AssetInterface $asset)
{
$root = $asset->getSourceRoot();
$path = $asset->getSourcePath();
$lc = new \lessc();
if ($root && $path) {
$lc->importDir = dirname($root.'/'.$path);
}
$asset->setContent($lc->parse($asset->getContent()));
}
public function filterDump(AssetInterface $asset)
{
}
}

View File

@ -1,75 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Runs assets through OptiPNG.
*
* @link http://optipng.sourceforge.net/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class OptiPngFilter implements FilterInterface
{
private $optipngBin;
private $level;
/**
* Constructor.
*
* @param string $optipngBin Path to the optipng binary
*/
public function __construct($optipngBin = '/usr/bin/optipng')
{
$this->optipngBin = $optipngBin;
}
public function setLevel($level)
{
$this->level = $level;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = new ProcessBuilder(array($this->optipngBin));
if (null !== $this->level) {
$pb->add('-o')->add($this->level);
}
$pb->add('-out')->add($output = tempnam(sys_get_temp_dir(), 'assetic_optipng'));
unlink($output);
$pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_optipng'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
if (0 < $code) {
unlink($input);
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent(file_get_contents($output));
unlink($input);
unlink($output);
}
}

View File

@ -1,64 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
/**
* Runs assets through Packager.
*
* @link https://github.com/kamicane/packager
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class PackagerFilter implements FilterInterface
{
private $packages;
public function __construct(array $packages = array())
{
$this->packages = $packages;
}
public function addPackage($package)
{
$this->packages[] = $package;
}
public function filterLoad(AssetInterface $asset)
{
static $manifest = <<<EOF
name: Application%s
sources: [source.js]
EOF;
$hash = substr(sha1(time().rand(11111, 99999)), 0, 7);
$package = sys_get_temp_dir().'/assetic_packager_'.$hash;
mkdir($package);
file_put_contents($package.'/package.yml', sprintf($manifest, $hash));
file_put_contents($package.'/source.js', $asset->getContent());
$packager = new \Packager(array_merge(array($package), $this->packages));
$content = $packager->build(array(), array(), array('Application'.$hash));
unlink($package.'/package.yml');
unlink($package.'/source.js');
rmdir($package);
$asset->setContent($content);
}
public function filterDump(AssetInterface $asset)
{
}
}

View File

@ -1,128 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Runs assets through pngout.
*
* @link http://advsys.net/ken/utils.htm#pngout
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class PngoutFilter implements FilterInterface
{
// -c#
const COLOR_GREY = '0';
const COLOR_RGB = '2';
const COLOR_PAL = '3';
const COLOR_GRAY_ALPHA = '4';
const COLOR_RGB_ALPHA = '6';
// -f#
const FILTER_NONE = '0';
const FILTER_X = '1';
const FILTER_Y = '2';
const FILTER_X_Y = '3';
const FILTER_PAETH = '4';
const FILTER_MIXED = '5';
// -s#
const STRATEGY_XTREME = '0';
const STRATEGY_INTENSE = '1';
const STRATEGY_LONGEST_MATCH = '2';
const STRATEGY_HUFFMAN_ONLY = '3';
const STRATEGY_UNCOMPRESSED = '4';
private $pngoutBin;
private $color;
private $filter;
private $strategy;
private $blockSplitThreshold;
/**
* Constructor.
*
* @param string $pngoutBin Path to the pngout binary
*/
public function __construct($pngoutBin = '/usr/bin/pngout')
{
$this->pngoutBin = $pngoutBin;
}
public function setColor($color)
{
$this->color = $color;
}
public function setFilter($filter)
{
$this->filter = $filter;
}
public function setStrategy($strategy)
{
$this->strategy = $strategy;
}
public function setBlockSplitThreshold($blockSplitThreshold)
{
$this->blockSplitThreshold = $blockSplitThreshold;
}
public function filterLoad(AssetInterface $asset)
{
}
public function filterDump(AssetInterface $asset)
{
$pb = new ProcessBuilder(array($this->pngoutBin));
if (null !== $this->color) {
$pb->add('-c'.$this->color);
}
if (null !== $this->filter) {
$pb->add('-f'.$this->filter);
}
if (null !== $this->strategy) {
$pb->add('-s'.$this->strategy);
}
if (null !== $this->blockSplitThreshold) {
$pb->add('-b'.$this->blockSplitThreshold);
}
$pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_pngout'));
file_put_contents($input, $asset->getContent());
$output = tempnam(sys_get_temp_dir(), 'assetic_pngout');
unlink($output);
$pb->add($output .= '.png');
$proc = $pb->getProcess();
$code = $proc->run();
if (0 < $code) {
unlink($input);
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent(file_get_contents($output));
unlink($input);
unlink($output);
}
}

View File

@ -1,169 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter\Sass;
use Assetic\Asset\AssetInterface;
use Assetic\Filter\FilterInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Loads SASS files.
*
* @link http://sass-lang.com/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class SassFilter implements FilterInterface
{
const STYLE_NESTED = 'nested';
const STYLE_EXPANDED = 'expanded';
const STYLE_COMPACT = 'compact';
const STYLE_COMPRESSED = 'compressed';
private $sassPath;
private $unixNewlines;
private $scss;
private $style;
private $quiet;
private $debugInfo;
private $lineNumbers;
private $loadPaths = array();
private $cacheLocation;
private $noCache;
private $compass;
public function __construct($sassPath = '/usr/bin/sass')
{
$this->sassPath = $sassPath;
$this->cacheLocation = realpath(sys_get_temp_dir());
}
public function setUnixNewlines($unixNewlines)
{
$this->unixNewlines = $unixNewlines;
}
public function setScss($scss)
{
$this->scss = $scss;
}
public function setStyle($style)
{
$this->style = $style;
}
public function setQuiet($quiet)
{
$this->quiet = $quiet;
}
public function setDebugInfo($debugInfo)
{
$this->debugInfo = $debugInfo;
}
public function setLineNumbers($lineNumbers)
{
$this->lineNumbers = $lineNumbers;
}
public function addLoadPath($loadPath)
{
$this->loadPaths[] = $loadPath;
}
public function setCacheLocation($cacheLocation)
{
$this->cacheLocation = $cacheLocation;
}
public function setNoCache($noCache)
{
$this->noCache = $noCache;
}
public function setCompass($compass)
{
$this->compass = $compass;
}
public function filterLoad(AssetInterface $asset)
{
$pb = new ProcessBuilder(array($this->sassPath));
$root = $asset->getSourceRoot();
$path = $asset->getSourcePath();
if ($root && $path) {
$pb->add('--load-path')->add(dirname($root.'/'.$path));
}
if ($this->unixNewlines) {
$pb->add('--unix-newlines');
}
if (true === $this->scss || (null === $this->scss && 'scss' == pathinfo($path, PATHINFO_EXTENSION))) {
$pb->add('--scss');
}
if ($this->style) {
$pb->add('--style')->add($this->style);
}
if ($this->quiet) {
$pb->add('--quiet');
}
if ($this->debugInfo) {
$pb->add('--debug-info');
}
if ($this->lineNumbers) {
$pb->add('--line-numbers');
}
foreach ($this->loadPaths as $loadPath) {
$pb->add('--load-path')->add($loadPath);
}
if ($this->cacheLocation) {
$pb->add('--cache-location')->add($this->cacheLocation);
}
if ($this->noCache) {
$pb->add('--no-cache');
}
if ($this->compass) {
$pb->add('--compass');
}
// input
$pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_sass'));
file_put_contents($input, $asset->getContent());
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 < $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
}

View File

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter\Sass;
/**
* Loads SCSS files.
*
* @link http://sass-lang.com/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class ScssFilter extends SassFilter
{
public function __construct($sassPath = '/usr/bin/sass')
{
parent::__construct($sassPath);
$this->setScss(true);
}
}

View File

@ -1,148 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Runs assets through Sprockets.
*
* Requires Sprockets 1.0.x.
*
* @link http://getsprockets.org/
* @link http://github.com/sstephenson/sprockets/tree/1.0.x
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class SprocketsFilter implements FilterInterface
{
private $sprocketsLib;
private $rubyBin;
private $includeDirs;
private $assetRoot;
/**
* Constructor.
*
* @param string $sprocketsLib Path to the Sprockets lib/ directory
* @param string $rubyBin Path to the ruby binary
*/
public function __construct($sprocketsLib = null, $rubyBin = '/usr/bin/ruby')
{
$this->sprocketsLib = $sprocketsLib;
$this->rubyBin = $rubyBin;
$this->includeDirs = array();
}
public function addIncludeDir($directory)
{
$this->includeDirs[] = $directory;
}
public function setAssetRoot($assetRoot)
{
$this->assetRoot = $assetRoot;
}
/**
* Hack around a bit, get the job done.
*/
public function filterLoad(AssetInterface $asset)
{
static $format = <<<'EOF'
#!/usr/bin/env ruby
require %s
%s
options = { :load_path => [],
:source_files => [%s],
:expand_paths => false }
%ssecretary = Sprockets::Secretary.new(options)
secretary.install_assets if options[:asset_root]
print secretary.concatenation
EOF;
$more = '';
foreach ($this->includeDirs as $directory) {
$more .= 'options[:load_path] << '.var_export($directory, true)."\n";
}
if (null !== $this->assetRoot) {
$more .= 'options[:asset_root] = '.var_export($this->assetRoot, true)."\n";
}
if ($more) {
$more .= "\n";
}
$tmpAsset = tempnam(sys_get_temp_dir(), 'assetic_sprockets');
file_put_contents($tmpAsset, $asset->getContent());
$input = tempnam(sys_get_temp_dir(), 'assetic_sprockets');
file_put_contents($input, sprintf($format,
$this->sprocketsLib
? sprintf('File.join(%s, \'sprockets\')', var_export($this->sprocketsLib, true))
: '\'sprockets\'',
$this->getHack($asset),
var_export($tmpAsset, true),
$more
));
$pb = new ProcessBuilder(array(
$this->rubyBin,
$input,
));
$proc = $pb->getProcess();
$code = $proc->run();
unlink($tmpAsset);
unlink($input);
if (0 < $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
public function filterDump(AssetInterface $asset)
{
}
private function getHack(AssetInterface $asset)
{
static $format = <<<'EOF'
module Sprockets
class Preprocessor
protected
def pathname_for_relative_require_from(source_line)
Sprockets::Pathname.new(@environment, File.join(%s, location_from(source_line)))
end
end
end
EOF;
$root = $asset->getSourceRoot();
$path = $asset->getSourcePath();
if ($root && $path) {
return sprintf($format, var_export(dirname($root.'/'.$path), true));
}
}
}

View File

@ -1,117 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter;
use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Loads STYL files.
*
* @link http://learnboost.github.com/stylus/
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class StylusFilter implements FilterInterface
{
private $nodeBin;
private $nodePaths;
private $compress;
/**
* Constructs filter.
*
* @param string $nodeBin The path to the node binary
* @param array $nodePaths An array of node paths
*/
public function __construct($nodeBin = '/usr/bin/node', array $nodePaths = array())
{
$this->nodeBin = $nodeBin;
$this->nodePaths = $nodePaths;
}
/**
* Enable output compression.
*
* @param boolean $compress
*/
public function setCompress($compress)
{
$this->compress = $compress;
}
/**
* {@inheritdoc}
*/
public function filterLoad(AssetInterface $asset)
{
static $format = <<<'EOF'
var stylus = require('stylus');
var sys = require(process.binding('natives').util ? 'util' : 'sys');
stylus(%s, %s).render(function(e, css){
if (e) {
throw e;
}
sys.print(css);
process.exit(0);
});
EOF;
$root = $asset->getSourceRoot();
$path = $asset->getSourcePath();
// parser options
$parserOptions = array();
if ($root && $path) {
$parserOptions['paths'] = array(dirname($root.'/'.$path));
$parserOptions['filename'] = basename($path);
}
if (null !== $this->compress) {
$parserOptions['compress'] = $this->compress;
}
$pb = new ProcessBuilder();
$pb->inheritEnvironmentVariables();
// node.js configuration
if (0 < count($this->nodePaths)) {
$pb->setEnv('NODE_PATH', implode(':', $this->nodePaths));
}
$pb->add($this->nodeBin)->add($input = tempnam(sys_get_temp_dir(), 'assetic_stylus'));
file_put_contents($input, sprintf($format,
json_encode($asset->getContent()),
json_encode($parserOptions)
));
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 < $code) {
throw FilterException::fromProcess($proc)->setInput($asset->getContent());
}
$asset->setContent($proc->getOutput());
}
/**
* {@inheritdoc}
*/
public function filterDump(AssetInterface $asset)
{
}
}

View File

@ -1,108 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter\Yui;
use Assetic\Asset\AssetInterface;
use Assetic\Filter\FilterInterface;
use Assetic\Exception\FilterException;
use Symfony\Component\Process\ProcessBuilder;
/**
* Base YUI compressor filter.
*
* @link http://developer.yahoo.com/yui/compressor/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
abstract class BaseCompressorFilter implements FilterInterface
{
private $jarPath;
private $javaPath;
private $charset;
private $lineBreak;
public function __construct($jarPath, $javaPath = '/usr/bin/java')
{
$this->jarPath = $jarPath;
$this->javaPath = $javaPath;
}
public function setCharset($charset)
{
$this->charset = $charset;
}
public function setLineBreak($lineBreak)
{
$this->lineBreak = $lineBreak;
}
public function filterLoad(AssetInterface $asset)
{
}
/**
* Compresses a string.
*
* @param string $content The content to compress
* @param string $type The type of content, either "js" or "css"
* @param array $options An indexed array of additional options
*
* @return string The compressed content
*/
protected function compress($content, $type, $options = array())
{
$pb = new ProcessBuilder(array(
$this->javaPath,
'-jar',
$this->jarPath,
));
foreach ($options as $option) {
$pb->add($option);
}
if (null !== $this->charset) {
$pb->add('--charset')->add($this->charset);
}
if (null !== $this->lineBreak) {
$pb->add('--line-break')->add($this->lineBreak);
}
// input and output files
$tempDir = realpath(sys_get_temp_dir());
$hash = substr(sha1(time().rand(11111, 99999)), 0, 7);
$input = $tempDir.DIRECTORY_SEPARATOR.$hash.'.'.$type;
$output = $tempDir.DIRECTORY_SEPARATOR.$hash.'-min.'.$type;
file_put_contents($input, $content);
$pb->add('-o')->add($output)->add($input);
$proc = $pb->getProcess();
$code = $proc->run();
unlink($input);
if (0 < $code) {
if (file_exists($output)) {
unlink($output);
}
throw FilterException::fromProcess($proc)->setInput($content);
} elseif (!file_exists($output)) {
throw new \RuntimeException('Error creating output file.');
}
$retval = file_get_contents($output);
unlink($output);
return $retval;
}
}

View File

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter\Yui;
use Assetic\Asset\AssetInterface;
/**
* CSS YUI compressor filter.
*
* @link http://developer.yahoo.com/yui/compressor/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class CssCompressorFilter extends BaseCompressorFilter
{
public function filterDump(AssetInterface $asset)
{
$asset->setContent($this->compress($asset->getContent(), 'css'));
}
}

View File

@ -1,61 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Filter\Yui;
use Assetic\Asset\AssetInterface;
/**
* Javascript YUI compressor filter.
*
* @link http://developer.yahoo.com/yui/compressor/
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class JsCompressorFilter extends BaseCompressorFilter
{
private $nomunge;
private $preserveSemi;
private $disableOptimizations;
public function setNomunge($nomunge = true)
{
$this->nomunge = $nomunge;
}
public function setPreserveSemi($preserveSemi)
{
$this->preserveSemi = $preserveSemi;
}
public function setDisableOptimizations($disableOptimizations)
{
$this->disableOptimizations = $disableOptimizations;
}
public function filterDump(AssetInterface $asset)
{
$options = array();
if ($this->nomunge) {
$options[] = '--nomunge';
}
if ($this->preserveSemi) {
$options[] = '--preserve-semi';
}
if ($this->disableOptimizations) {
$options[] = '--disable-optimizations';
}
$asset->setContent($this->compress($asset->getContent(), 'js', $options));
}
}

View File

@ -1,64 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic;
use Assetic\Filter\FilterInterface;
/**
* Manages the available filters.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class FilterManager
{
private $filters = array();
public function set($alias, FilterInterface $filter)
{
$this->checkName($alias);
$this->filters[$alias] = $filter;
}
public function get($alias)
{
if (!isset($this->filters[$alias])) {
throw new \InvalidArgumentException(sprintf('There is no "%s" filter.', $alias));
}
return $this->filters[$alias];
}
public function has($alias)
{
return isset($this->filters[$alias]);
}
public function getNames()
{
return array_keys($this->filters);
}
/**
* Checks that a name is valid.
*
* @param string $name An asset name candidate
*
* @throws InvalidArgumentException If the asset name is invalid
*/
protected function checkName($name)
{
if (!ctype_alnum(str_replace('_', '', $name))) {
throw new \InvalidArgumentException(sprintf('The name "%s" is invalid.', $name));
}
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace Assetic\Util;
/**
* Path Utils.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
abstract class PathUtils
{
public static function resolvePath($path, array $vars, array $values)
{
$map = array();
foreach ($vars as $var) {
if (false === strpos($path, '{'.$var.'}')) {
continue;
}
if (!isset($values[$var])) {
throw new \InvalidArgumentException(sprintf('The path "%s" contains the variable "%s", but was not given any value for it.', $path, $var));
}
$map['{'.$var.'}'] = $values[$var];
}
return strtr($path, $map);
}
private final function __construct() { }
}

View File

@ -1,44 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic\Util;
/**
* An object that can be used as either a string or array.
*
* @author Kris Wallsmith <kris.wallsmith@gmail.com>
*/
class TraversableString implements \IteratorAggregate, \Countable
{
private $one;
private $many;
public function __construct($one, array $many)
{
$this->one = $one;
$this->many = $many;
}
public function getIterator()
{
return new \ArrayIterator($this->many);
}
public function count()
{
return count($this->many);
}
public function __toString()
{
return (string) $this->one;
}
}

View File

@ -1,29 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Assetic;
/**
* Value Supplier Interface.
*
* Implementations determine runtime values for compile-time variables.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface ValueSupplierInterface
{
/**
* Returns a map of values.
*
* @return array
*/
function getValues();
}

View File

@ -1,121 +0,0 @@
<?php
/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2011 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Assetic\Factory\AssetFactory;
use Assetic\Util\TraversableString;
/**
* Initializes the global Assetic object.
*
* @param AssetFactory $factory The asset factory
*/
function assetic_init(AssetFactory $factory)
{
global $_assetic;
$_assetic = new stdClass();
$_assetic->factory = $factory;
}
/**
* Returns an array of javascript URLs.
*
* @param array|string $inputs Input strings
* @param array|string $filters Filter names
* @param array $options An array of options
*
* @return array An array of javascript URLs
*/
function assetic_javascripts($inputs = array(), $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = 'js/*.js';
}
return _assetic_urls($inputs, $filters, $options);
}
/**
* Returns an array of stylesheet URLs.
*
* @param array|string $inputs Input strings
* @param array|string $filters Filter names
* @param array $options An array of options
*
* @return array An array of stylesheet URLs
*/
function assetic_stylesheets($inputs = array(), $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = 'css/*.css';
}
return _assetic_urls($inputs, $filters, $options);
}
/**
* Returns an image URL.
*
* @param string $input An input
* @param array|string $filters Filter names
* @param array $options An array of options
*
* @return string An image URL
*/
function assetic_image($input, $filters = array(), array $options = array())
{
if (!isset($options['output'])) {
$options['output'] = 'images/*';
}
$urls = _assetic_urls($input, $filters, $options);
return current($urls);
}
/**
* Returns an array of asset urls.
*
* @param array|string $inputs Input strings
* @param array|string $filters Filter names
* @param array $options An array of options
*
* @return array An array of URLs
*/
function _assetic_urls($inputs = array(), $filters = array(), array $options = array())
{
global $_assetic;
if (!is_array($inputs)) {
$inputs = array_filter(array_map('trim', explode(',', $inputs)));
}
if (!is_array($filters)) {
$filters = array_filter(array_map('trim', explode(',', $filters)));
}
$coll = $_assetic->factory->createAsset($inputs, $filters, $options);
$debug = isset($options['debug']) ? $options['debug'] : $_assetic->factory->isDebug();
$combine = isset($options['combine']) ? $options['combine'] : !$debug;
$one = $coll->getTargetPath();
if ($combine) {
$many = array();
foreach ($coll as $leaf) {
$many[] = $leaf->getTargetPath();
}
} else {
$many = array($one);
}
return new TraversableString($one, $many);
}

View File

@ -1,910 +0,0 @@
<?php
/**
* A Mustache implementation in PHP.
*
* {@link http://defunkt.github.com/mustache}
*
* Mustache is a framework-agnostic logic-less templating language. It enforces separation of view
* logic from template files. In fact, it is not even possible to embed logic in the template.
*
* This is very, very rad.
*
* @author Justin Hileman {@link http://justinhileman.com}
*/
class Mustache {
const VERSION = '1.0.0';
const SPEC_VERSION = '1.1.2';
/**
* Should this Mustache throw exceptions when it finds unexpected tags?
*
* @see self::_throwsException()
*/
protected $_throwsExceptions = array(
MustacheException::UNKNOWN_VARIABLE => false,
MustacheException::UNCLOSED_SECTION => true,
MustacheException::UNEXPECTED_CLOSE_SECTION => true,
MustacheException::UNKNOWN_PARTIAL => false,
MustacheException::UNKNOWN_PRAGMA => true,
);
// Override charset passed to htmlentities() and htmlspecialchars(). Defaults to UTF-8.
protected $_charset = 'UTF-8';
/**
* Pragmas are macro-like directives that, when invoked, change the behavior or
* syntax of Mustache.
*
* They should be considered extremely experimental. Most likely their implementation
* will change in the future.
*/
/**
* The {{%UNESCAPED}} pragma swaps the meaning of the {{normal}} and {{{unescaped}}}
* Mustache tags. That is, once this pragma is activated the {{normal}} tag will not be
* escaped while the {{{unescaped}}} tag will be escaped.
*
* Pragmas apply only to the current template. Partials, even those included after the
* {{%UNESCAPED}} call, will need their own pragma declaration.
*
* This may be useful in non-HTML Mustache situations.
*/
const PRAGMA_UNESCAPED = 'UNESCAPED';
/**
* Constants used for section and tag RegEx
*/
const SECTION_TYPES = '\^#\/';
const TAG_TYPES = '#\^\/=!<>\\{&';
protected $_otag = '{{';
protected $_ctag = '}}';
protected $_tagRegEx;
protected $_template = '';
protected $_context = array();
protected $_partials = array();
protected $_pragmas = array();
protected $_pragmasImplemented = array(
self::PRAGMA_UNESCAPED
);
protected $_localPragmas = array();
/**
* Mustache class constructor.
*
* This method accepts a $template string and a $view object. Optionally, pass an associative
* array of partials as well.
*
* Passing an $options array allows overriding certain Mustache options during instantiation:
*
* $options = array(
* // `charset` -- must be supported by `htmlspecialentities()`. defaults to 'UTF-8'
* 'charset' => 'ISO-8859-1',
*
* // opening and closing delimiters, as an array or a space-separated string
* 'delimiters' => '<% %>',
*
* // an array of pragmas to enable/disable
* 'pragmas' => array(
* Mustache::PRAGMA_UNESCAPED => true
* ),
*
* // an array of thrown exceptions to enable/disable
* 'throws_exceptions' => array(
* MustacheException::UNKNOWN_VARIABLE => false,
* MustacheException::UNCLOSED_SECTION => true,
* MustacheException::UNEXPECTED_CLOSE_SECTION => true,
* MustacheException::UNKNOWN_PARTIAL => false,
* MustacheException::UNKNOWN_PRAGMA => true,
* ),
* );
*
* @access public
* @param string $template (default: null)
* @param mixed $view (default: null)
* @param array $partials (default: null)
* @param array $options (default: array())
* @return void
*/
public function __construct($template = null, $view = null, $partials = null, array $options = null) {
if ($template !== null) $this->_template = $template;
if ($partials !== null) $this->_partials = $partials;
if ($view !== null) $this->_context = array($view);
if ($options !== null) $this->_setOptions($options);
}
/**
* Helper function for setting options from constructor args.
*
* @access protected
* @param array $options
* @return void
*/
protected function _setOptions(array $options) {
if (isset($options['charset'])) {
$this->_charset = $options['charset'];
}
if (isset($options['delimiters'])) {
$delims = $options['delimiters'];
if (!is_array($delims)) {
$delims = array_map('trim', explode(' ', $delims, 2));
}
$this->_otag = $delims[0];
$this->_ctag = $delims[1];
}
if (isset($options['pragmas'])) {
foreach ($options['pragmas'] as $pragma_name => $pragma_value) {
if (!in_array($pragma_name, $this->_pragmasImplemented, true)) {
throw new MustacheException('Unknown pragma: ' . $pragma_name, MustacheException::UNKNOWN_PRAGMA);
}
}
$this->_pragmas = $options['pragmas'];
}
if (isset($options['throws_exceptions'])) {
foreach ($options['throws_exceptions'] as $exception => $value) {
$this->_throwsExceptions[$exception] = $value;
}
}
}
/**
* Mustache class clone method.
*
* A cloned Mustache instance should have pragmas, delimeters and root context
* reset to default values.
*
* @access public
* @return void
*/
public function __clone() {
$this->_otag = '{{';
$this->_ctag = '}}';
$this->_localPragmas = array();
if ($keys = array_keys($this->_context)) {
$last = array_pop($keys);
if ($this->_context[$last] instanceof Mustache) {
$this->_context[$last] =& $this;
}
}
}
/**
* Render the given template and view object.
*
* Defaults to the template and view passed to the class constructor unless a new one is provided.
* Optionally, pass an associative array of partials as well.
*
* @access public
* @param string $template (default: null)
* @param mixed $view (default: null)
* @param array $partials (default: null)
* @return string Rendered Mustache template.
*/
public function render($template = null, $view = null, $partials = null) {
if ($template === null) $template = $this->_template;
if ($partials !== null) $this->_partials = $partials;
$otag_orig = $this->_otag;
$ctag_orig = $this->_ctag;
if ($view) {
$this->_context = array($view);
} else if (empty($this->_context)) {
$this->_context = array($this);
}
$template = $this->_renderPragmas($template);
$template = $this->_renderTemplate($template);
$this->_otag = $otag_orig;
$this->_ctag = $ctag_orig;
return $template;
}
/**
* Wrap the render() function for string conversion.
*
* @access public
* @return string
*/
public function __toString() {
// PHP doesn't like exceptions in __toString.
// catch any exceptions and convert them to strings.
try {
$result = $this->render();
return $result;
} catch (Exception $e) {
return "Error rendering mustache: " . $e->getMessage();
}
}
/**
* Internal render function, used for recursive calls.
*
* @access protected
* @param string $template
* @return string Rendered Mustache template.
*/
protected function _renderTemplate($template) {
if ($section = $this->_findSection($template)) {
list($before, $type, $tag_name, $content, $after) = $section;
$rendered_before = $this->_renderTags($before);
$rendered_content = '';
$val = $this->_getVariable($tag_name);
switch($type) {
// inverted section
case '^':
if (empty($val)) {
$rendered_content = $this->_renderTemplate($content);
}
break;
// regular section
case '#':
// higher order sections
if ($this->_varIsCallable($val)) {
$rendered_content = $this->_renderTemplate(call_user_func($val, $content));
} else if ($this->_varIsIterable($val)) {
foreach ($val as $local_context) {
$this->_pushContext($local_context);
$rendered_content .= $this->_renderTemplate($content);
$this->_popContext();
}
} else if ($val) {
if (is_array($val) || is_object($val)) {
$this->_pushContext($val);
$rendered_content = $this->_renderTemplate($content);
$this->_popContext();
} else {
$rendered_content = $this->_renderTemplate($content);
}
}
break;
}
return $rendered_before . $rendered_content . $this->_renderTemplate($after);
}
return $this->_renderTags($template);
}
/**
* Prepare a section RegEx string for the given opening/closing tags.
*
* @access protected
* @param string $otag
* @param string $ctag
* @return string
*/
protected function _prepareSectionRegEx($otag, $ctag) {
return sprintf(
'/(?:(?<=\\n)[ \\t]*)?%s(?:(?P<type>[%s])(?P<tag_name>.+?)|=(?P<delims>.*?)=)%s\\n?/s',
preg_quote($otag, '/'),
self::SECTION_TYPES,
preg_quote($ctag, '/')
);
}
/**
* Extract the first section from $template.
*
* @access protected
* @param string $template
* @return array $before, $type, $tag_name, $content and $after
*/
protected function _findSection($template) {
$regEx = $this->_prepareSectionRegEx($this->_otag, $this->_ctag);
$section_start = null;
$section_type = null;
$content_start = null;
$search_offset = 0;
$section_stack = array();
$matches = array();
while (preg_match($regEx, $template, $matches, PREG_OFFSET_CAPTURE, $search_offset)) {
if (isset($matches['delims'][0])) {
list($otag, $ctag) = explode(' ', $matches['delims'][0]);
$regEx = $this->_prepareSectionRegEx($otag, $ctag);
$search_offset = $matches[0][1] + strlen($matches[0][0]);
continue;
}
$match = $matches[0][0];
$offset = $matches[0][1];
$type = $matches['type'][0];
$tag_name = trim($matches['tag_name'][0]);
$search_offset = $offset + strlen($match);
switch ($type) {
case '^':
case '#':
if (empty($section_stack)) {
$section_start = $offset;
$section_type = $type;
$content_start = $search_offset;
}
array_push($section_stack, $tag_name);
break;
case '/':
if (empty($section_stack) || ($tag_name !== array_pop($section_stack))) {
if ($this->_throwsException(MustacheException::UNEXPECTED_CLOSE_SECTION)) {
throw new MustacheException('Unexpected close section: ' . $tag_name, MustacheException::UNEXPECTED_CLOSE_SECTION);
}
}
if (empty($section_stack)) {
// $before, $type, $tag_name, $content, $after
return array(
substr($template, 0, $section_start),
$section_type,
$tag_name,
substr($template, $content_start, $offset - $content_start),
substr($template, $search_offset),
);
}
break;
}
}
if (!empty($section_stack)) {
if ($this->_throwsException(MustacheException::UNCLOSED_SECTION)) {
throw new MustacheException('Unclosed section: ' . $section_stack[0], MustacheException::UNCLOSED_SECTION);
}
}
}
/**
* Prepare a pragma RegEx for the given opening/closing tags.
*
* @access protected
* @param string $otag
* @param string $ctag
* @return string
*/
protected function _preparePragmaRegEx($otag, $ctag) {
return sprintf(
'/%s%%\\s*(?P<pragma_name>[\\w_-]+)(?P<options_string>(?: [\\w]+=[\\w]+)*)\\s*%s\\n?/s',
preg_quote($otag, '/'),
preg_quote($ctag, '/')
);
}
/**
* Initialize pragmas and remove all pragma tags.
*
* @access protected
* @param string $template
* @return string
*/
protected function _renderPragmas($template) {
$this->_localPragmas = $this->_pragmas;
// no pragmas
if (strpos($template, $this->_otag . '%') === false) {
return $template;
}
$regEx = $this->_preparePragmaRegEx($this->_otag, $this->_ctag);
return preg_replace_callback($regEx, array($this, '_renderPragma'), $template);
}
/**
* A preg_replace helper to remove {{%PRAGMA}} tags and enable requested pragma.
*
* @access protected
* @param mixed $matches
* @return void
* @throws MustacheException unknown pragma
*/
protected function _renderPragma($matches) {
$pragma = $matches[0];
$pragma_name = $matches['pragma_name'];
$options_string = $matches['options_string'];
if (!in_array($pragma_name, $this->_pragmasImplemented)) {
if ($this->_throwsException(MustacheException::UNKNOWN_PRAGMA)) {
throw new MustacheException('Unknown pragma: ' . $pragma_name, MustacheException::UNKNOWN_PRAGMA);
} else {
return '';
}
}
$options = array();
foreach (explode(' ', trim($options_string)) as $o) {
if ($p = trim($o)) {
$p = explode('=', $p);
$options[$p[0]] = $p[1];
}
}
if (empty($options)) {
$this->_localPragmas[$pragma_name] = true;
} else {
$this->_localPragmas[$pragma_name] = $options;
}
return '';
}
/**
* Check whether this Mustache has a specific pragma.
*
* @access protected
* @param string $pragma_name
* @return bool
*/
protected function _hasPragma($pragma_name) {
if (array_key_exists($pragma_name, $this->_localPragmas) && $this->_localPragmas[$pragma_name]) {
return true;
} else {
return false;
}
}
/**
* Return pragma options, if any.
*
* @access protected
* @param string $pragma_name
* @return mixed
* @throws MustacheException Unknown pragma
*/
protected function _getPragmaOptions($pragma_name) {
if (!$this->_hasPragma($pragma_name)) {
if ($this->_throwsException(MustacheException::UNKNOWN_PRAGMA)) {
throw new MustacheException('Unknown pragma: ' . $pragma_name, MustacheException::UNKNOWN_PRAGMA);
}
}
return (is_array($this->_localPragmas[$pragma_name])) ? $this->_localPragmas[$pragma_name] : array();
}
/**
* Check whether this Mustache instance throws a given exception.
*
* Expects exceptions to be MustacheException error codes (i.e. class constants).
*
* @access protected
* @param mixed $exception
* @return void
*/
protected function _throwsException($exception) {
return (isset($this->_throwsExceptions[$exception]) && $this->_throwsExceptions[$exception]);
}
/**
* Prepare a tag RegEx for the given opening/closing tags.
*
* @access protected
* @param string $otag
* @param string $ctag
* @return string
*/
protected function _prepareTagRegEx($otag, $ctag, $first = false) {
return sprintf(
'/(?P<leading>(?:%s\\r?\\n)[ \\t]*)?%s(?P<type>[%s]?)(?P<tag_name>.+?)(?:\\2|})?%s(?P<trailing>\\s*(?:\\r?\\n|\\Z))?/s',
($first ? '\\A|' : ''),
preg_quote($otag, '/'),
self::TAG_TYPES,
preg_quote($ctag, '/')
);
}
/**
* Loop through and render individual Mustache tags.
*
* @access protected
* @param string $template
* @return void
*/
protected function _renderTags($template) {
if (strpos($template, $this->_otag) === false) {
return $template;
}
$first = true;
$this->_tagRegEx = $this->_prepareTagRegEx($this->_otag, $this->_ctag, true);
$html = '';
$matches = array();
while (preg_match($this->_tagRegEx, $template, $matches, PREG_OFFSET_CAPTURE)) {
$tag = $matches[0][0];
$offset = $matches[0][1];
$modifier = $matches['type'][0];
$tag_name = trim($matches['tag_name'][0]);
if (isset($matches['leading']) && $matches['leading'][1] > -1) {
$leading = $matches['leading'][0];
} else {
$leading = null;
}
if (isset($matches['trailing']) && $matches['trailing'][1] > -1) {
$trailing = $matches['trailing'][0];
} else {
$trailing = null;
}
$html .= substr($template, 0, $offset);
$next_offset = $offset + strlen($tag);
if ((substr($html, -1) == "\n") && (substr($template, $next_offset, 1) == "\n")) {
$next_offset++;
}
$template = substr($template, $next_offset);
$html .= $this->_renderTag($modifier, $tag_name, $leading, $trailing);
if ($first == true) {
$first = false;
$this->_tagRegEx = $this->_prepareTagRegEx($this->_otag, $this->_ctag);
}
}
return $html . $template;
}
/**
* Render the named tag, given the specified modifier.
*
* Accepted modifiers are `=` (change delimiter), `!` (comment), `>` (partial)
* `{` or `&` (don't escape output), or none (render escaped output).
*
* @access protected
* @param string $modifier
* @param string $tag_name
* @param string $leading Whitespace
* @param string $trailing Whitespace
* @throws MustacheException Unmatched section tag encountered.
* @return string
*/
protected function _renderTag($modifier, $tag_name, $leading, $trailing) {
switch ($modifier) {
case '=':
return $this->_changeDelimiter($tag_name, $leading, $trailing);
break;
case '!':
return $this->_renderComment($tag_name, $leading, $trailing);
break;
case '>':
case '<':
return $this->_renderPartial($tag_name, $leading, $trailing);
break;
case '{':
// strip the trailing } ...
if ($tag_name[(strlen($tag_name) - 1)] == '}') {
$tag_name = substr($tag_name, 0, -1);
}
case '&':
if ($this->_hasPragma(self::PRAGMA_UNESCAPED)) {
return $this->_renderEscaped($tag_name, $leading, $trailing);
} else {
return $this->_renderUnescaped($tag_name, $leading, $trailing);
}
break;
case '#':
case '^':
case '/':
// remove any leftover section tags
return $leading . $trailing;
break;
default:
if ($this->_hasPragma(self::PRAGMA_UNESCAPED)) {
return $this->_renderUnescaped($modifier . $tag_name, $leading, $trailing);
} else {
return $this->_renderEscaped($modifier . $tag_name, $leading, $trailing);
}
break;
}
}
/**
* Returns true if any of its args contains the "\r" character.
*
* @access protected
* @param string $str
* @return boolean
*/
protected function _stringHasR($str) {
foreach (func_get_args() as $arg) {
if (strpos($arg, "\r") !== false) {
return true;
}
}
return false;
}
/**
* Escape and return the requested tag.
*
* @access protected
* @param string $tag_name
* @param string $leading Whitespace
* @param string $trailing Whitespace
* @return string
*/
protected function _renderEscaped($tag_name, $leading, $trailing) {
$rendered = htmlentities($this->_renderUnescaped($tag_name, '', ''), ENT_COMPAT, $this->_charset);
return $leading . $rendered . $trailing;
}
/**
* Render a comment (i.e. return an empty string).
*
* @access protected
* @param string $tag_name
* @param string $leading Whitespace
* @param string $trailing Whitespace
* @return string
*/
protected function _renderComment($tag_name, $leading, $trailing) {
if ($leading !== null && $trailing !== null) {
if (strpos($leading, "\n") === false) {
return '';
}
return $this->_stringHasR($leading, $trailing) ? "\r\n" : "\n";
}
return $leading . $trailing;
}
/**
* Return the requested tag unescaped.
*
* @access protected
* @param string $tag_name
* @param string $leading Whitespace
* @param string $trailing Whitespace
* @return string
*/
protected function _renderUnescaped($tag_name, $leading, $trailing) {
$val = $this->_getVariable($tag_name);
if ($this->_varIsCallable($val)) {
$val = $this->_renderTemplate(call_user_func($val));
}
return $leading . $val . $trailing;
}
/**
* Render the requested partial.
*
* @access protected
* @param string $tag_name
* @param string $leading Whitespace
* @param string $trailing Whitespace
* @return string
*/
protected function _renderPartial($tag_name, $leading, $trailing) {
$partial = $this->_getPartial($tag_name);
if ($leading !== null && $trailing !== null) {
$whitespace = trim($leading, "\r\n");
$partial = preg_replace('/(\\r?\\n)(?!$)/s', "\\1" . $whitespace, $partial);
}
$view = clone($this);
if ($leading !== null && $trailing !== null) {
return $leading . $view->render($partial);
} else {
return $leading . $view->render($partial) . $trailing;
}
}
/**
* Change the Mustache tag delimiter. This method also replaces this object's current
* tag RegEx with one using the new delimiters.
*
* @access protected
* @param string $tag_name
* @param string $leading Whitespace
* @param string $trailing Whitespace
* @return string
*/
protected function _changeDelimiter($tag_name, $leading, $trailing) {
list($otag, $ctag) = explode(' ', $tag_name);
$this->_otag = $otag;
$this->_ctag = $ctag;
$this->_tagRegEx = $this->_prepareTagRegEx($this->_otag, $this->_ctag);
if ($leading !== null && $trailing !== null) {
if (strpos($leading, "\n") === false) {
return '';
}
return $this->_stringHasR($leading, $trailing) ? "\r\n" : "\n";
}
return $leading . $trailing;
}
/**
* Push a local context onto the stack.
*
* @access protected
* @param array &$local_context
* @return void
*/
protected function _pushContext(&$local_context) {
$new = array();
$new[] =& $local_context;
foreach (array_keys($this->_context) as $key) {
$new[] =& $this->_context[$key];
}
$this->_context = $new;
}
/**
* Remove the latest context from the stack.
*
* @access protected
* @return void
*/
protected function _popContext() {
$new = array();
$keys = array_keys($this->_context);
array_shift($keys);
foreach ($keys as $key) {
$new[] =& $this->_context[$key];
}
$this->_context = $new;
}
/**
* Get a variable from the context array.
*
* If the view is an array, returns the value with array key $tag_name.
* If the view is an object, this will check for a public member variable
* named $tag_name. If none is available, this method will execute and return
* any class method named $tag_name. Failing all of the above, this method will
* return an empty string.
*
* @access protected
* @param string $tag_name
* @throws MustacheException Unknown variable name.
* @return string
*/
protected function _getVariable($tag_name) {
if ($tag_name === '.') {
return $this->_context[0];
} else if (strpos($tag_name, '.') !== false) {
$chunks = explode('.', $tag_name);
$first = array_shift($chunks);
$ret = $this->_findVariableInContext($first, $this->_context);
foreach ($chunks as $next) {
// Slice off a chunk of context for dot notation traversal.
$c = array($ret);
$ret = $this->_findVariableInContext($next, $c);
}
return $ret;
} else {
return $this->_findVariableInContext($tag_name, $this->_context);
}
}
/**
* Get a variable from the context array. Internal helper used by getVariable() to abstract
* variable traversal for dot notation.
*
* @access protected
* @param string $tag_name
* @param array $context
* @throws MustacheException Unknown variable name.
* @return string
*/
protected function _findVariableInContext($tag_name, $context) {
foreach ($context as $view) {
if (is_object($view)) {
if (method_exists($view, $tag_name)) {
return $view->$tag_name();
} else if (isset($view->$tag_name)) {
return $view->$tag_name;
}
} else if (is_array($view) && array_key_exists($tag_name, $view)) {
return $view[$tag_name];
}
}
if ($this->_throwsException(MustacheException::UNKNOWN_VARIABLE)) {
throw new MustacheException("Unknown variable: " . $tag_name, MustacheException::UNKNOWN_VARIABLE);
} else {
return '';
}
}
/**
* Retrieve the partial corresponding to the requested tag name.
*
* Silently fails (i.e. returns '') when the requested partial is not found.
*
* @access protected
* @param string $tag_name
* @throws MustacheException Unknown partial name.
* @return string
*/
protected function _getPartial($tag_name) {
if ((is_array($this->_partials) || $this->_partials instanceof ArrayAccess) && isset($this->_partials[$tag_name])) {
return $this->_partials[$tag_name];
}
if ($this->_throwsException(MustacheException::UNKNOWN_PARTIAL)) {
throw new MustacheException('Unknown partial: ' . $tag_name, MustacheException::UNKNOWN_PARTIAL);
} else {
return '';
}
}
/**
* Check whether the given $var should be iterated (i.e. in a section context).
*
* @access protected
* @param mixed $var
* @return bool
*/
protected function _varIsIterable($var) {
return $var instanceof Traversable || (is_array($var) && !array_diff_key($var, array_keys(array_keys($var))));
}
/**
* Higher order sections helper: tests whether the section $var is a valid callback.
*
* In Mustache.php, a variable is considered 'callable' if the variable is:
*
* 1. an anonymous function.
* 2. an object and the name of a public function, i.e. `array($SomeObject, 'methodName')`
* 3. a class name and the name of a public static function, i.e. `array('SomeClass', 'methodName')`
*
* @access protected
* @param mixed $var
* @return bool
*/
protected function _varIsCallable($var) {
return !is_string($var) && is_callable($var);
}
}
/**
* MustacheException class.
*
* @extends Exception
*/
class MustacheException extends Exception {
// An UNKNOWN_VARIABLE exception is thrown when a {{variable}} is not found
// in the current context.
const UNKNOWN_VARIABLE = 0;
// An UNCLOSED_SECTION exception is thrown when a {{#section}} is not closed.
const UNCLOSED_SECTION = 1;
// An UNEXPECTED_CLOSE_SECTION exception is thrown when {{/section}} appears
// without a corresponding {{#section}} or {{^section}}.
const UNEXPECTED_CLOSE_SECTION = 2;
// An UNKNOWN_PARTIAL exception is thrown whenever a {{>partial}} tag appears
// with no associated partial.
const UNKNOWN_PARTIAL = 3;
// An UNKNOWN_PRAGMA exception is thrown whenever a {{%PRAGMA}} tag appears
// which can't be handled by this Mustache instance.
const UNKNOWN_PRAGMA = 4;
}

17
vendor/autoload.php vendored
View File

@ -1,17 +0,0 @@
<?php
spl_autoload_register(function ($className) {
$namespaces = explode('\\', $className);
if (count($namespaces) > 1) {
$classPath
= APPLICATION_BASE_PATH
. '/vendor/'
. implode('/', $namespaces)
. '.php';
if (file_exists($classPath)) {
require_once($classPath);
}
}
});
/* End of file autoload.php */

660
vendor/lessphp/LICENSE vendored
View File

@ -1,660 +0,0 @@
For ease of distribution, lessphp 0.2.0 is under a dual license.
You are free to pick which one suits your needs.
MIT LICENSE
Copyright (c) 2010 Leaf Corcoran, http://leafo.net/lessphp
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.
GPL VERSION 3
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

View File

@ -1,64 +0,0 @@
# lessphp v0.3.3
### <http://leafo.net/lessphp>
`lessphp` is a compiler for LESS written in PHP. The documentation is great,
so check it out: <http://leafo.net/lessphp/docs/>.
Here's a quick tutorial:
### How to use in your PHP project
Copy `lessc.inc.php` to your include directory and include it into your project.
There are a few ways to interface with the compiler. The easiest is to have it
compile a LESS file when the page is requested. The static function
`lessc::ccompile`, checked compile, will compile the input LESS file only when it
is newer than the output file.
try {
lessc::ccompile('input.less', 'output.css');
} catch (exception $ex) {
exit($ex->getMessage());
}
`lessc::ccompile` is not aware of imported files that change. Read [about
`lessc::cexecute`](http://leafo.net/lessphp/docs/#compiling_automatically).
Note that all failures with lessc are reported through exceptions.
If you need more control you can make your own instance of lessc.
$input = 'mystyle.less';
$lc = new lessc($input);
try {
file_put_contents('mystyle.css', $lc->parse());
} catch (exception $ex) { ... }
In addition to loading from file, you can also parse from a string like so:
$lc = new lessc();
$lesscode = 'body { ... }';
$out = $lc->parse($lesscode);
### How to use from the command line
An additional script has been included to use the compiler from the command
line. In the simplest invocation, you specify an input file and the compiled
css is written to standard out:
$ plessc input.less > output.css
Using the -r flag, you can specify LESS code directly as an argument or, if
the argument is left off, from standard in:
$ plessc -r "my less code here"
Finally, by using the -w flag you can watch a specified input file and have it
compile as needed to the output file
$ plessc -w input-file output-file
Errors from watch mode are written to standard out.

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +0,0 @@
#!/usr/bin/php -q
<?php
if (php_sapi_name() != "cli") {
err($fa.$argv[0]." must be run in the command line.");
exit(1);
}
$exe = array_shift($argv); // remove filename
if (!$fname = array_shift($argv)) {
exit("Usage: ".$exe." input-file\n");
}
require "lessify.inc.php";
try {
$parser = new lessify($fname);
echo $parser->parse();
} catch (exception $e) {
exit("Fatal error: ".$e->getMessage()."\n");
}

View File

@ -1,447 +0,0 @@
<?php
/**
* lessify
* Convert a css file into a less file
* http://leafo.net/lessphp
* Copyright 2010, leaf corcoran <leafot@gmail.com>
*
* WARNING: THIS DOES NOT WORK ANYMORE. NEEDS TO BE UPDATED FOR
* LATEST VERSION OF LESSPHP.
*
*/
require "lessc.inc.php";
//
// check if the merge during mixin is overwriting values. should or should it not?
//
//
// 1. split apart class tags
//
class easyparse {
var $buffer;
var $count;
function __construct($str) {
$this->count = 0;
$this->buffer = trim($str);
}
function seek($where = null) {
if ($where === null) return $this->count;
else $this->count = $where;
return true;
}
function preg_quote($what) {
return preg_quote($what, '/');
}
function match($regex, &$out, $eatWhitespace = true) {
$r = '/'.$regex.($eatWhitespace ? '\s*' : '').'/Ais';
if (preg_match($r, $this->buffer, $out, null, $this->count)) {
$this->count += strlen($out[0]);
return true;
}
return false;
}
function literal($what, $eatWhitespace = true) {
// this is here mainly prevent notice from { } string accessor
if ($this->count >= strlen($this->buffer)) return false;
// shortcut on single letter
if (!$eatWhitespace and strlen($what) == 1) {
if ($this->buffer{$this->count} == $what) {
$this->count++;
return true;
}
else return false;
}
return $this->match($this->preg_quote($what), $m, $eatWhitespace);
}
}
class tagparse extends easyparse {
static private $combinators = null;
static private $match_opts = null;
function parse() {
if (empty(self::$combinators)) {
self::$combinators = '('.implode('|', array_map(array($this, 'preg_quote'),
array('+', '>', '~'))).')';
self::$match_opts = '('.implode('|', array_map(array($this, 'preg_quote'),
array('=', '~=', '|=', '$=', '*='))).')';
}
// crush whitespace
$this->buffer = preg_replace('/\s+/', ' ', $this->buffer).' ';
$tags = array();
while ($this->tag($t)) $tags[] = $t;
return $tags;
}
static function compileString($string) {
list(, $delim, $str) = $string;
$str = str_replace($delim, "\\".$delim, $str);
$str = str_replace("\n", "\\\n", $str);
return $delim.$str.$delim;
}
static function compilePaths($paths) {
return implode(', ', array_map(array('self', 'compilePath'), $paths));
}
// array of tags
static function compilePath($path) {
return implode(' ', array_map(array('self', 'compileTag'), $path));
}
static function compileTag($tag) {
ob_start();
if (isset($tag['comb'])) echo $tag['comb']." ";
if (isset($tag['front'])) echo $tag['front'];
if (isset($tag['attr'])) {
echo '['.$tag['attr'];
if (isset($tag['op'])) {
echo $tag['op'].$tag['op_value'];
}
echo ']';
}
return ob_get_clean();
}
function string(&$out) {
$s = $this->seek();
if ($this->literal('"')) {
$delim = '"';
} elseif ($this->literal("'")) {
$delim = "'";
} else {
return false;
}
while (true) {
// step through letters looking for either end or escape
$buff = "";
$escapeNext = false;
$finished = false;
for ($i = $this->count; $i < strlen($this->buffer); $i++) {
$char = $this->buffer[$i];
switch ($char) {
case $delim:
if ($escapeNext) {
$buff .= $char;
$escapeNext = false;
break;
}
$finished = true;
break 2;
case "\\":
if ($escapeNext) {
$buff .= $char;
$escapeNext = false;
} else {
$escapeNext = true;
}
break;
case "\n":
if (!$escapeNext) {
break 3;
}
$buff .= $char;
$escapeNext = false;
break;
default:
if ($escapeNext) {
$buff .= "\\";
$escapeNext = false;
}
$buff .= $char;
}
}
if (!$finished) break;
$out = array('string', $delim, $buff);
$this->seek($i+1);
return true;
}
$this->seek($s);
return false;
}
function tag(&$out) {
$s = $this->seek();
$tag = array();
if ($this->combinator($op)) $tag['comb'] = $op;
if (!$this->match('(.*?)( |$|\[|'.self::$combinators.')', $match)) {
$this->seek($s);
return false;
}
if (!empty($match[3])) {
// give back combinator
$this->count-=strlen($match[3]);
}
if (!empty($match[1])) $tag['front'] = $match[1];
if ($match[2] == '[') {
if ($this->ident($i)) {
$tag['attr'] = $i;
if ($this->match(self::$match_opts, $m) && $this->value($v)) {
$tag['op'] = $m[1];
$tag['op_value'] = $v;
}
if ($this->literal(']')) {
$out = $tag;
return true;
}
}
} elseif (isset($tag['front'])) {
$out = $tag;
return true;
}
$this->seek($s);
return false;
}
function ident(&$out) {
// [-]?{nmstart}{nmchar}*
// nmstart: [_a-z]|{nonascii}|{escape}
// nmchar: [_a-z0-9-]|{nonascii}|{escape}
if ($this->match('(-?[_a-z][_\w]*)', $m)) {
$out = $m[1];
return true;
}
return false;
}
function value(&$out) {
if ($this->string($str)) {
$out = $this->compileString($str);
return true;
} elseif ($this->ident($id)) {
$out = $id;
return true;
}
return false;
}
function combinator(&$op) {
if ($this->match(self::$combinators, $m)) {
$op = $m[1];
return true;
}
return false;
}
}
class nodecounter {
var $count = 0;
var $children = array();
var $name;
var $child_blocks;
var $the_block;
function __construct($name) {
$this->name = $name;
}
function dump($stack = null) {
if (is_null($stack)) $stack = array();
$stack[] = $this->getName();
echo implode(' -> ', $stack)." ($this->count)\n";
foreach ($this->children as $child) {
$child->dump($stack);
}
}
static function compileProperties($c, $block) {
foreach($block as $name => $value) {
if ($c->isProperty($name, $value)) {
echo $c->compileProperty($name, $value)."\n";
}
}
}
function compile($c, $path = null) {
if (is_null($path)) $path = array();
$path[] = $this->name;
$isVisible = !is_null($this->the_block) || !is_null($this->child_blocks);
if ($isVisible) {
echo $c->indent(implode(' ', $path).' {');
$c->indentLevel++;
$path = array();
if ($this->the_block) {
$this->compileProperties($c, $this->the_block);
}
if ($this->child_blocks) {
foreach ($this->child_blocks as $block) {
echo $c->indent(tagparse::compilePaths($block['__tags']).' {');
$c->indentLevel++;
$this->compileProperties($c, $block);
$c->indentLevel--;
echo $c->indent('}');
}
}
}
// compile child nodes
foreach($this->children as $node) {
$node->compile($c, $path);
}
if ($isVisible) {
$c->indentLevel--;
echo $c->indent('}');
}
}
function getName() {
if (is_null($this->name)) return "[root]";
else return $this->name;
}
function getNode($name) {
if (!isset($this->children[$name])) {
$this->children[$name] = new nodecounter($name);
}
return $this->children[$name];
}
function findNode($path) {
$current = $this;
for ($i = 0; $i < count($path); $i++) {
$t = tagparse::compileTag($path[$i]);
$current = $current->getNode($t);
}
return $current;
}
function addBlock($path, $block) {
$node = $this->findNode($path);
if (!is_null($node->the_block)) throw new exception("can this happen?");
unset($block['__tags']);
$node->the_block = $block;
}
function addToNode($path, $block) {
$node = $this->findNode($path);
$node->child_blocks[] = $block;
}
}
/**
* create a less file from a css file by combining blocks where appropriate
*/
class lessify extends lessc {
public function dump() {
print_r($this->env);
}
public function parse($str = null) {
$this->prepareParser($str ? $str : $this->buffer);
while (false !== $this->parseChunk());
$root = new nodecounter(null);
// attempt to preserve some of the block order
$order = array();
$visitedTags = array();
foreach (end($this->env) as $name => $block) {
if (!$this->isBlock($name, $block)) continue;
if (isset($visitedTags[$name])) continue;
foreach ($block['__tags'] as $t) {
$visitedTags[$t] = true;
}
// skip those with more than 1
if (count($block['__tags']) == 1) {
$p = new tagparse(end($block['__tags']));
$path = $p->parse();
$root->addBlock($path, $block);
$order[] = array('compressed', $path, $block);
continue;
} else {
$common = null;
$paths = array();
foreach ($block['__tags'] as $rawtag) {
$p = new tagparse($rawtag);
$paths[] = $path = $p->parse();
if (is_null($common)) $common = $path;
else {
$new_common = array();
foreach ($path as $tag) {
$head = array_shift($common);
if ($tag == $head) {
$new_common[] = $head;
} else break;
}
$common = $new_common;
if (empty($common)) {
// nothing in common
break;
}
}
}
if (!empty($common)) {
$new_paths = array();
foreach ($paths as $p) $new_paths[] = array_slice($p, count($common));
$block['__tags'] = $new_paths;
$root->addToNode($common, $block);
$order[] = array('compressed', $common, $block);
continue;
}
}
$order[] = array('none', $block['__tags'], $block);
}
$compressed = $root->children;
foreach ($order as $item) {
list($type, $tags, $block) = $item;
if ($type == 'compressed') {
$top = tagparse::compileTag(reset($tags));
if (isset($compressed[$top])) {
$compressed[$top]->compile($this);
unset($compressed[$top]);
}
} else {
echo $this->indent(implode(', ', $tags).' {');
$this->indentLevel++;
nodecounter::compileProperties($this, $block);
$this->indentLevel--;
echo $this->indent('}');
}
}
}
}

View File

@ -1,22 +0,0 @@
#!/bin/sh
# creates tar.gz for current version
VERSION=`./plessc -v | sed -n 's/^v\(.*\)$/\1/p'`
OUT_DIR="tmp/lessphp"
TMP=`dirname $OUT_DIR`
mkdir -p $OUT_DIR
tar -c `git ls-files` | tar -C $OUT_DIR -x
rm $OUT_DIR/.gitignore
rm $OUT_DIR/package.sh
rm $OUT_DIR/lessify
rm $OUT_DIR/lessify.inc.php
OUT_NAME="lessphp-$VERSION.tar.gz"
tar -czf $OUT_NAME -C $TMP lessphp/
echo "Wrote $OUT_NAME"
rm -r $TMP

193
vendor/lessphp/plessc vendored
View File

@ -1,193 +0,0 @@
#!/usr/bin/php -q
<?php
//
// command line utility to compile less to stdout
// leaf corcoran <leafo.net>
error_reporting(E_ALL);
$path = realpath(dirname(__FILE__)).'/';
require $path."lessc.inc.php";
$VERSION = lessc::$VERSION;
$fa = "Fatal Error: ";
function err($msg) {
fwrite(STDERR, $msg."\n");
}
if (php_sapi_name() != "cli") {
err($fa.$argv[0]." must be run in the command line.");
exit(1);
}
$exe = array_shift($argv); // remove filename
function process($data, $import = null) {
global $fa;
$l = new lessc();
if ($import) $l->importDir = $import;
try {
echo $l->parse($data);
exit(0);
} catch (exception $ex) {
err($fa."\n".str_repeat('=', 20)."\n".
$ex->getMessage());
exit(1);
}
}
// process args
$opts = array();
foreach ($argv as $loc => $a) {
if (preg_match("/^-([a-zA-Z]+)$/", $a, $m)) {
$m = $m[1];
for ($i = 0; $i < strlen($m); $i++)
$opts[$m{$i}] = $loc;
unset($argv[$loc]);
}
}
function has($o, &$loc = null) {
global $opts;
if (!isset($opts[$o])) return false;
$loc = $opts[$o];
return true;
}
function hasValue($o, &$value = null) {
global $argv;
if (!has($o,$loc)) return false;
if (!isset($argv[$loc+1])) return false;
$value = $argv[$loc+1];
return true;
}
if (has("v")) {
exit($VERSION."\n");
}
if (has("r", $loc)) {
if (!hasValue("r", $data)) {
while (!feof(STDIN)) {
$data .= fread(STDIN, 8192);
}
}
return process($data);
}
if (has("w")) {
// need two files
if (!is_file($in = array_shift($argv)) ||
null == $out = array_shift($argv))
{
err($fa.$exe." -w infile outfile");
exit(1);
}
echo "Watching ".$in.
(has("n") ? ' with notifications' : '').
", press Ctrl + c to exit.\n";
$cache = $in;
$last_action = 0;
while (1) {
clearstatcache();
// check if anything has changed since last fail
$updated = false;
if (is_array($cache)) {
foreach ($cache['files'] as $fname=>$_) {
if (filemtime($fname) > $last_action) {
$updated = true;
break;
}
}
} else $updated = true;
// try to compile it
if ($updated) {
$last_action = time();
try {
$cache = lessc::cexecute($cache);
echo "Writing updated file: ".$out."\n";
if (!file_put_contents($out, $cache['compiled'])) {
err($fa."Could not write to file ".$out);
exit(1);
}
} catch (exception $ex) {
echo "\nFatal Error:\n".str_repeat('=', 20)."\n".$ex->getMessage()."\n\n";
if (has("n")) {
`notify-send -u critical "compile failed" "{$ex->getMessage()}"`;
}
}
}
sleep(1);
}
exit(0);
}
if (!$fname = array_shift($argv)) {
echo "Usage: ".$exe." input-file [output-file]\n";
exit(1);
}
function dumpValue($node, $depth = 0) {
if (is_object($node)) {
$indent = str_repeat(" ", $depth);
$out = array();
foreach ($node->props as $prop) {
$out[] = $indent . dumpValue($prop, $depth + 1);
}
$out = implode("\n", $out);
if (!empty($node->tags)) {
$out = "+ ".implode(", ", $node->tags)."\n".$out;
}
return $out;
} elseif (is_array($node)) {
if (empty($node)) return "[]";
$type = $node[0];
if ($type == "block")
return dumpValue($node[1], $depth);
$out = array();
foreach ($node as $value) {
$out[] = dumpValue($value, $depth);
}
return "{ ".implode(", ", $out)." }";
} else {
if (is_string($node) && preg_match("/[\s,]/", $node)) {
return '"'.$node.'"';
}
return $node; // normal value
}
}
try {
$l = new lessc($fname);
if (has("T") || has("X")) {
$t = $l->parseTree();
if (has("X"))
$out = print_r($t, 1);
else
$out = dumpValue($t)."\n";
} else {
$out = $l->parse();
}
if (!$fout = array_shift($argv)) {
echo $out;
} else {
file_put_contents($fout, $out);
}
} catch (exception $ex) {
err($fa.$ex->getMessage());
exit(1);
}
?>

View File

@ -1,36 +0,0 @@
PHP Markdown & Extra
Copyright (c) 2004-2009 Michel Fortin
<http://michelf.com/>
All rights reserved.
Based on Markdown
Copyright (c) 2003-2006 John Gruber
<http://daringfireball.net/>
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 "Markdown" 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.

View File

@ -1,802 +0,0 @@
PHP Markdown Extra
==================
Version 1.2.5 - Sun 8 Jan 2012
by Michel Fortin
<http://michelf.com/>
based on Markdown by John Gruber
<http://daringfireball.net/>
Introduction
------------
This is a special version of PHP Markdown with extra features. See
<http://michelf.com/projects/php-markdown/extra/> for details.
Markdown is a text-to-HTML conversion tool for web writers. Markdown
allows you to write using an easy-to-read, easy-to-write plain text
format, then convert it to structurally valid XHTML (or HTML).
"Markdown" is two things: a plain text markup syntax, and a software
tool, written in Perl, that converts the plain text markup to HTML.
PHP Markdown is a port to PHP of the original Markdown program by
John Gruber.
PHP Markdown can work as a plug-in for WordPress and bBlog, as a
modifier for the Smarty templating engine, or as a remplacement for
textile formatting in any software that support textile.
Full documentation of Markdown's syntax is available on John's
Markdown page: <http://daringfireball.net/projects/markdown/>
Installation and Requirement
----------------------------
PHP Markdown requires PHP version 4.0.5 or later.
### WordPress ###
PHP Markdown works with [WordPress][wp], version 1.2 or later.
[wp]: http://wordpress.org/
1. To use PHP Markdown with WordPress, place the "makrdown.php" file
in the "plugins" folder. This folder is located inside
"wp-content" at the root of your site:
(site home)/wp-content/plugins/
2. Activate the plugin with the administrative interface of
WordPress. In the "Plugins" section you will now find Markdown.
To activate the plugin, click on the "Activate" button on the
same line than Markdown. Your entries will now be formatted by
PHP Markdown.
3. To post Markdown content, you'll first have to disable the
"visual" editor in the User section of WordPress.
You can configure PHP Markdown to not apply to the comments on your
WordPress weblog. See the "Configuration" section below.
It is not possible at this time to apply a different set of
filters to different entries. All your entries will be formated by
PHP Markdown. This is a limitation of WordPress. If your old entries
are written in HTML (as opposed to another formatting syntax, like
Textile), they'll probably stay fine after installing Markdown.
### bBlog ###
PHP Markdown also works with [bBlog][bb].
[bb]: http://www.bblog.com/
To use PHP Markdown with bBlog, rename "markdown.php" to
"modifier.markdown.php" and place the file in the "bBlog_plugins"
folder. This folder is located inside the "bblog" directory of
your site, like this:
(site home)/bblog/bBlog_plugins/modifier.markdown.php
Select "Markdown" as the "Entry Modifier" when you post a new
entry. This setting will only apply to the entry you are editing.
### Replacing Textile in TextPattern ###
[TextPattern][tp] use [Textile][tx] to format your text. You can
replace Textile by Markdown in TextPattern without having to change
any code by using the *Texitle Compatibility Mode*. This may work
with other software that expect Textile too.
[tx]: http://www.textism.com/tools/textile/
[tp]: http://www.textpattern.com/
1. Rename the "markdown.php" file to "classTextile.php". This will
make PHP Markdown behave as if it was the actual Textile parser.
2. Replace the "classTextile.php" file TextPattern installed in your
web directory. It can be found in the "lib" directory:
(site home)/textpattern/lib/
Contrary to Textile, Markdown does not convert quotes to curly ones
and does not convert multiple hyphens (`--` and `---`) into en- and
em-dashes. If you use PHP Markdown in Textile Compatibility Mode, you
can solve this problem by installing the "smartypants.php" file from
[PHP SmartyPants][psp] beside the "classTextile.php" file. The Textile
Compatibility Mode function will use SmartyPants automatically without
further modification.
[psp]: http://michelf.com/projects/php-smartypants/
### In Your Own Programs ###
You can use PHP Markdown easily in your current PHP program. Simply
include the file and then call the Markdown function on the text you
want to convert:
include_once "markdown.php";
$my_html = Markdown($my_text);
If you wish to use PHP Markdown with another text filter function
built to parse HTML, you should filter the text *after* the Markdown
function call. This is an example with [PHP SmartyPants][psp]:
$my_html = SmartyPants(Markdown($my_text));
### With Smarty ###
If your program use the [Smarty][sm] template engine, PHP Markdown
can now be used as a modifier for your templates. Rename "markdown.php"
to "modifier.markdown.php" and put it in your smarty plugins folder.
[sm]: http://smarty.php.net/
If you are using MovableType 3.1 or later, the Smarty plugin folder is
located at `(MT CGI root)/php/extlib/smarty/plugins`. This will allow
Markdown to work on dynamic pages.
### Updating Markdown in Other Programs ###
Many web applications now ship with PHP Markdown, or have plugins to
perform the conversion to HTML. You can update PHP Markdown -- or
replace it with PHP Markdown Extra -- in many of these programs by
swapping the old "markdown.php" file for the new one.
Here is a short non-exhaustive list of some programs and where they
hide the "markdown.php" file.
| Program | Path to Markdown
| ------- | ----------------
| [Pivot][] | `(site home)/pivot/includes/markdown/`
If you're unsure if you can do this with your application, ask the
developer, or wait for the developer to update his application or
plugin with the new version of PHP Markdown.
[Pivot]: http://pivotlog.net/
Configuration
-------------
By default, PHP Markdown produces XHTML output for tags with empty
elements. E.g.:
<br />
Markdown can be configured to produce HTML-style tags; e.g.:
<br>
To do this, you must edit the "MARKDOWN_EMPTY_ELEMENT_SUFFIX"
definition below the "Global default settings" header at the start of
the "markdown.php" file.
### WordPress-Specific Settings ###
By default, the Markdown plugin applies to both posts and comments on
your WordPress weblog. To deactivate one or the other, edit the
`MARKDOWN_WP_POSTS` or `MARKDOWN_WP_COMMENTS` definitions under the
"WordPress settings" header at the start of the "markdown.php" file.
Bugs
----
To file bug reports please send email to:
<michel.fortin@michelf.com>
Please include with your report: (1) the example input; (2) the output you
expected; (3) the output PHP Markdown actually produced.
Version History
---------------
1.0.1o (8 Jan 2012):
* Silenced a new warning introduced around PHP 5.3 complaining about
POSIX characters classes not being implemented. PHP Markdown does not
use POSIX character classes, but it nevertheless trigged that warning.
Extra 1.2.5 (8 Jan 2012):
* Fixed an issue preventing fenced code blocks indented inside lists items
and elsewhere from being interpreted correctly.
* Fixed an issue where HTML tags inside fenced code blocks were sometime
not encoded with entities.
1.0.1n (10 Oct 2009):
* Enabled reference-style shortcut links. Now you can write reference-style
links with less brakets:
This is [my website].
[my website]: http://example.com/
This was added in the 1.0.2 betas, but commented out in the 1.0.1 branch,
waiting for the feature to be officialized. [But half of the other Markdown
implementations are supporting this syntax][half], so it makes sense for
compatibility's sake to allow it in PHP Markdown too.
[half]: http://babelmark.bobtfish.net/?markdown=This+is+%5Bmy+website%5D.%0D%0A%09%09%0D%0A%5Bmy+website%5D%3A+http%3A%2F%2Fexample.com%2F%0D%0A&src=1&dest=2
* Now accepting many valid email addresses in autolinks that were
previously rejected, such as:
<abc+mailbox/department=shipping@example.com>
<!#$%&'*+-/=?^_`.{|}~@example.com>
<"abc@def"@example.com>
<"Fred Bloggs"@example.com>
<jsmith@[192.0.2.1]>
* Now accepting spaces in URLs for inline and reference-style links. Such
URLs need to be surrounded by angle brakets. For instance:
[link text](<http://url/with space> "optional title")
[link text][ref]
[ref]: <http://url/with space> "optional title"
There is still a quirk which may prevent this from working correctly with
relative URLs in inline-style links however.
* Fix for adjacent list of different kind where the second list could
end as a sublist of the first when not separated by an empty line.
* Fixed a bug where inline-style links wouldn't be recognized when the link
definition contains a line break between the url and the title.
* Fixed a bug where tags where the name contains an underscore aren't parsed
correctly.
* Fixed some corner-cases mixing underscore-ephasis and asterisk-emphasis.
Extra 1.2.4 (10 Oct 2009):
* Fixed a problem where unterminated tags in indented code blocks could
prevent proper escaping of characaters in the code block.
Extra 1.2.3 (31 Dec 2008):
* In WordPress pages featuring more than one post, footnote id prefixes are
now automatically applied with the current post ID to avoid clashes
between footnotes belonging to different posts.
* Fix for a bug introduced in Extra 1.2 where block-level HTML tags where
not detected correctly, thus the addition of erroneous `<p>` tags and
interpretation of their content as Markdown-formatted instead of
HTML-formatted.
Extra 1.2.2 (21 Jun 2008):
* Fixed a problem where abbreviation definitions, footnote
definitions and link references were stripped inside
fenced code blocks.
* Fixed a bug where characters such as `"` in abbreviation
definitions weren't properly encoded to HTML entities.
* Fixed a bug where double quotes `"` were not correctly encoded
as HTML entities when used inside a footnote reference id.
1.0.1m (21 Jun 2008):
* Lists can now have empty items.
* Rewrote the emphasis and strong emphasis parser to fix some issues
with odly placed and overlong markers.
Extra 1.2.1 (27 May 2008):
* Fixed a problem where Markdown headers and horizontal rules were
transformed into their HTML equivalent inside fenced code blocks.
Extra 1.2 (11 May 2008):
* Added fenced code block syntax which don't require indentation
and can start and end with blank lines. A fenced code block
starts with a line of consecutive tilde (~) and ends on the
next line with the same number of consecutive tilde. Here's an
example:
~~~~~~~~~~~~
Hello World!
~~~~~~~~~~~~
* Rewrote parts of the HTML block parser to better accomodate
fenced code blocks.
* Footnotes may now be referenced from within another footnote.
* Added programatically-settable parser property `predef_attr` for
predefined attribute definitions.
* Fixed an issue where an indented code block preceded by a blank
line containing some other whitespace would confuse the HTML
block parser into creating an HTML block when it should have
been code.
1.0.1l (11 May 2008):
* Now removing the UTF-8 BOM at the start of a document, if present.
* Now accepting capitalized URI schemes (such as HTTP:) in automatic
links, such as `<HTTP://EXAMPLE.COM/>`.
* Fixed a problem where `<hr@example.com>` was seen as a horizontal
rule instead of an automatic link.
* Fixed an issue where some characters in Markdown-generated HTML
attributes weren't properly escaped with entities.
* Fix for code blocks as first element of a list item. Previously,
this didn't create any code block for item 2:
* Item 1 (regular paragraph)
* Item 2 (code block)
* A code block starting on the second line of a document wasn't seen
as a code block. This has been fixed.
* Added programatically-settable parser properties `predef_urls` and
`predef_titles` for predefined URLs and titles for reference-style
links. To use this, your PHP code must call the parser this way:
$parser = new Markdwon_Parser;
$parser->predef_urls = array('linkref' => 'http://example.com');
$html = $parser->transform($text);
You can then use the URL as a normal link reference:
[my link][linkref]
[my link][linkRef]
Reference names in the parser properties *must* be lowercase.
Reference names in the Markdown source may have any case.
* Added `setup` and `teardown` methods which can be used by subclassers
as hook points to arrange the state of some parser variables before and
after parsing.
Extra 1.1.7 (26 Sep 2007):
1.0.1k (26 Sep 2007):
* Fixed a problem introduced in 1.0.1i where three or more identical
uppercase letters, as well as a few other symbols, would trigger
a horizontal line.
Extra 1.1.6 (4 Sep 2007):
1.0.1j (4 Sep 2007):
* Fixed a problem introduced in 1.0.1i where the closing `code` and
`pre` tags at the end of a code block were appearing in the wrong
order.
* Overriding configuration settings by defining constants from an
external before markdown.php is included is now possible without
producing a PHP warning.
Extra 1.1.5 (31 Aug 2007):
1.0.1i (31 Aug 2007):
* Fixed a problem where an escaped backslash before a code span
would prevent the code span from being created. This should now
work as expected:
Litteral backslash: \\`code span`
* Overall speed improvements, especially with long documents.
Extra 1.1.4 (3 Aug 2007):
1.0.1h (3 Aug 2007):
* Added two properties (`no_markup` and `no_entities`) to the parser
allowing HTML tags and entities to be disabled.
* Fix for a problem introduced in 1.0.1g where posting comments in
WordPress would trigger PHP warnings and cause some markup to be
incorrectly filtered by the kses filter in WordPress.
Extra 1.1.3 (3 Jul 2007):
* Fixed a performance problem when parsing some invalid HTML as an HTML
block which was resulting in too much recusion and a segmentation fault
for long documents.
* The markdown="" attribute now accepts unquoted values.
* Fixed an issue where underscore-emphasis didn't work when applied on the
first or the last word of an element having the markdown="1" or
markdown="span" attribute set unless there was some surrounding whitespace.
This didn't work:
<p markdown="1">_Hello_ _world_</p>
Now it does produce emphasis as expected.
* Fixed an issue preventing footnotes from working when the parser's
footnote id prefix variable (fn_id_prefix) is not empty.
* Fixed a performance problem where the regular expression for strong
emphasis introduced in version 1.1 could sometime be long to process,
give slightly wrong results, and in some circumstances could remove
entirely the content for a whole paragraph.
* Fixed an issue were abbreviations tags could be incorrectly added
inside URLs and title of links.
* Placing footnote markers inside a link, resulting in two nested links, is
no longer allowed.
1.0.1g (3 Jul 2007):
* Fix for PHP 5 compiled without the mbstring module. Previous fix to
calculate the length of UTF-8 strings in `detab` when `mb_strlen` is
not available was only working with PHP 4.
* Fixed a problem with WordPress 2.x where full-content posts in RSS feeds
were not processed correctly by Markdown.
* Now supports URLs containing literal parentheses for inline links
and images, such as:
[WIMP](http://en.wikipedia.org/wiki/WIMP_(computing))
Such parentheses may be arbitrarily nested, but must be
balanced. Unbalenced parentheses are allowed however when the URL
when escaped or when the URL is enclosed in angle brakets `<>`.
* Fixed a performance problem where the regular expression for strong
emphasis introduced in version 1.0.1d could sometime be long to process,
give slightly wrong results, and in some circumstances could remove
entirely the content for a whole paragraph.
* Some change in version 1.0.1d made possible the incorrect nesting of
anchors within each other. This is now fixed.
* Fixed a rare issue where certain MD5 hashes in the content could
be changed to their corresponding text. For instance, this:
The MD5 value for "+" is "26b17225b626fb9238849fd60eabdf60".
was incorrectly changed to this in previous versions of PHP Markdown:
<p>The MD5 value for "+" is "+".</p>
* Now convert escaped characters to their numeric character
references equivalent.
This fix an integration issue with SmartyPants and backslash escapes.
Since Markdown and SmartyPants have some escapable characters in common,
it was sometime necessary to escape them twice. Previously, two
backslashes were sometime required to prevent Markdown from "eating" the
backslash before SmartyPants sees it:
Here are two hyphens: \\--
Now, only one backslash will do:
Here are two hyphens: \--
Extra 1.1.2 (7 Feb 2007)
* Fixed an issue where headers preceded too closely by a paragraph
(with no blank line separating them) where put inside the paragraph.
* Added the missing TextileRestricted method that was added to regular
PHP Markdown since 1.0.1d but which I forgot to add to Extra.
1.0.1f (7 Feb 2007):
* Fixed an issue with WordPress where manually-entered excerpts, but
not the auto-generated ones, would contain nested paragraphs.
* Fixed an issue introduced in 1.0.1d where headers and blockquotes
preceded too closely by a paragraph (not separated by a blank line)
where incorrectly put inside the paragraph.
* Fixed an issue introduced in 1.0.1d in the tokenizeHTML method where
two consecutive code spans would be merged into one when together they
form a valid tag in a multiline paragraph.
* Fixed an long-prevailing issue where blank lines in code blocks would
be doubled when the code block is in a list item.
This was due to the list processing functions relying on artificially
doubled blank lines to correctly determine when list items should
contain block-level content. The list item processing model was thus
changed to avoid the need for double blank lines.
* Fixed an issue with `<% asp-style %>` instructions used as inline
content where the opening `<` was encoded as `&lt;`.
* Fixed a parse error occuring when PHP is configured to accept
ASP-style delimiters as boundaries for PHP scripts.
* Fixed a bug introduced in 1.0.1d where underscores in automatic links
got swapped with emphasis tags.
Extra 1.1.1 (28 Dec 2006)
* Fixed a problem where whitespace at the end of the line of an atx-style
header would cause tailing `#` to appear as part of the header's content.
This was caused by a small error in the regex that handles the definition
for the id attribute in PHP Markdown Extra.
* Fixed a problem where empty abbreviations definitions would eat the
following line as its definition.
* Fixed an issue with calling the Markdown parser repetitivly with text
containing footnotes. The footnote hashes were not reinitialized properly.
1.0.1e (28 Dec 2006)
* Added support for internationalized domain names for email addresses in
automatic link. Improved the speed at which email addresses are converted
to entities. Thanks to Milian Wolff for his optimisations.
* Made deterministic the conversion to entities of email addresses in
automatic links. This means that a given email address will always be
encoded the same way.
* PHP Markdown will now use its own function to calculate the length of an
UTF-8 string in `detab` when `mb_strlen` is not available instead of
giving a fatal error.
Extra 1.1 (1 Dec 2006)
* Added a syntax for footnotes.
* Added an experimental syntax to define abbreviations.
1.0.1d (1 Dec 2006)
* Fixed a bug where inline images always had an empty title attribute. The
title attribute is now present only when explicitly defined.
* Link references definitions can now have an empty title, previously if the
title was defined but left empty the link definition was ignored. This can
be useful if you want an empty title attribute in images to hide the
tooltip in Internet Explorer.
* Made `detab` aware of UTF-8 characters. UTF-8 multi-byte sequences are now
correctly mapped to one character instead of the number of bytes.
* Fixed a small bug with WordPress where WordPress' default filter `wpautop`
was not properly deactivated on comment text, resulting in hard line breaks
where Markdown do not prescribes them.
* Added a `TextileRestrited` method to the textile compatibility mode. There
is no restriction however, as Markdown does not have a restricted mode at
this point. This should make PHP Markdown work again in the latest
versions of TextPattern.
* Converted PHP Markdown to a object-oriented design.
* Changed span and block gamut methods so that they loop over a
customizable list of methods. This makes subclassing the parser a more
interesting option for creating syntax extensions.
* Also added a "document" gamut loop which can be used to hook document-level
methods (like for striping link definitions).
* Changed all methods which were inserting HTML code so that they now return
a hashed representation of the code. New methods `hashSpan` and `hashBlock`
are used to hash respectivly span- and block-level generated content. This
has a couple of significant effects:
1. It prevents invalid nesting of Markdown-generated elements which
could occur occuring with constructs like `*something [link*][1]`.
2. It prevents problems occuring with deeply nested lists on which
paragraphs were ill-formed.
3. It removes the need to call `hashHTMLBlocks` twice during the the
block gamut.
Hashes are turned back to HTML prior output.
* Made the block-level HTML parser smarter using a specially-crafted regular
expression capable of handling nested tags.
* Solved backtick issues in tag attributes by rewriting the HTML tokenizer to
be aware of code spans. All these lines should work correctly now:
<span attr='`ticks`'>bar</span>
<span attr='``double ticks``'>bar</span>
`<test a="` content of attribute `">`
* Changed the parsing of HTML comments to match simply from `<!--` to `-->`
instead using of the more complicated SGML-style rule with paired `--`.
This is how most browsers parse comments and how XML defines them too.
* `<address>` has been added to the list of block-level elements and is now
treated as an HTML block instead of being wrapped within paragraph tags.
* Now only trim trailing newlines from code blocks, instead of trimming
all trailing whitespace characters.
* Fixed bug where this:
[text](http://m.com "title" )
wasn't working as expected, because the parser wasn't allowing for spaces
before the closing paren.
* Filthy hack to support markdown='1' in div tags.
* _DoAutoLinks() now supports the 'dict://' URL scheme.
* PHP- and ASP-style processor instructions are now protected as
raw HTML blocks.
<? ... ?>
<% ... %>
* Fix for escaped backticks still triggering code spans:
There are two raw backticks here: \` and here: \`, not a code span
Extra 1.0 - 5 September 2005
* Added support for setting the id attributes for headers like this:
Header 1 {#header1}
========
## Header 2 ## {#header2}
This only work only for headers for now.
* Tables will now work correctly as the first element of a definition
list. For example, this input:
Term
: Header | Header
------- | -------
Cell | Cell
used to produce no definition list and a table where the first
header was named ": Header". This is now fixed.
* Fix for a problem where a paragraph following a table was not
placed between `<p>` tags.
Extra 1.0b4 - 1 August 2005
* Fixed some issues where whitespace around HTML blocks were trigging
empty paragraph tags.
* Fixed an HTML block parsing issue that would cause a block element
following a code span or block with unmatched opening bracket to be
placed inside a paragraph.
* Removed some PHP notices that could appear when parsing definition
lists and tables with PHP notice reporting flag set.
Extra 1.0b3 - 29 July 2005
* Definition lists now require a blank line before each term. Solves
an ambiguity where the last line of lazy-indented definitions could
be mistaken by PHP Markdown as a new term in the list.
* Definition lists now support multiple terms per definition.
* Some special tags were replaced in the output by their md5 hash
key. Things such as this now work as expected:
## Header <?php echo $number ?> ##
Extra 1.0b2 - 26 July 2005
* Definition lists can now take two or more definitions for one term.
This should have been the case before, but a bug prevented this
from working right.
* Fixed a problem where single column table with a pipe only at the
end where not parsed as table. Here is such a table:
| header
| ------
| cell
* Fixed problems with empty cells in the first column of a table with
no leading pipe, like this one:
header | header
------ | ------
| cell
* Code spans containing pipes did not within a table. This is now
fixed by parsing code spans before splitting rows into cells.
* Added the pipe character to the backlash escape character lists.
Extra 1.0b1 (25 Jun 2005)
* First public release of PHP Markdown Extra.
Copyright and License
---------------------
PHP Markdown & Extra
Copyright (c) 2004-2009 Michel Fortin
<http://michelf.com/>
All rights reserved.
Based on Markdown
Copyright (c) 2003-2005 John Gruber
<http://daringfireball.net/>
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 "Markdown" 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.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +0,0 @@
PHP SmartyPants & Typographer
Copyright (c) 2005-2006 Michel Fortin
<http://www.michelf.com/>
All rights reserved.
Original SmartyPants
Copyright (c) 2003-2004 John Gruber
<http://daringfireball.net/>
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 "SmartyPants" 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.

View File

@ -1,261 +0,0 @@
PHP SmartyPants Typographer
===========================
Version 1.0 - Wed 28 Jun 2006
by Michel Fortin
<http://www.michelf.com/>
Original SmartyPants by John Gruber
<http://daringfireball.net/>
Introduction
------------
This is a special version of PHP SmartyPants with extra features. See
<http://www.michelf.com/projects/php-smartypants/typographer/> for
details.
PHP SmartyPants is a free web publishing plug-in for WordPress and
Smarty template engine that easily translates plain ASCII punctuation
characters into "smart" typographic punctuation HTML entities.
SmartyPants can also be invoked as a standalone PHP function.
PHP SmartyPants is a port to PHP of the original SmartyPants written
in Perl by John Gruber.
SmartyPants can perform the following transformations:
* Straight quotes (`"` and `'`) into "curly" quote HTML entities
* Backtick-style quotes (` ``like this'' `) into "curly" quote HTML
entities
* Dashes (`--` and `---`) into en- and em-dash entities
* Three consecutive dots (`...`) into an ellipsis entity
This means you can write, edit, and save using plain old ASCII straight
quotes, plain dashes, and plain dots, but your published posts (and
final HTML output) will appear with smart quotes, em-dashes, and proper
ellipses.
SmartyPants does not modify characters within `<pre>`, `<code>`,
`<kbd>`, or `<script>` tag blocks. Typically, these tags are used to
display text where smart quotes and other "smart punctuation" would not
be appropriate, such as source code or example markup.
### Backslash Escapes ###
If you need to use literal straight quotes (or plain hyphens and
periods), SmartyPants accepts the following backslash escape sequences
to force non-smart punctuation. It does so by transforming the escape
sequence into a decimal-encoded HTML entity:
Escape Value Character
------ ----- ---------
\\ &#92; \
\" &#34; "
\' &#39; '
\. &#46; .
\- &#45; -
\` &#96; `
This is useful, for example, when you want to use straight quotes as
foot and inch marks:
6\'2\" tall
translates into:
6&#39;2&#34; tall
in SmartyPants's HTML output. Which, when rendered by a web browser,
looks like:
6'2" tall
Installation and Requirement
----------------------------
PHP SmartyPants require PHP version 4.0.5 or later.
### WordPress ###
WordPress already include a filter called "Texturize" with the same
goal as SmartyPants. You could still find some usefulness to
PHP SmartyPants if you are not happy enough with the standard algorithm.
PHP SmartyPants works with [WordPress][wp], version 1.2 or later.
[wp]: http://wordpress.org/
1. To use PHP SmartyPants with WordPress, place the "smartypants.php"
file in the "plugins" folder. This folder is hidden inside
"wp-content" at the root of your site:
(site home)/wp-content/plugins/smartypants.php
2. Activate the plugin with the administrative interface of WordPress.
In the "Plugins" section you will now find SmartyPants. To activate
the plugin, click on the "Activate" button on the same line than
SmartyPants. Your entries will now be filtered by PHP SmartyPants.
Note: It is not possible at this time to apply a different set of
filters to different entries. All your entries will be filtered by
PHP SmartyPants if the plugin is active. This is currently a limitation
of WordPress.
### In your programs ###
You can use PHP SmartyPants easily in your current PHP program. Simply
include the file and then call the `SmartyPants` function on the text
you want to convert:
include_once "smartypants.php";
$my_text = SmartyPants($my_text);
### With Smarty ###
If your program use the [Smarty][sm] template engine, PHP SmartyPants
can now be used as a modifier for your templates. Rename
"smartypants.php" to "modifier.smartypants.php" and put it in your
smarty plugins folder.
[sm]: http://smarty.php.net/
Options and Configuration
-------------------------
Settings are specified by editing the value of the `$smartypants_attr`
variable in the "smartypants.php" file. For users of the Smarty template
engine, the "smartypants" modifier also takes an optional attribute where
you can specify configuration options, like this:
`{$var|smartypants:1}` (where "1" is the configuration option).
Numeric values are the easiest way to configure SmartyPants's behavior:
"0"
Suppress all transformations. (Do nothing.)
"1"
Performs default SmartyPants transformations: quotes (including
backticks-style), em-dashes, and ellipses. `--` (dash dash) is
used to signify an em-dash; there is no support for en-dashes.
"2"
Same as smarty_pants="1", except that it uses the old-school
typewriter shorthand for dashes: `--` (dash dash) for en-dashes,
`---` (dash dash dash) for em-dashes.
"3"
Same as smarty_pants="2", but inverts the shorthand for dashes: `--`
(dash dash) for em-dashes, and `---` (dash dash dash) for en-dashes.
"-1"
Stupefy mode. Reverses the SmartyPants transformation process,
turning the HTML entities produced by SmartyPants into their ASCII
equivalents. E.g. `&#8220;` is turned into a simple double-quote
(`"`), `&#8212;` is turned into two dashes, etc. This is useful if you
wish to suppress smart punctuation in specific pages, such as
RSS feeds.
The following single-character attribute values can be combined to
toggle individual transformations from within the smarty_pants
attribute. For example, to educate normal quotes and em-dashes, but not
ellipses or backticks-style quotes:
$smartypants_attr = "qd";
Or inside a Smarty template:
{$var|smartypants:"qd"}
"q"
Educates normal quote characters: (`"`) and (`'`).
"b"
Educates ` ``backticks'' ` double quotes.
"B"
Educates backticks-style double quotes and ` `single' ` quotes.
"d"
Educates em-dashes.
"D"
Educates em-dashes and en-dashes, using old-school typewriter
shorthand: (dash dash) for en-dashes, (dash dash dash) for
em-dashes.
"i"
Educates em-dashes and en-dashes, using inverted old-school
typewriter shorthand: (dash dash) for em-dashes, (dash dash dash)
for en-dashes.
"e"
Educates ellipses.
"w"
Translates any instance of `&quot;` into a normal double-quote
character. This should be of no interest to most people, but of
particular interest to anyone who writes their posts using
Dreamweaver, as Dreamweaver inexplicably uses this entity to
represent a literal double-quote character. SmartyPants only
educates normal quotes, not entities (because ordinarily, entities
are used for the explicit purpose of representing the specific
character they represent). The "w" option must be used in
conjunction with one (or both) of the other quote options ("q" or
"b"). Thus, if you wish to apply all SmartyPants transformations
(quotes, en- and em-dashes, and ellipses) and also translate
`&quot;` entities into regular quotes so SmartyPants can educate
them, you should set the SMARTYPANTS_ATTR constant at the top of
the file to:
define( 'SMARTYPANTS_ATTR', "qDew" );
Inside a Smarty template, you could also pass the string as a
parameter:
{$var|smartypants:"qDew"}
### Algorithmic Shortcomings ###
One situation in which quotes will get curled the wrong way is when
apostrophes are used at the start of leading contractions. For example:
'Twas the night before Christmas.
In the case above, SmartyPants will turn the apostrophe into an opening
single-quote, when in fact it should be a closing one. I don't think
this problem can be solved in the general case -- every word processor
I've tried gets this wrong as well. In such cases, it's best to use the
proper HTML entity for closing single-quotes (`&#8217;` or `&rsquo;`) by
hand.
Bugs
----
To file bug reports or feature requests (other than topics listed in the
Caveats section above) please send email to:
<michel.fortin@michelf.com>
If the bug involves quotes being curled the wrong way, please send
example text to illustrate.
Version History
---------------
1.0 (28 Jun 2006)
* First public release of PHP SmartyPants Typographer.

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