caseyamcl/easyasset

此包已被废弃,不再维护。作者建议使用symfony/webpack-encore-bundle包。

适用于PHP 5.5或更高版本的简单资产工具

0.3.2 2017-08-29 18:03 UTC

This package is auto-updated.

Last update: 2020-08-21 14:07:23 UTC


README

一个库,可让您在使用应用程序中的资产(CSS、JS等)时更加方便。

主要功能

  • 编译LESS、SASS/SCSS、JS或任何Assetic资产(或资产集合)
  • 递归编译(合并)目录中的资产文件
  • 包括Silex提供程序,但将与任何框架、路由器或HTTP库一起工作
  • 包括'强制编译'选项,允许您在每次页面加载时编译资产
  • 单元测试,完全符合PSR-4/PSR-2规范

为什么?

大多数人将在开发期间使用外部工具编译LESS、SASS和JS,例如IDE或GUI/CLI工具。然而,有时您可能希望应用程序本身能够以透明的方式编译这些资源。这样,资产编译在任何环境中都保持一致。EasyAsset就是这样做的。

我建立这个不是要取代功能齐全的Asset库,如Assetic,而是通过添加一个常见用例的API来补充它。但是,EasyAsset也可以作为一个独立工具使用。

安装

此包需要PHP >= 5.4

请将以下内容包含在您的composer.json文件中

require {
    ...
    "caseyamcl/easyasset": "@stable"
    ...
}

使用

EasyAsset库可以做一些不同的事情

  1. 通过HTTP从多个路径提供资产,发送正确的MIME类型和HTTP头。这使得您(可选)将资产保留在Web文档根目录之外。
  2. 即时编译LESS、SASS、JS或任何Assetic资产。
  3. 将编译后的资产写入输出文件。

使用AssetLoader

EasyAsset的核心是EasyAsset\AssetContentLoader类。此类流式传输静态(非编译)和/或编译后的资产。您应该传递资产所在位置(或将被编译到的)的文件路径

$loader = new AssetContentLoader(['/path/to/assets']);

此基本用法允许您加载静态资产,但不能编译任何资产。例如,如果您有一个位于/path/to/assets/img.jpg的图像,您可以加载内容

// Returns TRUE if the file exists, otherwise false
$loader->exists('img.jpg');

// Get a callable function (closure) that will echo the output of the 'img.jpg' file:
$output = $loader->load('img.jpg');

// And invoke the output to send the content to php://output
$output->__invoke();

如果您希望它搜索多个路径,可以向构造函数传递多个资产路径

$loader = new AssetContentLoader(['/path/to/assets', '/another/path']);

// Will search both paths, in the order they are specified, until a match is found
$loader->exists('img.jpg');

如果资产不存在,将抛出\EasyAsset\Exception\AssetNotExistsException异常,您可以在框架中捕获它并将其转换为404错误,或进行其他处理。

// Throws an AssetNotExistsException
$loader->load('does-not-exist.jpg');

使用编译后的资产(LESS、SASS、JS、Assetic..)与AssetLoader

如果静态版本已存在或您指定它应该每次都编译,无论是否存在静态文件,AssetContentLoader还将编译您想要的任何资产。

编译后的资产应指定输出文件名(例如style.cssjs/scripts.js),并指示提供原始内容的类。此类必须是\EasyAsset\CompiledAssetInterface的实例。

首先,创建一个 EasyAsset\CompiledAssetsCollection

$compiledAssets = new CompiledAssetsCollection();

然后,添加编译后的资源

$compiledAssets->add('styles.css', new EasyAsset\CompiledAsset\LessCompiledAsset('/path/to/less'));
$compiledAssets->add('script.js',  new EasyAsset\CompiledAsset\JsCompiledAsset('/path/to/js-source'));

如果您想使用编译后的资源,在实例化 AssetContentLoader 类时必须传递 CompiledAssetsCollection

$loader = new AssetContentLoader(['/path/to/assets'], $compiledAssets);

现在,当 AssetContentLoader 被指示查找 styles.css 时,它将首先检查是否有名为该名的文件存在于某个路径中,如果没有,它将使用 CompiledAsset 类构建内容。

// If the 'styles.css' exists in any specified asset path, use that; otherwise will compile
$output = $loader->load('styles.css');

// prints the raw CSS content
$output->__invoke();

