跳转到内容

资源管理


概览

Phalcon\Assets是一个组件,用于在 Web 应用程序中管理静态资源,如 CSS 样式表或 JavaScript 库。

Phalcon\Assets\Manager是注册和使用应用程序中资源的关键组件。如果您使用的是Phalcon\Di\FactoryDefault容器,则资源管理器已经注册,并且可以使用assets键从您的 DI 容器访问它。

<?php

use Phalcon\Di\FactoryDefault();

$container = new FactoryDefault();
$manager   = $container->get('assets')

另外,您可以手动注册Phalcon\Assets\Manager中更改了Phalcon\Di\Di:

<?php

use Phalcon\Assets\Manager;
use Phalcon\Di\Di();
use Phalcon\Html\TagFactory();

$container  = new Di();
$tagFactory = new TagFactory();

$container->set(
    'assets',
    function () use ($tagFactory) {
        return new Manager($tagFactory);
    }
)

如果你使用的是Phalcon\Di\FactoryDefaultPhalcon\Html\TagFactory已经作为名为tag的服务注册到容器中,并自动注入到Phalcon\Assets\Manager的构造函数中。这确保了对象的复用和最小的内存占用。如果您手动注册Phalcon\Assets\Manager并且您的容器中已经有Phalcon\Html\TagFactory,则可以直接复用它而无需创建新的实例。

资源管理

资源通过资产相关类添加到管理器或集合中。Phalcon\Assets\Asset类是基础类,它接受创建资源所需的必要数据。

类型

可以是css, js,或者其他类型,具体取决于您是否想要扩展该组件的功能。

路径

该资源的路径

本地

表示该资源是否为本地资源

过滤器

附加到该资源的任何过滤器

属性

与资源相关的属性

版本

资源的版本号

自动版本

是否由组件自动对该资源进行版本控制

每个资源都有一个唯一的键分配给它。该键通过sha256计算得出,计算方式如下:

$this->getType() . ":" . $this->getPath()

这确保了唯一性,并防止资源管理器中出现重复的资源。

<?php

use Phalcon\Assets\Asset;

$asset = new Asset(
    'css',
    'css/bootstrap.css',
    true,
    null,
    [],
    '1.0',
    true
);

CSS

您还可以使用Phalcon\Assets\Asset\Css用于创建 CSS 资源的类。这个类是一个辅助类,继承自Phalcon\Assets\Asset类,并在内部将第一个参数设置为css.

<?php

use Phalcon\Assets\Asset\Css;

$asset = new Css(
    'css/bootstrap.css',
    true,
    null,
    [],
    '1.0',
    true
);

JS

您还可以使用Phalcon\Assets\Asset\Js用于创建 JS 资源的类。这个类是一个辅助类,继承自Phalcon\Assets\Asset类,并在内部将第一个参数设置为js.

<?php

use Phalcon\Assets\Asset\Js;

$asset = new Js(
    'js/bootstrap.js',
    true,
    null,
    [],
    '1.0',
    true
);

内联

有时应用程序需要生成的 CSS 或 JS 注入到视图中。您可以使用Phalcon\Assets\Inline类来生成此内容。可以使用以下参数创建该对象:

类型

可以是css, js,或者其他类型,具体取决于您是否想要扩展该组件的功能。

内容

将被注入的内容

过滤器

附加到该资源的任何过滤器

属性

与资源相关的属性

<?php

use Phalcon\Assets\Inline;

$asset = new Inline(
    'css',
    '.spinner {color: blue; }'
);

CSS

您还可以使用Phalcon\Assets\Inline\Css用于创建内联 CSS 资源的类。这个类是一个辅助类,继承自Phalcon\Assets\Inline类,并在内部将第一个参数设置为css.

<?php

use Phalcon\Assets\Inline\Css;

$asset = new Css(
    '.spinner {color: blue; }'
);

JS

您还可以使用Phalcon\Assets\Inline\Js用于创建内联 JS 资源的类。这个类是一个辅助类,继承自Phalcon\Assets\Inline类,并在内部将第一个参数设置为js.

<?php

