the css view helper (application / layout / helpers / css.php)

来源:互联网 发布:unity3d 虚拟拆装demo 编辑:程序博客网 时间:2024/06/06 03:10

The goal of this helper is also to make managing css files easier.

This css view helper is really simple, im not kidding ;) It looks really complex but it isn't. There are just so many lines of code because i load several css files.

If you create a website you will probably have several css files, one for the default layout, a special one for the sick IE (Internet Explorer), perhaps a reset css as i do, another one for handhelds, you will also probably use a javascript library which has it's own css file and so on... So in the end you will have to load several css files, most of them will not be minifed.

There are three ways to optimize your css files. If you use Google Pagespeed or Yahoo's YSlow, those Firefox addons will give you hints about how you could optimize your css file(s).

- One way to improve the loading time, is to compress the css file. This is something we do using apache mod deflate, you can find the code i use to achieve this in the htaccess i previously published.
- Another thing you can do is to merge all the files into one single file. If we can achieve to reduce the amount of http requests per page we will increase the loading time of our website.
- The last optimization we can do, is to minify the css file content, we can remove spaces, remove comments and so on ... for this task i will use the great minify class from Joe Scylla that can be found here: http://code.google.com/p/cssmin/

Another thing that is important, in production mode you want to use the optimized css file but in development mode or even testing mode you want the raw files because those are lots easier to debug. This is something our helper will handle too. In non production mode it will use the raw css files, but for production mode it will take all the css files, merge them into a single file and then minify this file. The output of that single minified css file will be cached, we will only regenrate a new one if something changes in one of the raw css files.

Ok let's start coding ...

The first lines are nothing else then a list of css files used throughout our application / website. have four files, each file has a version number. I use the version number in development evironment mode. Every time i make big changes to my css i increase the version number, this ensures that the css url changes and therefore the browser downloads the new file and removes the old one from cache, if you don't chnage the version number or don't use it at all, the browser could continue using the old file. In production mode the name of the css file changes if you change the version number because the name is an md5 of all names and version numbers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
  
/**
 * CSS VIEW HELPER
 */
class Application_Layouts_Helpers_Css extends Zend_View_Helper_Abstract {
  
