Put your assets into the pipe and smoke them.

Overview

Pipe

Put your assets into the pipe and smoke them.

Pipe is an asset pipeline in the spirit of Sprockets. It's meant as the practical way for managing assets. It aims to provide a useful out of the box setup for managing assets and support for common preprocessor languages found in the web environment, like CoffeeScript or LESS.

What Pipe provides for you:

  • Out of the box support for Less and CoffeeScript
  • Integrated Dependency Managment.
  • Support for multiple asset load paths, which allows you to untie your application's libraries from your vendor libraries.
  • Tries to take the pain out of asset deployment, by being designed for dealing with cache busting and compression.

Build Status

Install

Get composer (if you haven't):

wget http://getcomposer.org/composer.phar

Then add this to a composer.json in your project's root:

{
    "require": {
        "chh/pipe": "*"
    }
}

Now install:

php composer.phar install

Getting Started

Environment

First you need an instance of Pipe\Environment. The environment holds your load paths, is used to retrieve assets and maps processors/engines to your assets.

The environment consists of multiple load paths. When retrieving an asset, it's looked up in every directory of the load paths. This way you can split your assets up in multiple directories, for example vendor_assets and assets.

To add some load paths, use the appendPath method. Paths can be prepended by the prependPath method. But use this carefully, because you can override assets this way.

<?php
use Pipe\Environment;

$env = new Environment;
$env->appendPath("assets");
$env->appendPath("vendor_assets");

Assets are retrieved by accessing an index of the environment instance, or by calling the find method.

The find method returns either null when no asset was found or an asset instance.

<?php

$asset = $env["js/application.js"];
# equal to:
$asset = $env->find("js/application.js");

To get the asset's processed body, use the getBody method.

<?php

echo $asset->getBody();

You can get the asset's last modified timestamp with the getLastModified method.

Dumping an Asset to a File

To dump an asset to a file, use the write method.

The write method takes an array of options:

  • dir (string): The directory is the prefix of the file. A hash of the asset's contents is automatically included in the resulting filename.
  • include_digest (bool): Should the SHA1 hash of the asset's contents be included in the filename?
  • compress (bool): Compresses the contents with GZIP and writes it with an .gz extension.

Enabling Compression

You can turn on compression by setting the js_compressor and css_compressor config keys, or by calling setJsCompressor or setCssCompressor on an Environment instance.

Supported Javascript Compressors:

  • uglify_js, uses the popular Uglify JS compressor built for NodeJS. Install with npm -g install uglify-js.
  • yuglify_js, Compressor built upon Uglify JS, and behaves like YUI compressor. Install with npm -g install yuglify.

Supported CSS Compressors:

  • yuglify_css, uses the Yuglify compressor's ability to compress CSS using CSSmin. Requires the yuglify NPM package.

Example:

<?php

$env = new Environment;
$env->appendPath("assets/stylesheets");
$env->appendPath("assets/vendor/stylesheets");
$env->appendPath("assets/javascripts");
$env->appendPath("assets/vendor/javascripts");

$env->setJsCompressor('yuglify_js');
$env->setCssCompressor('yuglify_css');

# Compressors are Bundle Processors. Bundle Processors are only run on Bundled Assets.
# Pass 'bundled' => true to get a Bundled Asset.
$asset = $env->find('application.js', ['bundled' => true]);

echo $asset->getBody();

Directives

Each file with content type application/javascript or text/css is processed by the DirectiveProcessor. The DirectiveProcessor parses the head of these files for special comments starting with an equals sign.

/* CSS
 *= require foo.css
 *= depend_on bar.css
 */

# CoffeeScript
#= require foo.coffee

// Javascript
//= require foo.js

The arguments for each directive are split by the Bourne Shell's rules. This means you have to quote arguments which contain spaces with either single or double quotes.

//= require "some name with spaces.js"

require

Usage:

require <path>

The require directive takes an asset path as argument, processes the asset and puts the dependency's contents before the asset's contents.

The path can also start with ./, which skips the load path for the path resolution and looks up the file in the same path as the current asset.

depend_on

Usage:

depend_on <path>

Defines that the path is a dependency of the current asset, but does not process anything. Assets defined this way get considered when the last modified time is calculated, but the contents get not prepended.

require_tree

Usage:

require_tree <path>

Requires all files found in the directory specified by path.

For example, if you have a directory for all individual widgets and a widget base prototype, then you could require_tree the widgets/ directory. This way every developer can just drop a file into the widgets/ directory without having to maintain a massive list of individual assets.

// index.js
//= require ./widget_base
//= require_tree ./widgets

Engines

Engines are used to process assets before they're dumped. Each engine is mapped to one or more file extension (e.g. CoffeeScript to .coffee). Each asset can be processed by one or more engines. Which engines are used on the asset and their order is determined by the asset's file extensions.

For example, to process an asset first by the PHP engine and then by the LESS compiler, give the asset the .less.php suffix.

Here's a list of the engines provided by default and their mapping to file extensions:

Engine Extensions Requirements
CoffeeScript .coffee coffee — install with npm install -g coffee-script
LESS .less lessc — install with npm install -g less
PHP .php, .phtml
Mustache .mustache Add phly/mustache package
Markdown .markdown, .md Add dflydev/markdown package
Twig .twig Add twig/twig package
TypeScript .ts tsc — install with npm install -g typescript

Under the hood, Pipe Engines are meta-template templates. Look at its README for more information on building your own engines.

To add an engine class to Pipe, use the environment's registerEngine method, which takes the engine's class name as first argument and an array of extensions as second argument.

Serving Assets dynamically.

Pipe includes a Pipe\Server which is able to serve assets dynamically via HTTP. The server is designed to be called in .php file, served via mod_php or FastCGI.

To use the dynamic asset server, you've to additionally require symfony/http-foundation. The require section of your composer.json should look like this:

{
    "require": {
        "chh/pipe": "*@dev",
        "symfony/http-foundation": "*"
    }
}

The server's constructor expects an environment as only argument. You can either construct the environment from scratch or use the Config class.

Put this in a file named assets.php:

<?php

use Pipe\Server,
    Pipe\Environment,
    Symfony\Component\HttpFoundation\Request;

$env = new Environment;
$env->appendPath("vendor_assets");
$env->appendPath("assets");

$server = new Server($env);
$server->dispatch(Request::createFromGlobals())
       ->send();

The server resolves all request URIs relative to the environment's load path. So to render the Javascript file js/index.js you would request the URI /assets.php/js/index.js.

The server also applies some conditional caching via Last-Modified and If-Not-Modified-Since HTTP headers. Should a change to a dependency not be instantly visible, try to make a hard refresh in your browser or clear your browser's cache.

Preparing Assets for Production Deployment

It's a good idea to compile assets in a way that they don't need the runtime support of Pipe. The Pipe\Manifest class is responsible for just that.

The Manifest is used to compile assets and writes a JSON encoded file which maps the logical paths (which the app knows anyway) to the paths including the digest (which the app can't know in advance).

To add a file to the manifest, call the manifest's compile method:

<?php

$env = new \Pipe\Environment;
$env->appendPath('assets/javascripts');

$manifest = new \Pipe\Manifest($env, 'build/assets/manifest.json');
$manifest->compile('index.js');

This creates the index-<SHA1 digest>.js file, and a manifest.json both in the build/assets directory.

This file looks a bit like this:

{
    "assets": {
        "index.js": "index-0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33.js"
    }
}

Then deploy everything located in build/assets, e.g. to some CDN or static file server.

An app running in a production environment could use the manifest like this, to create links to the assets:

<?php

# Better cache this, but omitted for brevity
$manifest = json_decode(file_get_contents('/path/to/manifest.json'), true);

# Path where the contents of "build/assets" are deployed.
# Could be a path to a CDN.
$prefix = "/assets";

printf('<script type="text/javascript" src="%s/%s"></script>', $prefix, $manifest['assets']['index.js']);

License

The MIT License

Copyright (c) 2012 Christoph Hochstrasser

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.

Comments
  • Updates path to autoload.php

    Updates path to autoload.php

    When loaded with composer, bin symlinks are setup in vendor/bin (where vendor can be changed based on composers config.) Thus, I think we want to load autoload.php relative to the location of the bin symlink. We also don't want to reference the vendor folder itself.

    opened by svperfecta 5
  • Bug with compression?

    Bug with compression?

    Fatal error: Uncaught exception 'UnexpectedValueException' with message 'Error while compressing "./css/application.css": sh: : command not found ' in /Users/tk/Desktop/web/purpleleaves1_6/pipe/lib/Pipe/Compressor/BaseYuglifyCompressor.php:32 Stack trace: #0 /Users/tk/Desktop/web/purpleleaves1_6/pipe/lib/Pipe/Compressor/YuglifyCss.php(14): Pipe\Compressor\BaseYuglifyCompressor->compress('#dashboard_wrap...', 'css') #1 /Users/tk/Desktop/web/purpleleaves1_6/pipe/lib/Pipe/Context.php(76): Pipe\Compressor\YuglifyCss->render(Object(Pipe\Context)) #2 /Users/tk/Desktop/web/purpleleaves1_6/pipe/lib/Pipe/BundledAsset.php(18): Pipe\Context->evaluate('./css/applicati...', Array) #3 /Users/tk/Desktop/web/purpleleaves1_6/templates/purpleleaves/css-assets.php(29): Pipe\BundledAsset->getBody() #4 {main} thrown in /Users/tk/Desktop/web/purpleleaves1_6/pipe/lib/Pipe/Compressor/BaseYuglifyCompressor.php on line 32

    my code:

    appendPath("./"); $env->appendPath("./css"); $env->appendPath("./css/tk"); $env->setJsCompressor('yuglify_js'); $env->setCssCompressor('yuglify_css'); # application holds directive for several .css files. works fine without compression. $assets[] = $env->find('application.css', ['bundled' => true]); foreach($assets as $asset) { echo $asset->getBody(); #$asset->write(array("dir" => './build/css', 'include_digest' => true, 'compress' => true)); }
    opened by thekar 4
  • Fixes CSS preprocessor

    Fixes CSS preprocessor

    Hey @chh - When playing with PIPE today I found I couldn't get CSS directives to work at all. (I'm on PHP 5.3.16 if it matters)

    The culprit was a mistake in a regular expression that accounts for multi-line statements. I fixed that (small change) and found that the resulting header had a bunch of blank lines in it. I'm stripping them out too. Finally, I added a new unit test to cover the bug.

    Cheers! Brian

    opened by svperfecta 3
  • Invalid Content-Type

    Invalid Content-Type

    I'm using the Server module using the example on the PIPE homepage. I'm having a strange problem. No matter what I do I'm getting a content type back of text/html. I'm using PHP 5.3.16 (installed via homebrew-php), NGINX, and FPM.

    First, the file:

    $ curl http://localhost:8081/assets.php/init.css                                                                                               
    .test{}
    

    And here are the headers:

    $ curl -I http://localhost:8081/assets.php/init.css
    HTTP/1.1 200 OK                                                                                   
    Server: nginx/1.2.3                                                                               
    Date: Sun, 09 Sep 2012 04:24:56 GMT                                                               
    Content-Type: text/html                                                                           
    Connection: keep-alive                                                                            
    X-Powered-By: PHP/5.3.16 
    

    Here's my NGINX config (pretty vanilla):

    # Apollo-JS
    server {
        listen       8081;
        server_name  localhost;
        root   /my/home/folder/web;
    
        location / {
            try_files $uri /index.html;
        }
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ ^/.*\.php(/|$) {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_split_path_info ^(.+\.php)(/.*)$;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME   $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO         $fastcgi_path_info;
            fastcgi_param HTTPS             off;
        }
    }
    

    If I echo the response object in my assets.php file I get:

    HTTP/1.0 200 OK                                                                                
    Cache-Control: public                                                                          
    Content-Type:  text/css; charset=UTF-8                                                         
    Date:          Sun, 09 Sep 2012 04:32:21 GMT                                                   
    Last-Modified: Sun, 09 Sep 2012 00:53:32 GMT    
    

    I can't see anything wrong anywhere. NGINX isn't touching the headers, PHP-FPM doesn't seem to have any option to manipulate them even if I wanted to, and PHP seems to be sending them correctly. I was wondering if I was missing something obvious?

    opened by svperfecta 2
  • Document Mustache Templates

    Document Mustache Templates

    Hi @css - Could you explain how you're intending that I use mustache templates with Pipe?

    I assumed I could do something like this:

    //= require file.js //= require template.mustache

    This parses, but doesn't do what I expected. I assumed it would somehow wrap my template files up so that they are accessible via my other JS files. Instead it just puts the template code inline in my Javascript (which doesn't parse). What should I be doing here?

    Thanks!

    opened by svperfecta 2
  • Serves singular assets

    Serves singular assets

    Hi CHH - This change allows PIPE to serve assets that reference zero child elements. The main use case here is that you may want to keep assets that require compilation and those that require no compilation in the same folder structure, allowing PIPE to serve all of them.

    Also, I fixed the broken unit tests.

    opened by svperfecta 1
  • Create a Silex Service Provider

    Create a Silex Service Provider

    Silex + Pipe could be a crack team for creating client side apps. Would be great if using them together would be painless and fun.

    • Create a Silex Service Provider
    • Add a helper method, which allows to create links to assets
    • Provide a route which uses Pipe's server to render assets dynamically

    Discuss:

    • Provide the Silex integration as its own package?
    opened by CHH 1
  • Move asset compilation features more to Pipe

    Move asset compilation features more to Pipe

    • Decouple the asset compilation from Bob
    • Add a class for generation of Manifest files
    • Add an Environment, which acts as a proxy to a Manifest file.
    • Dump all assets registered in the Manifest with one method call.
    opened by CHH 1
  • Write Filters

    Write Filters

    Add a system for attaching filters to a Content Type which get run when an asset's ->write() method was called.

    What's this good for?

    • Invoking processors like bless which modify the resulting file and write additional files, but need to know about the final file name (with digest).
    • Implement compression algorithms, like GZIP, as filter and don't hardcode them into the ->write() method.
    opened by CHH 0
  • Add Pipe Middleware

    Add Pipe Middleware

    Add a Pipe Middleware which captures requests to a certain prefix (e.g. by default /_pipe) and responds with the asset just like Pipe\Server. This Middleware implements the HttpKernelInterface and should be usable within Middleware pipelines.

    Benefits:

    • Barebones integration with Symfony possible by decorating Symfony's HttpKernel, like the HttpCache does.
    • Common class between the Silex and Symfony integrations (eventually)
    opened by CHH 0
  • Add logging to Manifest compilation

    Add logging to Manifest compilation

    Add some simple, Monolog powered logging to the Manifest when the compile method is called, so e.g. users of command line tools can see the progress of compilation.

    Tasks:

    • [ ] Add Monolog as dependency
    • [ ] Log compile times of each asset given in compile
    opened by CHH 0
  • Ambiguity between directory paths and asset files

    Ambiguity between directory paths and asset files

    The following test case results in an error (due to the ambiguity of the path reference?):

    <?php
    
    namespace Pipe\Test;
    
    use Pipe\DirectiveProcessor,
        Pipe\Context,
        Pipe\Environment;
    
    class RecursiveDirectiveProcessorTest extends \PHPUnit_Framework_TestCase
    {
        var $env;
    
        function setUp()
        {
            $this->env = new Environment;
            $this->env->appendPath(__DIR__.'/fixtures/directive_processor');
        }
    
        function testRequireTreeIncludesNestedAssets()
        {
            $asset = $this->env->find('tree.js');
        }
    }
    

    Log:

    There was 1 error:
    
    1) Pipe\Test\RecursiveDirectiveProcessorTest::testRequireTreeIncludesNestedAssets
    UnexpectedValueException: 
    Asset /pipe/tests/Pipe/Test/fixtures/directive_processor/module/ui not found
    
    opened by maetl 0
  • Caching

    Caching

    If you've many dependencies which go through some sort of preprocessor, dynamic generation can get really slow. This is because currently, if one file in the dependency tree is changed then everything is processed again – also files which were not changed.

    Tasks:

    • [ ] Add an EnvironmentInterface
    • [ ] Add a CachedEnvironment class which takes an EnvironmentInterface and a Cache (use Doctrine Cache?)
    • [ ] Cache processed bodies of assets
    • [ ] Cache asset instances?
    opened by CHH 0
  • Add Documentation / Update Readme

    Add Documentation / Update Readme

    The documentation standalone is not understandable without hours of DIY.

    Main issues:

    How to add multiple files, concatenate, minify, compress? Give examples to ALL functionalities. E.g. function "write": it says it takes 3 params. but you dont just need 3 params, but ONE param as array with 3 values. An example just solves this. Directives are totally uncomprehensive. Example?

    Keep on the great work.

    opened by thekar 0
