Merge pull request #8 from there4/2.0-wip

Release version 2.0
This commit is contained in:
Craig Davis 2014-01-12 15:22:07 -08:00
commit 12f81bea4e
121 changed files with 2323 additions and 18744 deletions

21
.gitignore vendored
View File

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

9
.travis.yml Normal file
View File

@ -0,0 +1,9 @@
language: php
before_script:
- curl -s http://getcomposer.org/installer | php
- php composer.phar install --dev
php:
- 5.3
- 5.4

View File

@ -6,55 +6,104 @@ at the [blog post for the project][blog].
## Features
* Three styles to choose from: modern, blockish, unstyled
* PDF generation via `wkhtmltopdf`
* Three styles to choose from: modern, blockish, unstyled (Fork and add more!)
* PDF generation via [wkhtmltopdf][wkhtmltopdf]
* Responsive design for multiple device viewport sizes
* Simple Markdown formatting
* Single file deployment
* Single file deployment (no external stylesheets)
* You can now version control and branch your resume.
## Quickstart
php ./bin/resume.php --source resume/sample.md
php ./bin/resume.php --source resume/sample.md --pdf
There is no installation or need to run composer. Just run the phar file:
## Options
./bin/md2resume html examples/source/sample.md examples/output/
./bin/md2resume pdf examples/source/sample.md examples/output/
## Help
Markdown Resume Generator version 2.0.0 by Craig Davis
Usage:
[options] command [arguments]
Options:
--help -h Display this help message.
--quiet -q Do not output any message.
--verbose -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
--version -V Display this application version.
--ansi Force ANSI output.
--no-ansi Disable ANSI output.
--no-interaction -n Do not ask any interactive question.
Available commands:
help Displays help for a command
html Generate an HTML resume from a markdown file
list Lists commands
pdf Generate a PDF from a markdown file
selfupdate Updates md2resume.phar to the latest version.
templates List available templates
version Show current version information
## Examples
Choose a template with the -t option.
php ./bin/resume.php --source resume/sample.md -t blockish
./bin/md2resume html --template blockish examples/source/sample.md examples/output/
If you want to edit your markdown resume in your editor while watching it
update in your browser, run this command:
watch php ./bin/resume.php -s resume/sample.md -r
watch ./bin/md2resume html --refresh examples/source/sample.md examples/output/
This makes the build script run periodically, and html document will refresh
every two seconds via a meta tag. Open the `./ouput/sample.html` file in
your browser, and then just save your markdown document when you want to see
every two seconds via a meta tag. Open the `./examples/ouput/sample.html` file
in your browser, and then just save your markdown document when you want to see
a fresh preview.
## Development
## Authoring Your Resume
Markdown is limited to basic html markup. Follow the `resume/sample.md` file
as a guideline. This file includes various headers and several nested elements.
This allows us to construct a semantic HTML document for the resume, and then
use a CSS rules to display a very nice resume. Note that because we have very
few ways to nest or identify elements that many of the css rules are based
on descendant and adjacent selectors.
Markdown is limited to basic html markup. Follow the `examples/source/sample.md`
file as a guideline. This file includes various headers and several nested
elements. This allows us to construct a semantic HTML document for the resume,
and then use a CSS rules to display a very nice resume. Note that because we
have very few ways to nest or identify elements that many of the css rules are
based on descendant and adjacent selectors.
__PLEASE NOTE__: The templates are compiled into the phar archive in the `./bin`
folder. If you intend to edit the templates or add new ones, you'll need to run
this application in the dev mode. See below for more information about doing
this.
## Feature Development
The application is deployed as a compiled phar file. In order to add new
commands, you'll need to first install the dependencies:
* `composer install`
* `pear install PHP_CodeSniffer`
* [install pake][pake]
After that, you can run the `md2resume_dev.php` file from the command line.
Check out the pake tooling for more information about the build.
## TODO
* Google Analytics include
* CDN for fonts
## Acknowledgments
The initial inspiration is from the [Sample Resume Template][srt].
However, no HTML from that project has been used in this. General layout has been reused, and media queries
have been added. It's a nice template, and if you are a more comfortable with html than markdown, you should use it.
However, no HTML from that project has been used in this. General layout has
been reused, and media queries have been added. It's a nice template, and if you
are a more comfortable with html than markdown, you should use it.
## Changelog
* __2.0.0__ : Complete rewrite with the [symfony console component][console].
Deployment is no done with a compiled phar file, and development dependencies
are managed with composer.
* __0.9.0__ : Add composer and update README with new changelog
* __0.8.8__ : Add Chinese text example (@ishitcno1)
* __0.8.7__ : Update pdf formatting of the modern template (@roleary)
@ -67,3 +116,6 @@ have been added. It's a nice template, and if you are a more comfortable with ht
[srt]: http://sampleresumetemplate.net/ "A great starting point"
[blog]: http://there4development.com/blog/2012/12/31/markdown-resume-builder/
[pake]: https://github.com/indeyets/pake/wiki/Installing-Pake
[wkhtmltopdf]: https://github.com/pdfkit/pdfkit/wiki/Installing-WKHTMLTOPDF
[console]: http://symfony.com/doc/current/components/console/introduction.html