    /**
     * css view helper that loads all css files needed in our layout
     */
    public function css() {
  
        // reset css config
        $resetCssPath = $this->view->base().'/css/reset.css';
        // change the css version number to ensure that the files get replaced in browser caches
        $resetCssVersion = '?v=1';
  
        // handheld css config
        $handheldCssPath = $this->view->base().'/css/handheld.css';
        $handheldCssVersion = '?v=1';
  
        // default (front) css config
        $defaultCssPath = $this->view->base().'/css/default.css';
        $defaultCssVersion = '?v=1';
  
        // admin (backend) css config
        $adminCssPath = $this->view->base().'/css/admin.css';
        $adminCssVersion = '?v=1';



In the next few lines i use the zend framework request object, to retrieve the name of the module, controller and action of the page the user is visiting. I use those informations to identify which css are needed, not every page needs the same css files, for example the administration part of the website uses other css files then the front pages. Whatever page is called i use the reset css, the reset css i use can be found here: http://html5boilerplate.com/.

1
2
3
4
5
// retrieve the module, controller and action name
$request = Zend_Controller_Front::getInstance()->getRequest();
$moduleName = $request->getModuleName();
$controllerName = $request->getControllerName();
$actionName = $request->getActionName();



Now the part of the code used in non production mode. I use the zend framework headLink view helper. As described above besides the path i add the version number as parameter to avoid that browser keep an updated file in their cache.

I also described above that i use the module, controller and action name to create some sort of rules. I try not to use a different css file for each page but on the other hand i also don't want a huge css file whith for the whole website. You have to find the correct balance for this.

At the end i have added a few lines that are commented, they show some additional examples of what's possible but are not used in the myapp pages.

More informations about the zend framework headLink view helper can be found here: http://framework.zend.com/manual/en/zend.view.helpers.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// if we are not in production mode we use the single default css files
if (APPLICATION_ENVIRONMENT != 'production') {
 
    // add the reset css for all modules
    if (file_exists(APPLICATION_PATH.'/../public/'.$resetCssPath)) {
        $this->view->headLink()->appendStylesheet($resetCssPath.$resetCssVersion, 'screen', false);
    } else {
        Zend_Debug::dump($resetCssPath, 'NOT FOUND');
    }
 
    // special css for handhelds
    // TODO: check if the device which requests the page is a mobile phone (smartphone)
    if (file_exists(APPLICATION_PATH.'/../public/'.$handheldCssPath)) {
        $this->view->headLink()->appendStylesheet($handheldCssPath.$handheldCssVersion, 'handheld, screen and (max-device-width: 480px)', false);
    } else {
        Zend_Debug::dump($handheldCssPath, 'NOT FOUND');
    }
 
    // css rules
    if (strtolower(substr(trim($moduleName), 0, 5)) == 'admin' || strtolower(substr(trim($controllerName), 0, 5)) == 'admin') {
 
        // administration css
        if (file_exists(APPLICATION_PATH.'/../public/'.$adminCssPath)) {
            $this->view->headLink()->appendStylesheet($adminCssPath.$adminCssVersion, 'screen', false);
        } else {
            Zend_Debug::dump($adminCssPath, 'NOT FOUND');
        }
 
    } else {
 
        if (file_exists(APPLICATION_PATH.'/../public/'.$defaultCssPath)) {
            $this->view->headLink()->appendStylesheet($defaultCssPath.$defaultCssVersion, 'screen', false);
        } else {
            Zend_Debug::dump($defaultCssPath, 'NOT FOUND');
        }
 
    }
 
    // EXAMPLES FOR OTHER CSS OPTIONS
    //$this->view->headLink()->prependStylesheet($this->base().'/css/print.css', 'print', false); // for pages that get printed
    //$this->view->headLink()->prependStylesheet($this->base().'/css/mobile.css', 'mobile', false); // for mobile phones
    //$this->view->headLink()->prependStylesheet($this->base().'/css/all.css', 'all', false); // for all types
    //$this->view->headLink()->appendStylesheet($this->base().'ie.css', 'screen', 'lt IE 7'); // stylesheet for ie smaller then ie7
    //$this->view->headLink()->appendStylesheet($this->base().'ie.css', 'screen', 'gte IE 7'); // stylesheet for ie greater or equal to ie7



Now comes the part i really like. This part is used in production environment mode. I add all css file pathes to an array depending on the page that gets visited. I also create string by using each files name and it's version number. The difference is that i don't print a message if the file is not found, if you forget to upload a css file it simply isn't added to the array. You could log a message if a file is not found.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// in production mode, we use a single, compressed css file
} else {
 
    // create a name for our css file, the name is a hash containing the
    // pathes of all the files and their version numbers, to avoid conflicts
    $rawCssName = '';
 
    // an array holding the css files pathes
    $cssPathes = array();
 
    if (file_exists(APPLICATION_PATH.'/../public/'.$resetCssPath)) {
        $cssPathes[] = $resetCssPath;
        $rawCssName .= $resetCssPath.$resetCssVersion;
    }
      
    if (file_exists(APPLICATION_PATH.'/../public/'.$handheldCssPath)) {
        $cssPathes[] = $handheldCssPath;
        $rawCssName .= $handheldCssPath.$handheldCssVersion;
    }
 
    if (strtolower(substr(trim($moduleName), 0, 5)) == 'admin' || strtolower(substr(trim($controllerName), 0, 5)) == 'admin') {
        if (file_exists(APPLICATION_PATH.'/../public/'.$adminCssPath)) {
            $cssPathes[] = $adminCssPath;
            $rawCssName .= $adminCssPath.$adminCssVersion;
        }
    } else {
        if (file_exists(APPLICATION_PATH.'/../public/'.$defaultCssPath)) {
            $cssPathes[] = $defaultCssPath;
            $rawCssName .= $defaultCssPath.$defaultCssVersion;
        }
    }



He i create the name of the css file, as name i use the string of the css files names and their version numbers and finally encrypt it with md5. md5 creates always different names with the same length.

1
2
// now we use md5 to create a hash that will be the final file name
$cssName = md5($rawCssName).'.css';



Now i check if the file already exists, this is some sort of cache mechanism. If the file exists i don't recreate it. We avoid reading the single files and writing the merged css file if it already exists. Every time a user visits the website you deliver the cached and optimized single css file, or if the content of one of the files changes you create a new cached version.

1
2
// if the file already exists we don't create a new pne
if (!file_exists(APPLICATION_PATH.'/../public/'.$this->view->base().'/css/cached/'.$cssName)) {



Now i use the php file get contents function (http://php.net/manual/en/function.file-get-contents.php) to get the content of each css file, i merge the content of all the files into a single string. You can also use external css files here, fetch them and they will get added to your single file.

1
2
3
4
5
6
7
// retrieve all the css files needed and merge them into a single file
// we us file exists because a non existing file would cause an error
$cssContent = '';
 
foreach ($cssPathes as $cssPath) {
    $cssContent .= file_get_contents(APPLICATION_PATH.'/../public/'.$cssPath);
}



Now that i have everything merged i use a class i found some time ago. It's name is cssmin, it was created by Joe Scylla that can be found here: http://code.google.com/p/cssmin/. This class minifies the css string, it removes comments, spaces and more useless code. As you can see, you have several configuration options, just change the values of the array to better match your needs. You should download the class and add it to your library. I put the file in /myapp/library/My and changed it name to My_CssMin. The zend framework autoloader will find the file and load it automatically.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    // now we minifi the css content
    // example with full configuration
    $cssSingleFile = My_CssMin::minify($cssContent, array(
            "remove-empty-blocks"           => true,
            "remove-empty-rulesets"         => true,
            "remove-last-semicolons"        => true,
            "convert-css3-properties"       => true,
            "convert-font-weight-values"    => true, // new in v2.0.2
            "convert-named-color-values"    => true, // new in v2.0.2
            "convert-hsl-color-values"      => true, // new in v2.0.2
            "convert-rgb-color-values"      => true, // new in v2.0.2; was "convert-color-values" in v2.0.1
            "compress-color-values"         => true,
            "compress-unit-values"          => true,
            "emulate-css3-variables"        => true
            ));
 
    file_put_contents(APPLICATION_PATH.'/../public/'.$this->view->base().'/css/cached/'.$cssName, $cssSingleFile);
      
}



Now we are almost done, all we now need to do is use the zend framework headLink view helper. We use the name of the file we created eralier. That's it, now you will get a minified single css files instead of several non minified files and the best is you don't have to create it manually. If you often change the content or amount of css files this helper will save you lots of time, it will optimize the website for your users and you will get a nice score in pagespeed ;). Chris.lu for example uses this view helper.

1
2
3
4
5
6
7
8
9
10
            // use the zf headLink view helper
            if (file_exists(APPLICATION_PATH.'/../public/'.$this->view->base().'/css/cached/'.$cssName)) {
                $this->view->headLink()->appendStylesheet($this->view->base().'/css/cached/'.$cssName, 'screen', false);
            }
  
        }
  
    }
  
}




 

原创粉丝点击