Owner
Christoph Hochstrasser
Christoph Hochstrasser
The Asset component manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files.

Asset Component The Asset component manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files. Res

Symfony 2.9k Jan 5, 2023
A simple but powerful API for processing & compiling assets built around Webpack

Webpack Encore: A Simple & Powerful Webpack API Webpack Encore is a simpler way to integrate Webpack into your application. It wraps Webpack, giving y

Symfony 2.1k Jan 5, 2023
A Parser for CSS Files written in PHP. Allows extraction of CSS files into a data structure, manipulation of said structure and output as (optimized) CSS

PHP CSS Parser A Parser for CSS Files written in PHP. Allows extraction of CSS files into a data structure, manipulation of said structure and output

Raphael Schweikert 1.6k Jan 5, 2023
An asset compression plugin for CakePHP. Provides file concatenation and a flexible filter system for preprocessing and minification.

Asset Compress Asset Compress is CakePHP plugin for helping reduce the number of requests, and optimizing the remaining requests your application make

Mark Story 367 Jul 20, 2022
GLPI is a Free Asset and IT Management Software package, Data center management, ITIL Service Desk, licenses tracking and software auditing.

GLPI stands for Gestionnaire Libre de Parc Informatique is a Free Asset and IT Management Software package, that provides ITIL Service Desk features, licenses tracking and software auditing.