BIN
bin/md2resume Executable file

Binary file not shown.

View File

@ -1,129 +0,0 @@
#!/usr/bin/env php
<?php
define('APPLICATION_BASE_PATH', realpath(__DIR__ . '/..'));
require APPLICATION_BASE_PATH . '/vendor/autoload.php';
require APPLICATION_BASE_PATH . '/vendor/Mustache/Mustache.php';
require APPLICATION_BASE_PATH . '/vendor/smartypants/smartypants.php';
require APPLICATION_BASE_PATH . '/vendor/markdown-extra/markdown.php';
require APPLICATION_BASE_PATH . '/vendor/lessphp/lessc.inc.php';
require APPLICATION_BASE_PATH . '/vendor/simpledom/simple_html_dom.php';
use Assetic\Asset\AssetCollection;
use Assetic\Asset\FileAsset;
use Assetic\Asset\GlobAsset;
use Assetic\Filter;
// Application defaults
$config = (object) array(
"source" => "",
"output" => "./output",
"template" => "modern",
"refresh" => false,
"pdf" => false
);
// Command line arguments to populate the config
$opts = array(
"s:" => "source:", // source
"o:" => "output:", // output
"t:" => "template:", // template
"r" => "refresh", // refresh
"p" => "pdf" // pdf output
);
// Fetch the options from the command line arguments
$options = getopt(implode("", array_keys($opts)), array_values($opts));
// Consolidate the short and long options into the config array
// Make sure that boolean options are set appropriately.
foreach ($opts as $short => $long) {
$isBool = (substr($short, -1, 1) !== ":");
$short = trim($short, ":");
$long = trim($long, ":");
if (isset($options[$short])) {
$config->$long = $isBool ? true : $options[$short];
}
else if (isset($options[$long])) {
$config->$long = $isBool ? true : $options[$long];
}
}
if (empty($config->source)) {
exit("Please specify a source document: bin/resume.php -s resume/resume.pdf\n");
}
$basename = pathinfo($config->source, PATHINFO_FILENAME);
$template_path = realpath(__DIR__ . '/../templates/' . $config->template);
$pdf_source = $config->output . '/' . $basename . '-pdf.html';
$output = $config->output . '/' . $basename . '.html';
$pdf_output = $config->output . '/' . $basename . '.pdf';
if (!file_exists($config->source)) {
exit("Please specify a valid source file.\n");
}
if (!file_exists($template_path)) {
// TODO: List templates
exit("Please specify a valid template.\n");
}
// We build these into a single string so that we can deploy this resume as a
// single file.
$css = new AssetCollection(
array(new GlobAsset($template_path . '/css/*.css')),
array(new Filter\LessphpFilter())
);
$style = $css->dump();
$template = file_get_contents($template_path . '/index.html');
$resume = file_get_contents($config->source);
// Process with Markdown, and then use SmartyPants to clean up punctuation.
$resume = SmartyPants(Markdown($resume));
// We'll construct the title for the html document from the h1 and h2 tags
$html = str_get_html($resume);
$title = sprintf(
'%s | %s',
$html->find('h1', 0)->innertext,
$html->find('h2', 0)->innertext
);
// We'll now render the Markdown into an html file with Mustache Templates
$m = new Mustache;
$rendered = $m->render(
$template,
array(
'title' => $title,
'style' => $style,
'resume' => $resume,
'reload' => $config->refresh
)
);
// Save the fully rendered html to the final destination
file_put_contents($output, $rendered);
echo "Wrote html to $output\n";
// If the user wants to make a pdf file, we'll use wkhtmltopdf to convert
// the html document into a nice looking pdf.
if ($config->pdf) {
// The pdf needs some extra css rules, and so we'll add them here
// to our html document
$pdf_classed = str_replace('body class=""', 'body class="pdf"', $rendered);
// Save the new pdf-ready html to a temp destination
file_put_contents($pdf_source, $pdf_classed );
// Process the document with wkhtmltopdf
exec('wkhtmltopdf ' . $pdf_source .' ' . $pdf_output);
// Unlink the temporary file
unlink($pdf_source);
echo "Wrote pdf to $pdf_output\n";
}
/* End of file resume.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

@ -1,29 +1,60 @@
{
"name": "there4/markdown-resume",
"description": "Generate a responsive CSS3 and HTML5 resume with Markdown, with optional PDF output.",
"description": "Markdown Resume Generator",
"homepage": "https://github.com/there4/markdown-resume",
"keywords": [
"markdown",
"resume",
"html5"
],
"license": "MIT",
"version": "2.0.0",
"selfupdatepath": "://github.com/there4/markdown-resume/raw/master/bin/md2resume",
"selfupdateversion": "://github.com/there4/markdown-resume/raw/master/version",
"authors": [
{
"name": "Craig Davis",
"email": "craig@there4development.com",
"role": "Developer"
}, {
},
{
"name": "Kaiwen Xu",
"email": "kevin@kevxu.net",
"role": "Contributor"
}, {
},
{
"name": "ishitcno1",
"role": "Contributor"
}, {
},
{
"name": "Roland O'Leary",
"role": "Contributor"
}, {
},
{
"name": "Abhishek Kandoi",
"email": "abhikandoi2000@gmail.com",
"role": "Contributor"
}
],
"support": {
"issues": "https://github.com/there4/markdown-resume/issues"
},
"minimum-stability": "dev",
"require": {
"kriswallsmith/assetic": "1.1.2",
"leafo/lessphp": "v0.4.0",
"michelf/php-markdown": "1.4.0",
"michelf/php-smartypants": "1.6.0-beta1",
"mustache/mustache": "2.5.1",
"simple-html-dom/simple-html-dom": "1.5.0",
"symfony/config": "v2.3.4",
"symfony/console": "v2.3.4",
"symfony/yaml": "v2.3.4",
"twig/twig": "v1.13.2"
},
"autoload": {
"classmap": [
"vendor/simple-html-dom/simple-html-dom/"
]
}
}
}

608
composer.lock generated Normal file
View File

@ -0,0 +1,608 @@
{
"_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": "40e7ded8558afab5c3b481ec2d33dafe",
"packages": [
{
"name": "kriswallsmith/assetic",
"version": "v1.1.2",
"source": {
"type": "git",
"url": "https://github.com/kriswallsmith/assetic.git",
"reference": "735cffd3982c6e8cdebe292d5db39d077f65890f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/735cffd3982c6e8cdebe292d5db39d077f65890f",
"reference": "735cffd3982c6e8cdebe292d5db39d077f65890f",
"shasum": ""
},
"require": {
"php": ">=5.3.1",
"symfony/process": "~2.1"
},
"require-dev": {
"cssmin/cssmin": "*",
"joliclic/javascript-packer": "*",
"kamicane/packager": "*",
"leafo/lessphp": "*",
"leafo/scssphp": "*",
"leafo/scssphp-compass": "*",
"mrclay/minify": "*",
"phpunit/phpunit": "~3.7",
"ptachoire/cssembed": "*",
"twig/twig": "~1.6"
},
"suggest": {
"leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler",
"leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler",
"leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin",
"ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris",
"twig/twig": "Assetic provides the integration with the Twig templating engine"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
},
"autoload": {
"psr-0": {
"Assetic": "src/"
},
"files": [
"src/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kris Wallsmith",
"email": "kris.wallsmith@gmail.com",
"homepage": "http://kriswallsmith.net/"
}
],
"description": "Asset Management for PHP",
"homepage": "https://github.com/kriswallsmith/assetic",
"keywords": [
"assets",
"compression",
"minification"
],
"time": "2013-07-19 00:03:27"
},
{
"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": "michelf/php-smartypants",
"version": "1.6.0-beta1",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-smartypants.git",
"reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-smartypants/zipball/c0465c6d4c5ab853c2fa45df6c10bce7e35cc137",
"reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-lib": "1.6.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 SmartyPants",
"homepage": "http://michelf.ca/projects/php-smartypants/",
"keywords": [
"dashes",
"quotes",
"spaces",
"typographer",
"typography"
],
"time": "2013-07-31 18:13:10"
},
{
"name": "mustache/mustache",
"version": "v2.5.1",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/mustache.php.git",
"reference": "996c944fa2ddedddfaf0d276b913809d6a32fd85"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/996c944fa2ddedddfaf0d276b913809d6a32fd85",
"reference": "996c944fa2ddedddfaf0d276b913809d6a32fd85",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"type": "library",
"autoload": {
"psr-0": {
"Mustache": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Justin Hileman",
"email": "justin@justinhileman.info",
"homepage": "http://justinhileman.com"
}
],
"description": "A Mustache implementation in PHP.",
"homepage": "https://github.com/bobthecow/mustache.php",
"keywords": [
"mustache",
"templating"
],
"time": "2014-01-09 00:36:09"
},
{
"name": "simple-html-dom/simple-html-dom",
"version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/Youpie/simple-html-dom.git",
"reference": "961610576c460546677a635062e9d3d7b6dab745"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Youpie/simple-html-dom/zipball/961610576c460546677a635062e9d3d7b6dab745",
"reference": "961610576c460546677a635062e9d3d7b6dab745",
"shasum": ""
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "S.C. Chen",
"email": "me578022@gmail.com",
"homepage": "http://simplehtmldom.sourceforge.net/"
}
],
"description": "A copy of the PHP Simple HTML DOM Parser project.",
"time": "2013-05-14 22:27:35"
},
{
"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/process",
"version": "dev-master",
"target-dir": "Symfony/Component/Process",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
"reference": "d4b086ca4d6e192d8c2c64fe011159c8bc4c9daa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/d4b086ca4d6e192d8c2c64fe011159c8bc4c9daa",
"reference": "d4b086ca4d6e192d8c2c64fe011159c8bc4c9daa",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Process\\": ""
}
},
"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 Process 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"
},
{
"name": "twig/twig",
"version": "v1.13.2",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig.git",
"reference": "6d6a1009427d1f398c9d40904147bf9f723d5755"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Twig/zipball/6d6a1009427d1f398c9d40904147bf9f723d5755",
"reference": "6d6a1009427d1f398c9d40904147bf9f723d5755",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
}
},
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
],
"time": "2013-08-03 15:35:31"
}
],
"packages-dev": [
],
"aliases": [
],
"minimum-stability": "dev",
"stability-flags": {
"michelf/php-smartypants": 10
},
"platform": [
],
"platform-dev": [
]
}

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
------

32
md2resume_dev.php Executable file
View File

@ -0,0 +1,32 @@
<?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 application with the Composer autoloader
$app = require __DIR__ . '/vendor/autoload.php';
// Setup the namespace for our own namespace
$app->add('Resume', __DIR__ . '/src');
// Instantiate our Console application
$console = new Resume\Cli\Resume();
// 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'));
}
$templatePath = __DIR__ . '/templates';
$consoleTemplatePath = __DIR__ . '/src/Resume/Templates';
// Init the app with these params
$console->initialize($templatePath, $consoleTemplatePath, $project);
// Execute the console app.
$console->run();
/* End of resume.php */