use Phalcon\Assets\Asset\Js;

$asset = new Js(
    'alert("hello");'
);

自定义

实现Phalcon\Assets\AssetInterface接口使您可以创建可被资源管理器.

异常

在资源管理器组件中抛出的任何异常都是Phalcon\Assets\Exception。你可以使用此异常选择性地捕获仅从此组件抛出的异常。

<?php

use Phalcon\Assets\Exception;
use Phalcon\Mvc\Controller;

class IndexController extends Controller
{
    public function index()
    {
        try {
            $this->assets->addCss('css/style.css');
            $this->assets->addCss('css/index.css');
        } catch (Exception $ex) {
            echo $ex->getMessage();
        }
    }
}

添加资源

文件

Phalcon\Assets\Manager支持两种内置资源:CSS 和 JavaScript 资源。如果需要,您也可以创建其他类型的资源。资源管理器内部存储了两个默认的资源集合——一个用于 JavaScript,另一个用于 CSS。

您可以轻松地将资源添加到这些集合中:

<?php

use Phalcon\Mvc\Controller;

class IndexController extends Controller
{
    public function index()
    {
        $this->assets->addCss('css/style.css');
        $this->assets->addCss('css/index.css');

        $this->assets->addJs('js/jquery.js');
        $this->assets->addJs('js/bootstrap.min.js');
    }
}

为了提高页面加载性能,建议将 JavaScript 链接放在 HTML 的末尾,而不是放在<head>元素中。但是,根据您需要加载的 JavaScript 文件及其依赖关系,这可能并不总是可行的。

您也可以通过使用 Asset 对象将资源添加到管理器中:

<?php

use Phalcon\Assets\Asset\Css;
use Phalcon\Assets\Asset\Js;
use Phalcon\Mvc\Controller;

class IndexController extends Controller
{
    public function index()
    {
        $css1 = new Css('css/style.css');
        $css2 = new Css('css/index.css');

        $this->assets->addAsset($css1);
        $this->assets->addAssetByType('css', $css2);

        $js1 = new Js('js/jquery.js');
        $js2 = new Js('js/bootstrap.min.js');

        $this->assets->addAsset($js1);
        $this->assets->addAssetByType('js', $js2);
    }
}

内联

您也可以将内联资源添加到管理器中。内联资源代表需要动态注入到视图中的 CSS 或 JS 字符串(而非来自资源文件)。addInlineCode(), addInlineCodeByType(), addInlineCss()addInlineJs()可供您使用。

<?php

use Phalcon\Assets\Manager;
use Phalcon\Assets\Inline;

$css      = '.spinner {color: blue; }';
$js       = 'alert("hello")';
$manager  = new Manager();
$assetCss = new Inline('css', $css};
$assetJs  = new Inline('js', $js};

$manager
    ->addInlineCode($assetCss)
    ->addInlineCode($assetJs)
;

$manager
    ->addInlineByType('css', $assetCss)
    ->addInlineByType('js', $assetJs)
;

$manager
    ->addInlineCss($css)
    ->addInlineJs($js)
;

本地/远程资源

本地资源是指由同一应用程序提供的资源,位于公共位置(通常是public)。本地资源的 URL 通过url服务对其清理或过滤。

生成。远程资源是指像常见的库如jQuery, 引导程序等,通常由CDN.

第二个参数addCss()addJs()表示资源是否为本地资源(true是本地,false是远程)。默认情况下,资源管理器会假定资源是本地资源:

<?php

public function indexAction()
{
    $this->assets->addCss(
        '//cdn.assets.com/bootstrap/4/css/library.min.css', 
        false
    );

    $this->assets->addCss('css/style.css', true);
    $this->assets->addCss('css/extra.css');
}

集合

Phalcon\Assets\Collections是用于对相同类型的资源进行分组的对象。资源管理器隐式地创建了两个集合:cssjs。您可以创建额外的集合以便将特定资源进行分组,从而更方便地在视图中放置这些资源:

<?php

// Javascript - <head>
$headerCollection = $this->assets->collection('headerJs');