GLPI 2.9k Jan 2, 2023
Combines. minifies, and serves CSS or Javascript files

Welcome to Minify! Minify is an HTTP server for JS and CSS assets. It compresses and combines files and serves it with appropriate headers, allowing c

Steve Clay 3k Jan 7, 2023
NotrinosERP is an open source, web-based enterprise management system that written in PHP and MySql.

NotrinosERP is an open source, web-based enterprise management system that written in PHP and MySql. NotrinosERP contains all the required modules for running any small to medium size businesses. It supports multi users, multi currencies, multi languages

Phương 56 Dec 20, 2022
Commenting program developed with Html & Css & Php JavaScript Languages ​​and MySql

CommentSystem [BETA] Commenting program developed with Html & Css & Php JavaScript Languages and MySql How does it work ? After you set up your Databa

Azad 0 May 19, 2022
A fast Javascript minifier that removes unnecessary whitespace and comments

js-minify A fast Javascript minifier that removes unnecessary whitespace and comments Installation If you are using Composer, use composer require gar

Patrick van Bergen 5 Aug 19, 2022
Middleware to minify the Html, CSS and Javascript content using wyrihaximus/compress

middlewares/minifier Middleware to minify the Html, CSS and Javascript content using wyrihaximus/compress and the following compressors by default: wy