您也可以通过将 true 作为 AssetContentLoader::load 的第三个参数传递给 Loader 来强制其编译资源,即使存在文件也是如此。这在进行开发/设计时非常有用。

$output = $loader->load('styles.css', null, true):

内置编译后的资源

EasyAsset 包含四个内置编译器

  • EasyAsset\CompiledAsset\LessCompiledAsset - 使用 oyejorge/less.php 库编译 LESS
  • EasyAsset\CompiledAsset\JsCompiledAsset - 使用 patchwork/jsqueeze 库编译和压缩 JS
  • EasyAsset\CompiledAsset\SassCompiledAsset - 使用 leafo/scssphp 库编译和压缩 SASS/SCSS
  • EasyAsset\CompiledAsset\AsseticAsset - 包装任何 Assetic\Asset\AssetInterface 实例,以便您可以使用 EasyAsset

此外,LESS、JS 和 SASS/SCSS 编译器将递归地合并和压缩您指定的路径中所有相应的文件类型。

例如,假设您有以下 JS 文件

/asset_source/js
    /01-jquery
        /01-jquery.js
        /02-jquery-ui.min.js
    /02-vendor
        /01-chosen.js
        /02-mappify.js
    /03-local
        /scripts.js

如果您将 /asset_source/js 路径传递给 JsCompiledAsset 类,它将按字母顺序按完整路径名合并所有文件并压缩它们。这使得组织和最小化资源 HTTP 请求变得非常容易。

您也可以将单个文件名传递给编译器的构造函数,例如

$lessCompiler = new EasyAsset\CompiledAsset\LessCompiledAsset('/path/to/less/01-main.less');

Assetic Asset 类允许您使用任何 Assetic Asset 作为编译后的资源。例如