121
pakefile Normal file
View File

@ -0,0 +1,121 @@
<?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 Markdown Resume Builder version " . $composer->version . "\n";
echo str_repeat("=", 80) . "\n";
}
function run_version_file() {
// Find the latest tag
$version = trim(shell_exec('git describe --abbrev=0 --tags'));
// Write it to the version file for the self update command
file_put_contents('./version', $version);
// Write it to the composer.json file as well
$config = json_decode(file_get_contents('composer.json'));
$config->version = $version;
file_put_contents('composer.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
}
function run_lint() {
echo "\n * Linting files\n";
passthru("./build/lint -R ./src");
}
function run_phar()
{
echo " * Construction phar and moving to ./bin/md2resume\n";
$command =
'rm -f ./bin/md2resume && rm -f ./bin/md2resume.phar &&'
. 'php -dphar.readonly=0 build/empir make ./bin/md2resume.phar md2resume_dev.php . --exclude="'
. '*.git/*|*.gitignore|*test*|*Tests*|*.md|*/doc/*|*.lock|*token.txt|pakefile'
. '|.*|build/*|*.markdown|*.phar|*LICENSE|*AUTHORS|*CHANGELOG|*.dist|*.tpl|.travis.yml'
. '" && chmod a+x ./bin/md2resume.phar'
. ' && mv ./bin/md2resume.phar ./bin/md2resume';
passthru($command);
}
function run_sniff()
{
echo " * Checking files for PSR2\n";
passthru("phpcs -p --standard=PSR2 ./src/ ./md2resume_dev.php");
}
function run_fixer()
{
echo "\n * Running php-cs-fixer\n";
passthru(
"php-cs-fixer fix ./md2resume_dev.php"
. " && php-cs-fixer fix ./src/Resume/Cli/"
. " && php-cs-fixer fix ./src/Resume/Command/"
);
}
function run_readme()
{
echo " * Updating README documentation\n";
$readme = file("README.md");
$help = explode("\n", shell_exec("php ./md2resume_dev.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 ./bin/md2resume ~/bin/md2resume');
}
/* 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="Markdown Resume Builder Test Suite">
<directory suffix="Test.php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

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

@ -0,0 +1,144 @@
<?php
namespace Resume\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\Output\OutputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Resume\Cli;
use Resume\Command;
class Resume extends Application
{
public $defaultTemplate = 'modern';
public $recentCaseLimit = 10;
public function initialize($templatePath, $consoleTemplatePath, $project)
{
$runSetup = false;
// Add the composer information for use in version info and such.
$this->project = $project;
// The absolute path to the html output templates
$this->templatePath = $templatePath;
// https://github.com/symfony/Console/blob/master/Output/Output.php
// the alternative is OutputInterface::OUTPUT_PLAIN;
$this->outputFormat = OutputInterface::OUTPUT_NORMAL;
// 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\HtmlCommand());
$this->add(new Command\PdfCommand());
$this->add(new Command\SelfUpdateCommand());
$this->add(new Command\TemplatesCommand());
$this->add(new Command\VersionCommand());
// We'll use [Twig](http://twig.sensiolabs.org/) for template output
$loader = new \Twig_Loader_Filesystem($consoleTemplatePath);
$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("Resume\Cli\TwigFormatters::strpad"));
$this->twig->addFilter('style', new \Twig_Filter_Function("Resume\Cli\TwigFormatters::style"));
$this->twig->addFilter('repeat', new \Twig_Filter_Function("str_repeat"));
$this->twig->addFilter('wrap', new \Twig_Filter_Function("wordwrap"));
}
public function getLongVersion()
{
return parent::getLongVersion().' by <comment>Craig Davis</comment>';
}
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");
}
}
return parent::run($input, $output);
}
}
/* End of file Resume.php */

View File

@ -0,0 +1,36 @@
<?php
namespace Resume\Cli;
class TwigFormatters
{
public static function strpad($string, $length, $position = "center")
{
switch ($position) {
case "left":
$padding = STR_PAD_RIGHT;
break;
case "right":
$padding = STR_PAD_LEFT;
break;
case "center":
default:
$padding = STR_PAD_BOTH;
}
// This must handle tagged strings for our Console formatting
// <info>this is a long title</info>
$total_length = strlen($string);
$stripped_length = strlen(strip_tags($string));
$length = $length + $total_length - $stripped_length;
return str_pad(substr($string, 0, $length), $length, " ", $padding);
}
public static function style($string, $format)
{
return sprintf('<%2$s>%1$s</%2$s>', $string, $format);
}
}
/* End of file TwigFormatters */

View File

@ -0,0 +1,155 @@
<?php
namespace Resume\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Assetic\Asset\AssetCollection;
use Assetic\Asset\FileAsset;
use Assetic\Filter;
use Michelf\MarkdownExtra;
use Michelf\SmartyPants;
class HtmlCommand extends Command
{
protected function configure()
{
// resume html source.md resume.html -template blockish -refresh
$this
->setName('html')
->setDescription('Generate an HTML resume from a markdown file')
->addArgument(
'source',
InputArgument::REQUIRED,
'Source markdown document'
)
->addArgument(
'destination',
InputArgument::REQUIRED,
'Output destination folder'
)
->addOption(
'template',
't',
InputOption::VALUE_OPTIONAL,
'Which of the templates to use'
)
->addOption(
'refresh',
'r',
InputOption::VALUE_NONE,
'If set, the html will include a meta command to refresh the ' .
'document every 5 seconds.'
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->app = $this->getApplication();
$source = $input->getArgument('source');
$destination = trim($input->getArgument('destination'), DIRECTORY_SEPARATOR);
$template = $input->getOption('template');
$refresh = $input->getOption('refresh');
$destFilename = join(DIRECTORY_SEPARATOR, array($destination, pathinfo($source, PATHINFO_FILENAME) . '.html'));
$rendered = $this->generateHtml($source, $template, $refresh);
file_put_contents($destFilename, $rendered);
$output->writeln(
sprintf(
"Wrote resume to: <info>%s</info>",
$destFilename
),
$this->app->outputFormat
);
return true;
}
protected function generateHtml($source, $template, $refresh)
{
// Check that the source file is sane
if (!file_exists($source)) {
$output->writeln(
sprintf(
"<error>Unable to open source file: %s</error>",
$source
),
$this->app->outputFormat
);
return false;
}
// Check that our template is sane, or set to the default one
if (!$template) {
$template = $this->app->defaultTemplate;
}
$templatePath = join(DIRECTORY_SEPARATOR, array($this->app->templatePath, basename($template)));
$templateIndexPath = join(DIRECTORY_SEPARATOR, array($templatePath, 'index.html'));
if (!file_exists($templateIndexPath)) {
$output->writeln(
sprintf(
"<error>Unable to open template file: %s</error>",
$templateIndexPath
),
$this->app->outputFormat
);
return false;
}
// We build these into a single string so that we can deploy this resume as a
// single file.
$cssAssetPath = join(DIRECTORY_SEPARATOR, array($templatePath, '/css'));
$cssAssets = array();
// Our PHAR deployment can't handle the GlobAsset typically used here
foreach (new \DirectoryIterator($cssAssetPath) as $fileInfo) {
if ($fileInfo->isDot() || !$fileInfo->isFile()) {
continue;
}
array_push($cssAssets, new FileAsset($fileInfo->getPathname()));
}
$css = new AssetCollection(
$cssAssets,
array(new Filter\LessphpFilter())
);
$style = $css->dump();
$templateContent = file_get_contents($templateIndexPath);
$resumeContent = file_get_contents($source);
// Process with Markdown, and then use SmartyPants to clean up punctuation.
$resumeHtml = MarkdownExtra::defaultTransform($resumeContent);
$resumeHtml = SmartyPants::defaultTransform($resumeHtml);
// We'll construct the title for the html document from the h1 and h2 tags
$simpleDom = new \simple_html_dom();
$simpleDom->load($resumeHtml);
$title = sprintf(
'%s | %s',
$simpleDom->find('h1', 0)->innertext,
$simpleDom->find('h2', 0)->innertext
);
// We'll now render the Markdown into an html file with Mustache Templates
$m = new \Mustache_Engine;
$rendered = $m->render(
$templateContent,
array(
'title' => $title,
'style' => $style,
'resume' => $resumeHtml,
'reload' => $refresh
)
);
return $rendered;
}
}
/* End of file HtmlCommand.php */

View File

@ -0,0 +1,89 @@
<?php
namespace Resume\Command;
use Resume\Command\HtmlCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class PdfCommand extends HtmlCommand
{
protected function configure()
{
$this
->setName('pdf')
->setDescription('Generate a PDF from a markdown file')
->addArgument(
'source',
InputArgument::REQUIRED,
'Source markdown document'
)
->addArgument(
'destination',
InputArgument::REQUIRED,
'Output destination folder'
)
->addOption(
'template',
't',
InputOption::VALUE_NONE,
'Which of the templates to use'
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->app = $this->getApplication();
$source = $input->getArgument('source');
$destination = trim($input->getArgument('destination'), DIRECTORY_SEPARATOR);
$template = $input->getOption('template');
$pdfSource = join(DIRECTORY_SEPARATOR, array($destination, '.tmp_pdf_source.html'));
$destFilename = join(DIRECTORY_SEPARATOR, array($destination, pathinfo($source, PATHINFO_FILENAME) . '.pdf'));
// Make sure we've got out converter available
exec('wkhtmltopdf -V', $results, $returnVal);
if ($returnVal) {
$output->writeln(
"\n<error>Error:</error> Unable to locate wkhtmltopdf.\n" .
" Please make sure that it is installed and available in " .
"your path. \n For installation help, please read: " .
"https://github.com/pdfkit/pdfkit/wiki/Installing-WKHTMLTOPDF \n\n",
$this->app->outputFormat
);
return false;
}
$rendered = $this->generateHtml($source, $template, false);
// The pdf needs some extra css rules, and so we'll add them here
// to our html document
$simpleDom = new \simple_html_dom($rendered);
$body = $simpleDom->find('body', 0);
$body->class = $body->class . ' pdf';
$rendered = (string) $simpleDom;
// Save to a temp destination for the pdf renderer to use
file_put_contents($pdfSource, $rendered);
// Process the document with wkhtmltopdf
exec('wkhtmltopdf ' . $pdfSource .' ' . $destFilename);
// Unlink the temporary file
unlink($pdfSource);
$output->writeln(
sprintf(
"Wrote pdf resume to: <info>%s</info>",
$destFilename
),
$this->app->outputFormat
);
return true;
}
}
/* End of file PdfCommand.php */

View File

@ -0,0 +1,86 @@
<?php
namespace Resume\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class SelfUpdateCommand extends Command
{
protected function configure()
{
$this
->setName('selfupdate')
->setDescription('Updates md2resume.phar to the latest version.')
->setHelp(
<<<EOT
The <info>self-update</info> command checks github for newer
versions of the command line client and if found, installs the latest.
EOT
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->app = $this->getApplication();
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
$tempFilename = dirname($localFilename) . '/' . basename($localFilename, '.phar').'-temp.phar';
if (substr($localFilename, -4) === '.php') {
throw new \Exception('You must run this from the compiled phar file.');
}
// check for permissions in local filesystem before start connection process
if (!is_writable($tempDirectory = dirname($tempFilename))) {
throw new \Exception(
'Self update failed: the "' . $tempDirectory
. '" directory used to download the temp file could not be written'
);
}
if (!is_writable($localFilename)) {
throw new \Exception(
'Self update failed: the "' . $localFilename . '" file could not be written'
);
}
$protocol = extension_loaded('openssl') ? 'https' : 'http';
$latest = trim(file_get_contents($protocol . $this->app->project->selfupdateversion, false));
if ($this->app->project->version !== $latest) {
$output->writeln(sprintf("Updating to version <info>%s</info>.", $latest));
$remoteFilename = $protocol . $this->app->project->selfupdatepath;
$phar = file_get_contents($remoteFilename);
file_put_contents($tempFilename, $phar);
if (!file_exists($tempFilename)) {
$output->writeln('<error>The download of the new version failed for an unexpected reason</error>');
return 1;
}
try {
@chmod($tempFilename, 0777 & ~umask());
// test the phar validity
$phar = new \Phar($tempFilename);
// free the variable to unlock the file
unset($phar);
rename($tempFilename, $localFilename);
} catch (\Exception $e) {
@unlink($tempFilename);
if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) {
throw $e;
}
$output->writeln('<error>The download is corrupted ('.$e->getMessage().').</error>');
$output->writeln('<error>Please re-run the self-update command to try again.</error>');
}
} else {
$output->writeln("<info>You are using the latest version.</info>");
}
}
}
/* End of file SelfUpdateCommand.php */

View File

@ -0,0 +1,35 @@
<?php
namespace Resume\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class TemplatesCommand extends Command
{
protected function configure()
{
$this
->setName('templates')
->setDescription('List available templates');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->app = $this->getApplication();
$tplData = array('templates' => array());
foreach (glob($this->app->templatePath . '/*', GLOB_ONLYDIR) as $dir) {
$tplData['templates'][] = (object) array(
'name' => basename($dir),
'description' => file_exists($dir . '/description.txt')
? trim(file_get_contents($dir . '/description.txt'))
: 'No description available'
);
}
$template = $this->app->twig->loadTemplate('templates.twig');
$view = $template->render($tplData);
$output->write($view, true, $this->app->outputFormat);
}
}
/* End of file TemplatesCommand.php */

View File

@ -0,0 +1,24 @@
<?php
namespace Resume\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class VersionCommand extends Command
{
protected function configure()
{
$this
->setName('version')
->setDescription('Show current version information');
}
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

@ -0,0 +1,9 @@
————————————————————————————————————————————————————————————————————————————————
<heading> Available Templates </heading>
————————————————————————————————————————————————————————————————————————————————
{% for template in templates %}
{{template.name|style("info")|pad(15, "left")}}{{template.description}}
{% endfor %}
{% if templates|length == 0%}
<notice>There are no templates available</notice>
{% endif %}

View File

@ -0,0 +1 @@
Blocky and bold colors

View File

@ -0,0 +1 @@
Modern and clean layout (default)

View File

@ -0,0 +1 @@
Unstyled, useful as a base for your own templates

22
tests/ListCommandTest.php Normal file
View File

@ -0,0 +1,22 @@
<?php
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
class ListCommandTest extends \ResumeTest
{
public function testExecute()
{
$command = $this->console->find('list');
$commandTester = new CommandTester($command);
$commandTester->execute(array(
'command' => $command->getName()
));
$this->assertRegExp('/Available commands/', $commandTester->getDisplay());
$this->assertRegExp('/Options/', $commandTester->getDisplay());
$this->assertRegExp('/list/', $commandTester->getDisplay());
}
}
/* End of file ListCommandTest.php */

View File

@ -0,0 +1,20 @@
<?php
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
class VersionCommandTest extends \ResumeTest
{
public function testExecute()
{
$command = $this->console->find('version');
$commandTester = new CommandTester($command);
$commandTester->execute(array(
'command' => $command->getName()
));
$this->assertEquals($this->console->project->version, trim($commandTester->getDisplay()));
}
}
/* End of file VersionCommandTest.php */

23
tests/bootstrap.php Normal file
View File

@ -0,0 +1,23 @@
<?php
$app = require __DIR__ . '/../vendor/autoload.php';
$app->add('Resume', __DIR__ . '/../src');
use Resume\Cli\Resume;
class ResumeTest extends \PHPUnit_Framework_TestCase
{
public $console;
public function setUp()
{
$templatePath = realpath(__DIR__ . '/../templates/');
$consoleTemplatePath = realpath(__DIR__ . '/../src/Resume/Templates');
$project = json_decode(file_get_contents(__DIR__ . '/../composer.json'));
$this->console = new Resume();
$this->console->initialize($templatePath, $consoleTemplatePath, $project);
}
}
/* End file bootstrap.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() { }
}

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