Middlewares 15 Oct 25, 2022
Safely break down arrays or objects, and put them back together in new shapes.

traverse/reshape traverse() and reshape() are companion functions that safely break down arrays or objects and put them back together in new shapes. t

Alley Interactive 2 Aug 4, 2022
Pimcore Localized Assets - localize your assets with no duplicating files

Localized assets in Pimcore Pimcore Bundle to localize your assets with same file. Installation composer require lemonmind/pimcore-localized-assets bi

LemonMind.com 7 Aug 31, 2022
Assets Manager for "Vitewind" Theme, will inject CSS and JS assets for "Vitewind" theme to work properly with viteJS in development and production

Vitewind Manager plugin ?? Windi CSS and ⚡️ Vite, for ?? OctoberCMS & ❄️ WinterCMS Introduction This is a helper plugin for ?? Vitewind theme, don't i

Adil Chehabi 4 May 29, 2022
The only way to implement the pipe operator in PHP.

Pipe Operator in PHP Introduction This package is based on the pipe operator RFC by Sara Golemon and Marcelo Camargo (2016), who explains the problem

BoostPHP 21 Nov 12, 2022
Simple HTTP smoke testing for your Symfony application

Shopsys HTTP Smoke Testing This package enables you to do simple HTTP smoke testing of your Symfony application. Basically, it generates a HTTP reques

Shopsys 65 Feb 3, 2022
Supper quick use Aliyun OSS or Tencent COS or Qiniu Koa to get、put、delete Object.

An SDK integrating Alibaba cloud, Tencent cloud and qiniu cloud object storage

null 6 Sep 26, 2021
💨 Smoke testing tool written in PHP

Cigar A smoke testing tool inspired by symm/vape Similar tools include: Blackfire Player Installation Install via composer: composer require brunty/ci

Matt Brunt 156 Oct 14, 2022
All In 1 Spam Tool For Termux Users Subscribe Us (Noob Hackers) some shit heads are trying to abuse this script so don't worry about them ...let them hallucinate ...but you are free to use this script

ABOUT TOOL : SPAMX is a all in one Bombing+Spam tool from this tool you can send anonymous messages to your target without showing your real number an

N17R0 449 Jan 7, 2023
Package to parse DNA kit files, and import them into Laravel

Package to parse DNA kit files, and import them into Laravel

Family Tree 365 4 Aug 31, 2022