$headerCollection->addJs('js/jquery.js');
$headerCollection->addJs('js/bootstrap.min.js');

// Javascript - <footer>
$footerCollection = $this->assets->collection('footerJs');

$footerCollection->addJs('js/jquery.js');
$footerCollection->addJs('js/bootstrap.min.js');

获取

The 获取器方法允许您从代码的任何地方获取集合并根据需要对其进行操作。管理器提供了get(), getCollection(), getCss()getJs()。这些方法将返回管理器存储的集合。getCss() 和 getJs() 方法返回预注册的内置集合。

The collection()方法同时扮演创建者和获取者的角色。它允许您创建一个集合并获取它,以便之后向其中添加资源。getCss()getJs()方法执行相同的功能,如果集合不存在则创建它并返回。这两个集合设置了管理器中的预定义cssjs管理器中的集合。

<?php

$headerCollection = $this->assets->collection('headerJs');

$headerCollection = $this->assets->get('headerJs');

是否存在

The has()方法允许你检查特定的集合是否存在于管理器中;

<?php

$headerCollection = $this->assets->collection('headerJs');

echo $this->assets->has('headerJs'); // true

设置

如果内置的cssjs集合无法满足你的需求,你可以通过使用set().

<?php

use Phalcon\Assets\Collection;

$collection = new Collection();

$this->assets->set('outputJs', $collection);

URL 前缀

集合可以带有 URL 前缀,使你可以根据应用程序的需求轻松更改前缀。例如,从本地环境切换到生产环境,并为资源使用不同的CDNURL:

<?php

$footerCollection = $this->assets->collection('footer');

if ($config->environment === 'development') {
    $footerCollection->setPrefix('/');
} else {
    $footerCollection->setPrefix('http:://cdn.example.com/');
}

$footerCollection->addJs('js/jquery.js');
$footerCollection->addJs('js/bootstrap.min.js');

如果你喜欢这种语法,还可以链式调用方法:

<?php

$headerCollection = $this
    ->assets
    ->collection('header')
    ->setPrefix('https://cdn.example.com/')
    ->setLocal(false)
    ->addJs('js/jquery.js')
    ->addJs('js/bootstrap.min.js');

内置过滤器

资源可以被过滤,即在输出到视图之前对其进行操作。虽然 Phalcon v3 提供了 JavaScript 和 CSS 的压缩器,但许可限制不允许我们继续使用这些库。对于 v5,我们仅提供Phalcon\Assets\Filters\None过滤器(不会改变资源内容)以及Phalcon\Assets\FilterInterface接口,提供了创建自定义过滤器的能力。

自定义过滤器

创建自定义过滤器非常简单。你可以利用这种可扩展性来使用现有的、更高级的过滤/压缩工具,比如YUI, Sass, Closure等:

<?php

use Phalcon\Assets\FilterInterface;

/**
 * Filters CSS content using YUI
 *
 * @param string $contents
 * @return string
 */
class CssYUICompressor implements FilterInterface
{
    protected $options;

    /**
     * CssYUICompressor constructor
     *
     * @param array $options
     */
    public function __construct(array $options)
    {
        $this->options = $options;
    }

    /**
     * @param string $contents
     *
     * @return string
     */
    public function filter($contents)
    {
        file_put_contents('temp/my-temp-1.css', $contents);

        system(
            $this->options['java-bin'] .
            ' -jar ' .
            $this->options['yui'] .
            ' --type css ' .
            'temp/my-temp-file-1.css ' .
            $this->options['extra-options'] .
            ' -o temp/my-temp-file-2.css'
        );

        return file_get_contents('temp/my-temp-file-2.css');
    }
}

使用方式:

<?php

$css = $this->assets->get('head');

$css->addFilter(
    new CssYUICompressor(
        [
            'java-bin'      => '/usr/local/bin/java',
            'yui'           => '/some/path/yuicompressor-x.y.z.jar',
            'extra-options' => '--charset utf8',
        ]
    )
);

在前面的例子中,我们使用了一个称为LicenseStamper的自定义过滤器,

<?php

use Phalcon\Assets\FilterInterface;

/**
 * @param string $contents
 *
 * @return string
 */