$compiledAssets->add('image.png', new Assetic\Asset\FileAsset('asset_src/image.png', [new AsseticPngFilter()]);

创建自己的编译后资源类型

如果内置的编译后资源类型都不符合您的需求,您可以通过实现 EasyAsset\CompiledAssetInterface 来创建自己的。

如果您的资源能够递归地合并文件,您可以使用 EasyAsset\RecursiveDirParserTrait。例如

class MyCompiledAsset implements CompiledAssetInterface
{
     // .. code here..
     
     use RecursiveDirParserTrait;

     public function compile($outStream)
     {
         // ..code here..
     
         // getCombinedFiles comes from the trait
         $combinedFiles = $this->getCombinedFiles($this->pathToSource);
         
         // ..or you can use getFileIterator(), which accepts a directory or file path
         // and returns an iterator with all files listed in alphabetical order by path
         $allAssetSourceFiles = $this->getFileIterator($this->pathToSource);
         
         // ..code here..
     }   
}

通过 HTTP 提供资源

EasyAsset 包含一个抽象控制器类,用于通过 PHP HTTP 框架提供资源:EasyAsset\AssetController。如果您正在使用使用 Symfony HttpKernel 组件的框架,您可以使用包含的 EasyAsset\Provider\Symfony\AssetController 控制器。如果不使用,您可以在自己的类中扩展 EasyAsset\AssetController。例如

/**
 * A simple Asset Controller that uses PHP-built in functions to deliver a response
 */
class MyAssetController extends EasyAsset\AssetController
{
    /**
     * This method prints out the response directly.. Alternatively, your
     * controller could return some kind of response object, which would be handled
     * by your framework.
     */
    protected function sendContentResponse(\Closure $contentCallback, $mimeType)
    {
        header("HTTP/1.0 200 OK");
        header("Content-Type: " . $mimeType);
        $contentCallback->__invoke(); // prints the raw asset content
    }
           
    /**
     * {@inheritdoc}
     */
    protected function sendNotFoundResponse($path)
    {
        header("HTTP/1.0 404 Not Found");
        header("Content-Type: text/plain");
        echo "Asset at path: {$path} not found";
    }
}

使用您的类很容易

$assetController = new MyAssetController($assetLoader);

// You'll probably be using a more sophisticated router than this...
$route = $_SERVER['REQUEST_URI']
if (substr($route, 0, strlen('asset')) == 'asset') {

    // Get the asset path from the URI; again, this is crude for example purposes..
    $assetPath = ltrim(substr($route, strlen('asset')), '/');

    // Your implementation of the AssetController class may return a value or simply echo output,
    // like this example does
    $assetController->loadAction($assetPath);
    
    exit();
}

如果您可以使用内置的 Symfony 控制器,那么使用起来甚至更简单。例如,在 Silex 中(注意:详细的 Silex 集成文档如下)

$controller = new EasyAsset\Provider\Symfony\AssetController($loader);
$app->get('assets/{path}', [$controller, 'loadAction']);

将资源写入输出文件

EasyAsset 包含一个类,用于将编译后的资源写入您选择的路径。这可能在构建过程中、部署过程中、按需发生,或者您可以配置您的应用程序在每次编译时都写入它们。

$writer = new AssetFileWriter('/path/to/assets');

// Write the whole collection of assets
$writer->writeAssetCollection($compiledAssets);

// Write a single asset
$writer->write('style.css', $lessCompiledAsset);

如果您的项目使用 Symfony Console 组件,您可以利用内置的命令来写入资源

use EasyAsset\Provider\AssetWriterCommand;

$mySymfonyConsoleApp->add(new AssetWriterCommand($compiledAssets, $writer));

然后,在命令行中

# Write assets
$ app/console assets:compile

# Or, override the default directory to write to:
$ app/console assets:compile /some/other/asset/path

与 Silex 一起使用

如果您使用Silex,可以使用EasyAsset\Provider\Silex\AssetServiceProvider

参数

  • assets.paths - 必填 - 一个数组,包含常规资产(图片等)的实际系统路径以及应该编译到的地方。您可以传递任意数量的路径;在检索资产时,将按顺序搜索这些路径。
  • assets.compiled - 资产编译器关联数组。键是编译后资产输出文件位置的相对URL;值是编译器类。下面是示例。
  • assets.force_compile - 真值/假值;如果您想在每次加载资产时都让EasyAsset编译它们,请将其设置为TRUE
    默认值为$app['debug']的值。
  • assets.write_path - 如果您的应用程序将写入资产,请指定文件应写入的路径。默认值为assets.paths中的第一个值。
  • assets.write_on_compile - 真值/假值;如果您指定了assets.write_path,您可以配置Silex在每次编译资产时将它们输出到文件。默认值为false

服务

  • assets.loader - 资产加载类;您通常不需要直接使用此功能
  • assets.controller - 资产控制器。期望URI参数{path}。例如:/assets/{path}
  • assets.writer - 资产写入器。将编译后的资产写入文件系统
  • assets.command - 资产写入器控制台命令。提供了一个Symfony控制台命令,用于将资产写入文件系统

示例Bootstrap代码

use EasyAsset\Provider\Silex\AssetServiceProvider;
use EasyAsset\CompiledAsset;

$app->register(new AssetServiceProvider(), array(
    'assets.paths'            => ['/path/to/assets', '/another/asset/path'],
    'assets.write_path'       => '/path/to/assets' // you can omit this if you want to use the first value from 'assets.path'
    'assets.force_compile'    => false, // you may want to use TRUE for development
    'assets.write_on_compile' => true, // defaults to FALSE 
    'assets.compilers         => [
        'style.css',   new CompiledAsset\LessCompiledAsset('/path/to/less'),
        'script.js',   new CompiledAsset\JsCompiledAsset('/path/to/js'),
        'fancypng.png, new CompiledAsset\AsseticAsset($someAsseticAsset)
    ];
));

示例控制器代码

$app->get('/asset/{path}, [$app['assets.controller'], 'loadAction']); 

如果您在应用程序中使用UrlGeneratorTwig,您可以在模板中轻松创建资产URL,通过将路由绑定到名称

$app->get('/asset/{path}, [$app['assets.controller'], 'loadAction'])->bind('asset'); 

然后,在您的模板中,使用url()函数创建资产URL。如果资产是编译后的资产,且输出文件不存在,应用程序将透明地编译它。

<head>
   {# ... #}
   <link rel='stylesheet' href="{{ url('asset', {'path': 'style.css'}); }}" />
   <script src="{{ url('asset', {'path': 'script.js'}); }}"></script>
   {# ... #}
</head>

贡献

欢迎贡献!我特别感兴趣的是包含除了Symfony/Silex之外提供者的拉取请求。

有关详细信息,请参阅<CONTRIBUTING.md>文件。

许可证

MIT许可证(MIT)。请参阅许可证文件获取更多信息。