class LicenseStamper implements FilterInterface
{
    /**
     * Do the filtering
     *
     * @param string $contents
     * @return string
     */
    public function filter($contents)
    {
        $license = '/* (c) 2019 Your Name Here */';

        return $license . PHP_EOL . PHP_EOL . $contents;
    }
}

输出

在所有资源都被添加到对应的集合之后,你可以使用输出方法来打印HTML 到你的视图中。这些方法是output(), outputCss(), outputJs(), outputInline(), outputInlineCss()outputInlineJs().

输出文件:

<?php

// Javascript - <head>
$headerCollection = $this->assets->collection('headerJs');

$headerCollection->addJs('js/jquery.js');
$headerCollection->addJs('js/bootstrap.min.js');

// Javascript - <footer>
$footerCollection = $this->assets->collection('footerJs');

$footerCollection->addJs('js/jquery.js');
$footerCollection->addJs('js/bootstrap.min.js');

然后在视图中:

<html>
    <head>
        <title>Some amazing website</title>

        <?php $this->assets->outputJs('headerJs'); ?>
    </head>

    <body>
        <!-- ... -->

        <?php $this->assets->outputJs('footerJs'); ?>
    </body>
<html>

Volt 语法:

<html>
    <head>
        <title>Some amazing website</title>

        {{ assets.outputCss('header') }}
    </head>

    <body>
        <!-- ... -->

        {{ assets.outputJs('footer') }}
    </body>
<html>

输出内联:

<?php

$css      = '.spinner {color: blue; }';
$js       = 'alert("hello")';
$assetCss = new Inline('css', $css};
$assetJs  = new Inline('js', $js};

$this
    ->assets
    ->addInlineCss($css)
    ->addInlineJs($js)
;

然后在视图中:

<html>
    <head>
        <title>Some amazing website</title>
    </head>
    <?php $this->assets->outputInlineCss(); ?>
    <body>

        <!-- ... -->

        <?php $this->assets->outputInlineJs(); ?>
    </body>
<html>

Volt 语法:

<html>
    <head>
        <title>Some amazing website</title>

        {{ assets.outputInlineCss() }}
    </head>

    <body>
        <!-- ... -->

        {{ assets.outputInlineJs() }}
    </body>
<html>
上面的代码行将被转换为:

<html>
    <head>
        <title>Some amazing website</title>

        <style>.spinner {color: blue; }</style>
    </head>

    <body>
        <!-- ... -->

        <script type="application/javascript">alert("hello")</script>
    </body>
<html>

自定义输出

The outputJs()outputCss()方法会为每种类型的资源生成必要的 HTML 代码。你可以重写此方法或手动打印资源,如下所示:

<?php

use Phalcon\Html\TagFactory;

$tagFactory   = new TagFactory();
$jsCollection = $this->assets->collection('js');

foreach ($jsCollection as $asset) {
    echo (string) $tagFactory
        ->script()
        ->add($asset->getPath())
    ;
}

隐式与显式输出

有时你可能需要隐式输出管理器的内容。要实现这一点,你可以使用useImplicitOutput()方法。调用output()之后将在屏幕上输出 HTML。

<?php

$this
    ->assets
    ->useImplicitOutput(true)
    ->addCss('css/style.css')
    ->output()
;

版本控制

The Assets组件支持版本控制(自动或手动),也被称为缓存清除。版本控制确保浏览器被告知需要重新下载资源文件,从而接收服务器上的最新 CSS 和 JS 代码。

要为资源添加版本号,在创建资源对象时包含版本字符串即可:

<?php

use Phalcon\Assets\Asset\Css;

$asset = new Css(
    'css/bootstrap.css',
    true,
    null,
    [],
    '1.0'
);

输出结果将在 URL 中包含版本号:

<link rel="stylesheet" href="css/bootstrap.css?ver=1.0"

你可以将版本号存储在配置文件或其他任何存储中,并在新版本发布至生产环境时更新它。

自动版本控制

你也可以使用资源文件的文件时间来控制资源的版本控制:

<?php

use Phalcon\Assets\Asset\Css;

$asset = new Css(
    'css/bootstrap.css',
    true,
    null,
    [],
    null,
    true
);
输出结果将在 URL 中包含文件修改时间:

<link rel="stylesheet" href="css/bootstrap.css?ver=1558392141">

注意

由于不必要的文件系统读取操作,不建议在生产环境中使用自动版本功能。

提升性能

为了优化资源处理,一种方法是让你的 Web 服务器来处理资源,提高响应时间。以下是设置资源管理器:

基础控制器

<?php

namespace App\Controllers;

use Phalcon\Mvc\Controller;

/**
 * App\Controllers\ControllerBase
 *
 * This is the base controller for all controllers in the application.
 */
class ControllerBase extends Controller
{
    public function onConstruct()
    {
        $this
            ->assets
            ->useImplicitOutput(false)
            ->collection('global')
            ->addJs('https://code.jquery.com/jquery-4.0.1.js', false, true)
        ;
    }
}

路由

<?php

/**
 * Define custom routes.
 * This file gets included in the router service definition.
 */
$router = new Phalcon\Mvc\Router();

$router->addGet(
    '/assets/(css|js)/([\w.-]+)\.(css|js)',
    [
        'controller' => 'assets',
        'action'     => 'serve',
        'type'       => 1,
        'collection' => 2,
        'extension'  => 3,
    ]
);

// ...

AssetsController

<?php

namespace App\Controllers;

use Phalcon\Http\Response;

/**
 * Serve site assets.
 */
class AssetsController extends ControllerBase
{
    public function serveAction(): Response
    {
        // Getting a response instance
        $response = new Response();

        // Prepare output path
        $collectionName = $this->dispatcher->getParam('collection');
        $extension      = $this->dispatcher->getParam('extension');
        $type           = $this->dispatcher->getParam('type');
        $targetPath     = "assets/{$type}/{$collectionName}.{$extension}";

        // Setting up the content type
        $contentType = $type == 'js' ? 'application/javascript' : 'text/css';
        $response->setContentType($contentType, 'UTF-8');

        // Check collection existence
        if (!$this->assets->exists($collectionName)) {
            return $response->setStatusCode(404, 'Not Found');
        }

        // Setting up the Assets Collection
        $collection = $this->assets
            ->collection($collectionName)
            ->setTargetUri($targetPath)
            ->setTargetPath($targetPath);

        // Store content to the disk and return a fully qualified file path
        $contentPath = $this->assets->output(
            $collection,
            function (array $parameters) {
                return BASE_PATH . '/public/' . $parameters[0];
            },
            $type
        );

        // Set the content of the response
        $response->setContent(
            file_get_contents($contentPath)
        );

        // Return the response
        return $response;
    }
}

如果预先编译的资源存在于文件系统中,它们必须由 Web 服务器直接提供。因此,为了获得静态资源的好处,我们必须更新我们的服务器配置。我们将为 Nginx 使用一个示例配置。对于 Apache,配置会略有不同:

location ~ ^/assets/ {
    expires 1y;
    add_header Cache-Control public;
    add_header ETag "";

    # If the file exists as a static file serve it directly without
    # running all the other rewrite tests on it
    try_files $uri $uri/ @phalcon;
}

location / {
    try_files $uri $uri/ @phalcon;
}

location @phalcon {
    rewrite ^(.*)$ /index.php?_url=$1;
}

其他配置指令

我们需要在应用程序的文档根目录中创建assets/jsassets/css目录(例如:public)。

每当应用程序请求诸如/assets/js/global.js的资源时,应用程序将检查资源是否存在。如果存在,则由 Web 服务器处理。否则,它将被重定向到AssetsController以由应用程序进行处理。

此方法不推荐用于生产环境和高负载应用程序。不过,该示例确实展示了使用此组件可以实现的功能。你选择的实现取决于应用程序的需求。

在大多数情况下,你的 Web 服务器,CDN,或者像Varnish HTTP Cache这样的服务将是更好的选择。

无噪 Logo
无噪文档
25 年 6 月翻译
版本号 5.9
文档源↗