<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>润物无声 &#187; PHP</title>
	<atom:link href="http://blog.zhourunsheng.com/tag/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhourunsheng.com</link>
	<description>天空一朵雨做的云</description>
	<lastBuildDate>Sat, 08 May 2021 05:17:21 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.41</generator>
	<item>
		<title>网站SEO优化之CSS JS代码合并</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/#comments</comments>
		<pubDate>Fri, 30 Aug 2013 03:34:48 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[SEO]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1824</guid>
		<description><![CDATA[<p>在优化网站的过程中，其中一个规则就是尽量减少HTTP的请求数量，比如图片的合并，JS，CSS代码的合并等，本文 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/">网站SEO优化之CSS JS代码合并</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>在优化网站的过程中，其中一个规则就是尽量减少HTTP的请求数量，比如图片的合并，JS，CSS代码的合并等，本文介绍了一款PHP的自动合并脚本，可以在网站中使用，只要将原来需要引用JS和CSS的地方，换成该脚本的函数调用，那么脚本就会自动将过个请求合并成一个请求，重而提高网站的打开速度。</p>
<h3>Combining Assets</h3>
<p>One of the basic tenets of making a web-page load quickly is reducing the number of HTTP requests that your page makes. One very common way of doing this, is assuring that all of your CSS files are combined into one request (ideally in the &lt;head&gt; tag), and all of your javscript files are combined into one request (ideally at the bottom of the &lt;body&gt; tag).<span id="more-1824"></span></p>
<h3>The Solution</h3>
<p>I’ve worked with several large sites which each had their own solutions, but recently I found myself needing to speed up a fairly simple site: <a title="Burndown Charts for Trello" href="https://burndownfortrello.com/">Burndown for Trello</a>.</p>
<p><strong>To make things simple (and so that I’d never have to write this again), I made a <tt>StaticResources</tt>system which will allow me to put just one PHP script into the root directory of the site, and use a few simple calls to add files to the header or footer of the page.</strong> The script requires no setup, installation, or configuration to run correctly. However, it has some optional advanced settings (which we’ll discuss at the end).</p>
<h3>Usage</h3>
<p>Usage is very simple. Just make a call to add files to the system, so that they’re included in the header or footer. Both local and external files are added with the same kind of call.<br />
StaticResources - how to add files<br />
PHP</p>
<pre>StaticResources::addHeaderFile("css/GoogleWebfont_Tauri_Latin.css");
StaticResources::addFooterFile("jquery-ui.js");
StaticResources::addFooterFile("http://code.jquery.com/ui/1.9.1/jquery-ui.js");</pre>
<p>After you’ve added the files, make sure that somewhere in your code you print the HTML which will contain all of the files that were added<br />
StaticResources - printing the script and style tags<br />
PHP</p>
<pre>// put this at the bottom of the HEAD tag
print StaticResources::getHeaderHtml();

// put this at the bottom of the BODY tag
print StaticResources::getFooterHtml();</pre>
<p>Here’s an example file of how you’d include a bunch of JS / CSS using this <tt>StaticResources</tt> system. It obviously won’t work if you don’t create JS/CSS files in the locations referenced, but this is very simple code to show the system in action.</p>
<p>StaticResources - EXAMPLE USAGE PAGE<br />
PHP</p>
<pre>&lt;?php

include 'static.php';

// You can add header files any time before the StaticResources::getHeaderHtml() call.
StaticResources::addHeaderFile("css/GoogleWebfont_Tauri_Latin.css");

// You can add footer files any time before the StaticResources::getHeaderHtml() call.
StaticResources::addFooterFile("jquery-1.9.1.js");

// For files that won't change (like a specific version of jQuery), it's often better to host it on your
// own server instead of making a separate HTTP request to the CDN.
StaticResources::addFooterFile("jquery-ui.js");

?&gt;&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
        &lt;title&gt;StaticResources example page&lt;/title&gt;
        &lt;?php

        // You can add header files any time before the StaticResources::getHeaderHtml() call.
        StaticResources::addHeaderFile("jquery-ui.min.css");

        // A good place to output the HTML is right at the end of the HEAD tag.
        // getHeaderHtml() returns a string (to be more flexible with various PHP frameworks)
        // so be sure to print its result.
        print StaticResources::getHeaderHtml();
        ?&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;header&gt;
            Created for &lt;a href='https://burndownfortrello.com'&gt;Burndown for Trello&lt;/a&gt;.
        &lt;/header&gt;

        &lt;article&gt;
            &lt;h1&gt;StaticResources example page&lt;/h1&gt;
            &lt;p&gt;This page is an example of loading static resources in combined files.&lt;/p&gt;
        &lt;/article&gt;

        &lt;footer&gt;
            © 2013 - Sean Colombo
        &lt;/footer&gt;
        &lt;?php

        // For files that won't be changing
        StaticResources::addFooterFile("stripeIntegration.js");

        // To add a file from another site, just make sure to use the full URL.
        // Since there is no file extension, we have to pass in the filetype as a second parameter.
        StaticResources::addFooterFile("https://www.google.com/jsapi", "js");

        // Output the footerHtml at the bottom of the page. It doesn't have to be in the FOOTER tag, it should
        // be done as far down on the page as possible (close to the end of the BODY tag).
        print StaticResources::getFooterHtml();

        ?&gt;
    &lt;/body&gt;
&lt;/html&gt;</pre>
<h3>The Code</h3>
<p>Without further delay, this is the code of <tt>static.php</tt> that you should put in the main (eg: public_html) directory of your app.<br />
static.php - THE ACTUAL CODE<br />
PHP</p>
<pre>&lt;?php
/**
* @author Sean Colombo
* @date 20130817
*
* This script is designed to be a generalized (not site-specific) tool
* for including static JS/CSS assets in PHP web-apps.  It's a single file,
* requires no configuration to start using it, and makes delivery of static
* assets significantly faster.
*
* BEST PRACTICE:
* Ideally, any javascript that doesn't NEED to be loaded higher up in the page should be
* loaded in the footer (added using addFooterFile()) so that the page can render while the
* javascript is still being requested from the server.
* However, since the page cannot render properly until the CSS is loaded, any CSS used for the initial
* page-layout should be put into the header. Otherwise the page will start out looking unstyled, then
* will re-flow (flicker or adjust) after the CSS file is loaded.
*
* The performance benefits include:
* - Making less HTTP-requests results in a lot less setup/teardown and latency-time being wasted.
* - Good caching headers by default (makes subsequent page-requests faster since the user won't have to
*   make another request for the file they already have cached).
* - Minification: NOTE: NOT DONE YET - this will make the js/css files significantly smaller which decreases
*   the download-time.
*
* This script should be placed in the root directory of your site.  It is both an includable
* script (for building the list of files) and an endpoint (for actually serving up combined files).
*
* Remote files are currently sent as separate requests from local files - they are not bundled into
* the same package.  However, the implementation details are transparent - no special action needs to
* be taken for remote files. Just add them like you'd add a resource that's on your own server, and as
* long as you include the full URL, they will be handled properly.  Example:
*        // Local and remote files are included the same way.
*        StaticResources::addHeaderFile("js/myFile.js");
*        StaticResources::addHeaderFile("http://cdn.google.com/someOtherFile.js");
*
* == USAGE ==
* To output the header files - YOU MUST ADD THIS CALL. PUT IT RIGHT BEFORE THE END OF THE &lt;HEAD&gt; TAG.
*        print StaticResources::getHeaderHtml();
*
* To output the footer files - YOU MUST ADD THIS CALL. PUT IT RIGHT BEFORE THE END OF THE &lt;BODY&gt; TAG.
*        print StaticResources::getFooterHtml();
*
* To add files to the HEAD tag or to the bottom of the page:
*        StaticResources::addHeaderFile("css/GoogleWebfont_Tauri_Latin.css");
*        StaticResources::addFooterFile("jquery-ui.js");
*        StaticResources::addFooterFile("http://code.jquery.com/ui/1.9.1/jquery-ui.js");
*
* To add a file which doesn't have an extension (eg: ".js") at the end.
*        StaticResources::addFooterFile("https://www.google.com/jsapi", "js");
*
* Optional: to specify that this file should be served from a different URL (eg: a Content Delivery Network).
* Put this right below where you include() this static.php file:
*        StaticResources::setRootUrl("http://cdn.example.com/"); // use a trailing slash
*
*
*
* TODO: Add minification
* TODO: LATER: Add options to allow the files to use HTML5 async and deferred attributes.
*/

/**
* For security reasons, we limit the types of files that can be outputted.
* If this wasn't here, someone could request each of your .php files and read all of
* your source-code (and password files, etc.), so please only change this if you understand
* the implications.
*
* NOTE: See STATICRESOURCES_ALLOWED_FILE_TYPES below for the actual array.
*/

// Need to define this way (serialized and inside of define()) because arrays can't be class-constants.
define("STATICRESOURCES_ALLOWED_FILE_TYPES", serialize( array( "js", "css" ))); // all types must be lowercase!

// Character(s) used in the URL to separate file names. This should be a character that's not
// allowed in filenames, otherwise a real file could be split into two filenames.
// For example: "bob-vila.txt" would be interpreted as "bob" and "vila.txt" if the delimiter was "-".
// There is no perfect result for this (since different characters are allowed on different operating systems).
define("STATICRESOURCES_FILE_DELIMITER", "||");

// If this script is being run as the page (rather than just included) then serve up the files
// being requested.
if( $_SERVER['SCRIPT_FILENAME'] == __FILE__ ){
    $files = (isset($_GET['files']) ? $_GET['files'] : "");
    $files = explode(STATICRESOURCES_FILE_DELIMITER, $files); // turn it into an array.

    if(count($files) == 0){
        print "/* NOTE: No files found in the URL.\n";
        print "Please specify the file names in the 'file' parameter, separated by \"".STATICRESOURCES_FILE_DELIMITER."\"\n";
        print "See ".basename(__FILE__)." for documentation on how to use this system.\n";
        print "*/\n";
    } else {
        $fileType = ""; // don't know the fileType until we examine the file names.

        foreach($files as $fileName){
            $fileName = StaticResources::sanitizeFileName($fileName);

            $ext = StaticResources::getFileExtension($fileName);
            if( (!empty($fileType)) &amp;&amp; ($ext != $fileType) ){
                // Warn the user that they're mixing file-types.
                print "\n\n/* ===== WARNING: \"$fileName\" is of wrong file type. ";
                print "It is a '$ext' file, but this is a '$fileType' file. Skipped. ===== */\n\n\n\n";

                // Don't output this file since it's the wrong type. Skip to next file.
                continue;
            } else {
                // If fileType is still empty, this is the first file encountered.  Set correct headers.
                if(empty($fileType)){
                    $fileType = $ext;

                    // Set the content-type headers
                    $allowMimeSniffing = false; // usually, listen to our Content-types instead of sniffing Mime-type. Only exception is when we don't set a Content-type.
                    if($fileType == "css"){
                        header("Content-type: text/css");
                    } else if($fileType == "js"){
                        header("Content-type: application/javascript");
                    } else {
                        $allowMimeSniffing = true;
                        print "/* WARNING: Don't know what Content-type header to set for files with extension: '$fileType' */\n";
                    }
                    if(!$allowMimeSniffing){
                        // Disables IE mime-type sniffing so it always listens to Content-type.
                        header("X-Content-Type-Options: nosniff");
                    }

                    // Set reasonable caching headers.
                    // LATER: Could add a configuration array for tweaking the expiration by file-type. This is a reasonable default though.
                    $SECONDS_IN_MONTH = 60*60*24*30;
                    header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + $SECONDS_IN_MONTH)); // needs to be after session_start if session_start is used.
                }
            }

            // If there is a query-string, chop that off before looking for the file.
            if(strpos($fileName, "?") !== false){
                $fileName = substr($fileName, 0, strpos($fileName, "?"));
            }

            // Read, preprocess (eg: minify), and print the file contents.
            print "/* File: \"$fileName\" */\n";
            @$content = file_get_contents($fileName);
            if($content === false){
                print "/* FILE NOT FOUND */\n\n";
            } else {

                // TODO: PRE-PROCESS THE CONTENT.  MINIFICATION, ETC.
                // TODO: PRE-PROCESS THE CONTENT.  MINIFICATION, ETC.

                // Prints the content.
                print "$content\n\n";
            }
        }
    }
}

/**
* Helper class for creating tags which will import several static resources in a single
* request.
*
* The most common way to interface with this class is through the static functions at the
* bottom of the class (which will all operate on the same singleton).
*/
class StaticResources {
    const ERR_WRONG_FILETYPE = "Error in StaticResources: Tried to add a file with a file-type that isn't allowed.  File had extension '.%s' which is not in ALLOWED_FILE_TYPES in %s\n";
    const ERR_CANT_OUTPUT_FILETYPE = "Error in StaticResources: Don't know how to include code for files of type '%s'\n";
    const ERR_ALREADY_OUTPUT_HEADER = "Error in StaticResources: Tried to add a head file after the head files have already been retrieved. The file that was added late was: \"%s\".";
    const ERR_ALREADY_OUTPUT_FOOTER = "Error in StaticResources: Tried to add a footer file after the footer files have already been retrieved. The file that was added late was: \"%s\".";

    private static $ALLOWED_FILE_TYPES; // we have to initialize this in the constructor because arrays can't be class-constants.
    private $staticResourcesRootUrl = "./";

    // NOTE: Inclusion-order matters for both js and css, so never sort the array of fileNames... always output them in the order
    // received.
    private $_headerFilesByType = array('js' =&gt; array(), 'css' =&gt; array());
    private $_footerFilesByType = array('js' =&gt; array(), 'css' =&gt; array());

    private $_headerAlreadyReturned = false;
    private $_footerAlreadyReturned = false;

    /**
     * Basic constructor - note that most of the interaction with this class will
     * be done using static functions which grab a singleton.
     */
    public function __construct(){
        self::$ALLOWED_FILE_TYPES = unserialize( STATICRESOURCES_ALLOWED_FILE_TYPES );

        foreach(self::$ALLOWED_FILE_TYPES as $type){
            $this-&gt;_headerFilesByType[$type] = array();
            $this-&gt;_footerFilesByType[$type] = array();
        }
    } // end constructor

    /**
     * If your static resources are served from another domain (such as cdn.yoursite.com), then set
     * that here. This string is prepended to the filename (eg: "static.php") when building the URL for the combined-file.
     */
    public function mySetRootUrl($rootUrl){
        $this-&gt;staticResourcesRootUrl = $rootUrl;
    }

    /**
     * Adds the filename to the list of files to include in the head tag for its given
     * filetype.  If the fileName does not have an extension of one of the ALLOWED_FILE_TYPES,
     * then the file will NOT be added and a warning will be given instead.
     *
     * For these files to be used, the calling code must remember to print the results of
     * getHeaderHtml() in the &lt;head&gt; tag.
     *
     * NOTE: The fileName must be structured relative to where THIS (StaticResources) file is,
     * and should not be in normal URL format, but rather in a format that this file can be read
     * from disk.  So doing "/directory/filename.ext" to get to the root of public_html will not work
     * unless the StaticResources file happens to be in public_html.  If this sounds confusing,
     * the simplest fix is to put this StaticResources file in your public_html directory, then you
     * can write file names exactly the way you would have written them in &lt;script&gt;&lt;/script&gt; tags.
     *
     * @param fileType - if this is empty, the fileType will be detected from the filename. Sometimes
     * the filename might not have an extension (some JS libraries don't have file extensions) so you may
     * need to specify this value. Should be "js" or "css".
     */
    public function myAddHeaderFile($fileName, $fileType="", $warnIfAddedLate=true){
        // Make sure this file wasn't added after the header files were already displayed.
        if( $warnIfAddedLate &amp;&amp; $this-&gt;_headerAlreadyReturned){
            $errorString = sprintf(self::ERR_ALREADY_OUTPUT_HEADER, $fileName);
            trigger_error($errorString, E_USER_WARNING);
        }

        // If this file type is allowed, then remember it for later.
        if( $this-&gt;fileIsAllowed($fileName) ){
            $ext = (empty($fileType) ? StaticResources::getFileExtension($fileName) : strtolower($fileType));
            $this-&gt;headerFilesByType[ $ext ][] = $fileName;
        } else {
            $ext = (empty($fileType) ? StaticResources::getFileExtension($fileName) : strtolower($fileType));
            $errorString = sprintf(self::ERR_WRONG_FILETYPE, $ext, __FILE__);
            trigger_error($errorString, E_USER_WARNING);
        }
    } // end addHeaderFile()

    /**
     * Adds the filename to the list of files to include at the bottom of the BODY tag.
     * If the fileName does not have an extension of one of the ALLOWED_FILE_TYPES,
     * then the file will NOT be added and a warning will be given instead.
     *
     * For these files to be used, the calling code must remember to print the results of
     * getFooterHtml() somewhere very late in the document, ideally at the very bottom of
     * the &lt;body&gt; tag (right before &lt;/body&gt;).
     *
     * @param fileType - if this is empty, the fileType will be detected from the filename. Sometimes
     * the filename might not have an extension (some JS libraries don't have file extensions) so you may
     * need to specify this value. Should be "js" or "css".
     */
    public function myAddFooterFile($fileName, $fileType="", $warnIfAddedLate=true){
        // Make sure this file wasn't added after the footer files were already displayed.
        if( $warnIfAddedLate &amp;&amp; $this-&gt;_footerAlreadyReturned){
            $errorString = sprintf(self::ERR_ALREADY_OUTPUT_FOOTER, $fileName);
            trigger_error($errorString, E_USER_WARNING);
        }

        // If this file type is allowed, then remember it for later.
        if( $this-&gt;fileIsAllowed($fileName) ){
            $ext = (empty($fileType) ? StaticResources::getFileExtension($fileName) : strtolower($fileType));
            $this-&gt;footerFilesByType[ $ext ][] = $fileName;
        } else {
            $ext = (empty($fileType) ? StaticResources::getFileExtension($fileName) : strtolower($fileType));
            $errorString = sprintf(self::ERR_WRONG_FILETYPE, $ext, __FILE__);
            trigger_error($errorString, E_USER_WARNING);
        }
    } // end addFooterFile()

    /**
     * Returns a string which contains the HTML that's needed to
     * import the files in the HEAD tag.  This should be called exactly
     * once (and it's results should be printed in the &lt;head&gt; tag) on every
     * page.
     */
    public function myGetHeaderHtml(){
        $html = "&lt;!-- StaticResources::getHeaderHtml() --&gt;\n" . $this-&gt;getHtmlForArray( $this-&gt;headerFilesByType );
        $_headerAlreadyReturned = true;
        return $html;
    } // end getHeaderHtml()

    /**
     * Returns a string which contains the HTML that's needed to
     * import the files at the bottom of the BODY tag.  This should be called exactly
     * once (and it's results should be printed at the bottom of the &lt;body&gt; tag - right
     * before &lt;/body&gt;) on every page.
     */
    public function myGetFooterHtml(){
        $html = "&lt;!-- StaticResources::getFooterHtml() --&gt;\n" . $this-&gt;getHtmlForArray( $this-&gt;footerFilesByType );
        $_footerAlreadyReturned = true;
        return $html;
    } // end getFooterHtml()

    /**
     * Given an associative array of static resources whose keys are
     * resource types and whose values are arrays of fileNames (local
     * and/or remote), this will return a string which contains the
     * HTML that's needed to import those files.
     */
    private function getHtmlForArray($filesByType){
        $html = "";

        // The URL of this script (which will act as an endpoint and serve up the actual content.
        $url = $this-&gt;staticResourcesRootUrl . basename(__FILE__);
        foreach($filesByType as $fileType =&gt; $files){
            $localFiles = array();

            foreach($files as $fileName){
                if(StaticResources::isRemoteFile($fileName)){
                    // Add the HTML for including the remote file.
                    if($fileType == "css"){
                        $html .= "        &lt;link rel=\"stylesheet\" href=\"$fileName\"/&gt;\n";
                    } else if($fileType == "js"){
                        $html .= "        &lt;script src=\"$fileName\"&gt;&lt;/script&gt;\n";
                    } else {
                        // Each file type needs to be included a certain way, and we don't recognize this fileType.
                        $errorString = sprintf(self::ERR_CANT_OUTPUT_FILETYPE, $fileType);
                        trigger_error($errorString, E_USER_WARNING);
                    }
                } else {
                    $localFiles[] = $fileName;
                }
            }

            // Output the HTML which makes the request for the combined-file of all local files of the same fileType.
            if(count($localFiles) &gt; 0){

                // TODO: TWEAK SO THAT THE DELIMITER ISN'T URL-ENCODED. MAKES IT MORE READABLE AND SHORTER.
                // TODO: TWEAK SO THAT THE DELIMITER ISN'T URL-ENCODED. MAKES IT MORE READABLE AND SHORTER.

                $fullUrl = $url . "?files=".rawurlencode( implode(STATICRESOURCES_FILE_DELIMITER, $localFiles) );

                if($fileType == "css"){
                    $html .= "        &lt;link rel=\"stylesheet\" href=\"$fullUrl\"/&gt;\n";
                } else if($fileType == "js"){
                    $html .= "        &lt;script src=\"$fullUrl\"&gt;&lt;/script&gt;\n";
                } else {
                    // Each file type needs to be included a certain way, and we don't recognize this fileType.
                    $errorString = sprintf(self::ERR_CANT_OUTPUT_FILETYPE, $fileType);
                    trigger_error($errorString, E_USER_WARNING);
                }
            }
        }

        return $html;
    } // end getHtmlForArray()

    /**
     * Returns true if the given fileName is allowed to be included, false otherwise.
     * The reason a file may not be allowed is that it's of the wrong file-type. One
     * reason for this is that we don't want attackers to request file types that may
     * contain password-files or source code that some users of this script might not
     * want to make public, etc..
     */
    private function fileIsAllowed($fileName){
        $fileIsAllowed = true;
        if( !StaticResources::isRemoteFile($fileName)){
            $fileExtension = strtolower( StaticResources::getFileExtension($fileName) );
            $fileIsAllowed = in_array($fileExtension, self::$ALLOWED_FILE_TYPES);
        }
        return $fileIsAllowed;
    } // end fileIsAllowed()

    /**
     * Returns true if the fileName is from another site and false if it is from this site.
     */
    public static function isRemoteFile($fileName){
        // If it starts with a protocol (ftp://, http://, https://, etc.) then it is remote (not local).
        return (0 &lt;  preg_match("/^[a-z0-9]+:\/\//i", $fileName));
    }

    /**
     * If the 'fileName' is a local file, this will return that fileName in such a way
     * that the fileName can be loaded from disk safely (without allowing the user to
     * jump out of the current directory with "../" or absolute directories such
     * as "/usr/bin/").
     */
    public static function sanitizeFileName($fileName){
        // Only local files need to be sanitized.
        if( !StaticResources::isRemoteFile($fileName)){
            // Make sure the user can't get above the current directory using "../".
            while(strpos($fileName, "../") !== false){
                $fileName = str_replace("../", "", $fileName);
            }

            // Starting out with current directory avoids abusing absolute paths such as "/usr/bin"
            if(strpos($fileName, "./") !== 0){ // if path already starts with ./, don't duplicate it.
                if(strpos($fileName, "/") === 0){ // path already starts with "/", just turn it into "./".
                    $fileName = ".$fileName";
                } else {
                    $fileName = "./$fileName"; // all other paths that start 'normally' (not "./" or "/").
                }
            }
        }

        return $fileName;
    }

    public static function getFileExtension($fileName){
        // If there is a query-string, chop that off before looking for the extension.
        if(strpos($fileName, "?") !== false){
            $fileName = substr($fileName, 0, strpos($fileName, "?"));
        }

        return pathinfo($fileName, PATHINFO_EXTENSION);
    }

    // ----- STATIC HELPERS -----
    /**
     * Gets a singleton object of the StaticResources type to make it easy for the
     * script to use StaticResources throughout the web-app without passing the object
     * around.  This is probably the most common use-case.
     */
    public static function getSingleton(){
        global $staticResourcesSingleton;
        if(empty($staticResourcesSingleton)){
            $staticResourcesSingleton = new StaticResources();
        }
        return $staticResourcesSingleton;
    } // end getSingleton()

    public static function addHeaderFile($fileName, $fileType="", $warnIfLateAdded=true){
        $singleton = StaticResources::getSingleton();
        $singleton-&gt;myAddHeaderFile($fileName, $fileType, $warnIfLateAdded);
    }
    public static function addFooterFile($fileName, $fileType="", $warnIfLateAdded=true){
        $singleton = StaticResources::getSingleton();
        $singleton-&gt;myAddFooterFile($fileName, $fileType, $warnIfLateAdded);
    }
    public static function getHeaderHtml(){
        $singleton = StaticResources::getSingleton();
        return $singleton-&gt;myGetHeaderHtml();
    }
    public static function getFooterHtml(){
        $singleton = StaticResources::getSingleton();
        return $singleton-&gt;myGetFooterHtml();
    }
    public static function setRootUrl( $rootUrl ){
        $singleton = StaticResources::getSingleton();
        $singleton-&gt;mySetRootUrl($rootUrl);
    }

} // end class StaticResources</pre>
<h3>Using this script with a CDN</h3>
<p>One drawback of serving a combined file is that your server has to read in all of the files, then print them all. This makes the call slightly slower than it has to be. One solution is to use a <a title="Content Delivery Network - Wikipedia" href="http://en.wikipedia.org/wiki/Content_delivery_network">CDN</a> to cache the files on your site. This way, even if it takes your server a second or so to generate the file the first time, it can be served to the next user instantly, from the CDN.</p>
<p>For <a title="Burndown for Trello" href="https://burndownfortrello.com/">Burndown for Trello</a>, we use <a title="Fastly CDN" href="http://www.fastly.com/">Fastly</a> as our CDN (disclosure: my fiance works there, but we use it because it’s free for one backend and it’s crazy fast).</p>
<p>The code to make this work with a CDN is very simple: one extra line below the include call:</p>
<p>How to serve the static assets from a CDN<br />
PHP</p>
<pre>include 'static.php';
StaticResources::setRootUrl("http://b4t.global.ssl.fastly.net/");</pre>
<h3>Code minification</h3>
<p>Another common performance trick is to use a <a href="http://en.wikipedia.org/wiki/Minification_(programming)">minifier</a> to reduce file-size. This hasn’t been added to the script yet, but may come in the future. If you have a favorite minifier, I’ve conspicuously commented in the code where it would make sense to run the minifier.</p>
<p>Minification takes a little while to run, so it’s highly recommended that you get a CDN if you’re using minificaiton.</p>
<h3>The End</h3>
<p>Hope you found the script useful. If you use it on your site, please link to it in the comments!<br />
Thanks,</p>
<p>参考文章：</p>
<p>1. <a href="http://bluelinegamestudios.com/blog/posts/simple-single-file-php-script-for-combining-js-css-assets" target="_blank">http://bluelinegamestudios.com/blog/posts/simple-single-file-php-script-for-combining-js-css-assets</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/">网站SEO优化之CSS JS代码合并</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>一种更符合自然语意的PHP正则表达式</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e4%b8%80%e7%a7%8d%e6%9b%b4%e7%ac%a6%e5%90%88%e8%87%aa%e7%84%b6%e8%af%ad%e6%84%8f%e7%9a%84php%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e4%b8%80%e7%a7%8d%e6%9b%b4%e7%ac%a6%e5%90%88%e8%87%aa%e7%84%b6%e8%af%ad%e6%84%8f%e7%9a%84php%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f/#comments</comments>
		<pubDate>Thu, 15 Aug 2013 14:07:37 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Regular Expressions]]></category>
		<category><![CDATA[正则表达式]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1782</guid>
		<description><![CDATA[<p>在编程的过程中，尤其是文本的处理，我们很多时候都需要正则表达式，比如，验证用户输入的Email是否格式正确，电 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e4%b8%80%e7%a7%8d%e6%9b%b4%e7%ac%a6%e5%90%88%e8%87%aa%e7%84%b6%e8%af%ad%e6%84%8f%e7%9a%84php%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f/">一种更符合自然语意的PHP正则表达式</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>在编程的过程中，尤其是文本的处理，我们很多时候都需要正则表达式，比如，验证用户输入的Email是否格式正确，电话号码是否符合规范，或者其他的自定义规则。对新手来说，熟记正则表达式的规则比较困难，那么有没有一种更符合自然语义的正则表达式呢，本文会给你答案。</p>
<p><img alt="verbalexp" src="http://www.codediesel.com/wp-content/uploads/2013/08/verbalexp.png" width="583" height="262" /></p>
<p>Most newbie (and some seasoned) programmers have difficultly constructing Regular Expressions. Many a times one needs to create a Regexp quickly to test a a particular piece of code. However, not being comfortable withe Regexps can be a problem. <a title="https://github.com/VerbalExpressions/PHPVerbalExpressions" href="https://github.com/VerbalExpressions/PHPVerbalExpressions" target="_blank">VerbalExpressions</a> is a PHP library that enables you to construct regular expressions using natural language like constructs. Think of it like a DSL for building Regexps.<span id="more-1782"></span><br />
verbalexpressions 已经实现了多个语言版本的库，可以从<a href="http://verbalexpressions.github.io/" target="_blank">这里</a>得到。</p>
<p>Below is a sample PHP code that constructs a regular expression which tests whether a given string is a valid url.</p>
<p>如下示例用来测试给定的一个string是否是有效的url地址。</p>
<pre>&lt;?php

include_once('VerbalExpressions.php');

$regex = new VerEx;

$regex  -&gt;startOfLine()
        -&gt;then("http")
        -&gt;maybe("s")
        -&gt;then("://")
        -&gt;maybe("www.")
        -&gt;anythingBut(" ")
        -&gt;endOfLine();

if($regex-&gt;test("http://www.codediesel.com"))
    echo "valid url";
else
    echo "invalid url";

?&gt;</pre>
<p>The main part of the code the DSL like interface that helps you build a Regexp.</p>
<pre>$regex  -&gt;startOfLine()
        -&gt;then("http")
        -&gt;maybe("s")
        -&gt;then("://")
        -&gt;maybe("www.")
        -&gt;anythingBut(" ")
        -&gt;endOfLine();</pre>
<p>If you want to see what regular expression the code has built, we can use the <strong>getRegex</strong> function of the class.</p>
<pre>&lt;?php

include_once('VerbalExpressions.php');

$regex = new VerEx;

$regex  -&gt;startOfLine()
        -&gt;then("http")
        -&gt;maybe("s")
        -&gt;then("://")
        -&gt;maybe("www.")
        -&gt;anythingBut(" ")
        -&gt;endOfLine();

echo $regex-&gt;getRegex();</pre>
<p>This will print the Regexp given below.</p>
<p>打印输出正则表达式。</p>
<pre>/^(http)(s)?(\:\/\/)(www\.)?([^ ]*)$/m</pre>
<p>We can now use the above Regexp in our code to accomplish the same thing as above.</p>
<pre>$myRegexp = '/^(http)(s)?(\:\/\/)(www\.)?([^ ]*)$/m';

if (preg_match($myRegexp, 'http://www.codediesel.com')) {
    echo 'valid url';
} else {
    echo 'invalud url';
}</pre>
<p>We can also use the <strong>$regex</strong> object given in the above example directly in our code where the particular Regexp is required.</p>
<p>可以使用函数 <strong>getRegex</strong> 获取正则表达式规则，或者直接可以用 <strong>$regex</strong> 对象，库内部已经实现了转换。</p>
<p>内部的转换函数：</p>
<pre>/**
* Object to string
*
* PHP Magic method to return a string representation of the object.
*
* @access public
* @return string
*/
    public function __toString()
    {
        return $this-&gt;getRegex();
    }</pre>
<pre>include_once('VerbalExpressions.php');

$regex = new VerEx;

$regex  -&gt;startOfLine()
        -&gt;then("http")
        -&gt;maybe("s")
        -&gt;then("://")
        -&gt;maybe("www.")
        -&gt;anythingBut(" ")
        -&gt;endOfLine();

if (preg_match($regex, 'http://www.codediesel.com')) {
    echo 'valid url';
} else {
    echo 'invalud url';
}</pre>
<p>The PHP version of VerbalExpressions is a port of the original JavaScript version,<a title="jsverbalexpressions" href="https://github.com/VerbalExpressions/JSVerbalExpressions" target="_blank">JSVerbalExpressions</a>. Additional modifiers that will help you build regular expressions can be found <a title="verbalexpressions wiki" href="https://github.com/VerbalExpressions/JSVerbalExpressions/wiki" target="_blank">here</a>.</p>
<p>文章节选：<a href="http://www.codediesel.com/php/constructing-hard-regular-expressions-with-verbalexpressions/" target="_blank">constructing-hard-regular-expressions-with-verbalexpressions</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e4%b8%80%e7%a7%8d%e6%9b%b4%e7%ac%a6%e5%90%88%e8%87%aa%e7%84%b6%e8%af%ad%e6%84%8f%e7%9a%84php%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f/">一种更符合自然语意的PHP正则表达式</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e4%b8%80%e7%a7%8d%e6%9b%b4%e7%ac%a6%e5%90%88%e8%87%aa%e7%84%b6%e8%af%ad%e6%84%8f%e7%9a%84php%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP中Session的使用</title>
		<link>http://blog.zhourunsheng.com/2013/08/php%e4%b8%adsession%e7%9a%84%e4%bd%bf%e7%94%a8/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/php%e4%b8%adsession%e7%9a%84%e4%bd%bf%e7%94%a8/#comments</comments>
		<pubDate>Thu, 15 Aug 2013 13:38:34 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1778</guid>
		<description><![CDATA[<p>php中使用session的场合主要是用户登录信息的记录和页面之间数据的传递，session的有效期为浏览器的 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/php%e4%b8%adsession%e7%9a%84%e4%bd%bf%e7%94%a8/">PHP中Session的使用</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>php中使用session的场合主要是用户登录信息的记录和页面之间数据的传递，session的有效期为浏览器的生存期，直到用户关闭浏览器，服务器中的session数据才清除。本文介绍了怎么样利用session来存储变量。</p>
<p>In this article I’m going to show you some simple things to work with sessions in php, these are mostly used to store information about your users, like usernames, choices selected by users and similar, these however are stored on the server and deleted once the user closes the browser. To interact with session variables you need to carefully create them and then check them and accordingly do something based on their value. This is actually the main topic of this article, how to check and create php session variables.<span id="more-1778"></span></p>
<h2>Starting sessions …【启用session】</h2>
<p>It is imperative that you first and foremost start the session on your website, otherwise the session variables will be lost as soon as the page is refreshed or goes to a different page. This can be easily done using the <strong>session_start()</strong> php function.</p>
<pre>&lt;?php session_start(); ?&gt;

&lt;html&gt;
    &lt;head&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;p&gt;Some content here&lt;/p&gt;
    &lt;/body&gt;
&lt;/html&gt;</pre>
<p>It is important that you use the <strong>session_start()</strong> function before any other code on your website.</p>
<h2>Creating php session variables【创建session变量】</h2>
<p>As you can see bellow, working wish session is similar to associative php arrays, the variable however needs to be <strong>$_SESSION</strong>. Then you create a session variable (more like an array element) by assigning it a value:</p>
<pre>&lt;?php
    session_start();
    $_SESSION['username']='Teddy';
    $_SESSION['logged']='yes';
?&gt;</pre>
<p>The above code will store the session variable ‘logged’ with the value ‘yes’ and based on this, you can probably show some specific links for a section that’s meant for logged users. Will show you in a second bellow.</p>
<h2>How to check session variable【访问session变量】</h2>
<p>Checking the session variable is important, else you’re just storing them for nothing, you need to make use of them in certain ways. Bellow its an example of using the above ‘logged’ session variable:</p>
<pre>&lt;?php
    session_start();
    $username = $_SESSION['username'];
    if(isset($_SESSION['logged']) &amp;&amp; $_SESSION['logged']=='yes') {
        echo "&lt;p&gt;&lt;a href='account-settings.php?user=$username' title='Account settings'&gt;Account settings&lt;/a&gt;&lt;/br&gt;";
        echo "&lt;p&gt;&lt;a href='view-profile.php' title='View profile&gt;View profile&lt;/a&gt;&lt;/br&gt;";
        echo "&lt;p&gt;&lt;a href='other-page.php' title='Other page'&gt;Some other page&lt;/a&gt;&lt;/br&gt;";
    }
// session is set, this will show similar as bellow:
// Account settings
// Edit account
// Some other page
?&gt;</pre>
<h2>Printing session variable【打印输出session变量】</h2>
<p>You can also print the session value wherever you need its value, for example:</p>
<pre>&lt;?php
    session_start();

    $username = $_SESSION['username'];
    if(isset($_SESSION['logged']) &amp;&amp; $_SESSION['logged']=='yes') {
        echo "Is $username logged in?&lt;br/&gt;";
        echo "Answer is: " . (isset($_SESSION['logged']) &amp;&amp; $_SESSION['logged']=='yes' ? $_SESSION['logged'] : "no");
    }
// Is Teddy logged in?
// Answer is: yes
?&gt;</pre>
<blockquote><p><strong>Heads up!</strong> If you are testing this, do not forget that sessions are being stored until you close your browser or until you clear your browser cookies and cache!</p></blockquote>
<p>As you can see, this is not exactly hard, but needs a bit of attention. Sessions are normally used for logged section, where you rely on session to show account level section for your users.</p>
<p>Do not forget that this is only an example to see how session work, you will need to secure these if you really wish to use them in production … but that’s another article that may come soon.</p>
<p>That’s it for now, don’t forget to share and follow our website for new articles!</p>
<p>文章节选：<a href="http://www.yourhowto.net/how-to-check-and-create-php-session-variables/" target="_blank">how-to-check-and-create-php-session-variables</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/php%e4%b8%adsession%e7%9a%84%e4%bd%bf%e7%94%a8/">PHP中Session的使用</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/php%e4%b8%adsession%e7%9a%84%e4%bd%bf%e7%94%a8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于PHP后台的Android云推送方案</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/#comments</comments>
		<pubDate>Thu, 08 Aug 2013 14:19:16 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1738</guid>
		<description><![CDATA[<p>开发过移动APP的同学都知道云推送，比如Google的后台服务，联系人同步，邮件同步，APK的自动升级等都是这 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/">基于PHP后台的Android云推送方案</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>开发过移动APP的同学都知道云推送，比如Google的后台服务，联系人同步，邮件同步，APK的自动升级等都是这个机制，其实利用云推送机制还可以实现消息的发送，本文介绍了一种基于PHP后台的推送机制，实现原理还是通过Google的云端服务(Google Cloud Messaging for Android service)，android客户端采用的是phoneGap的PushPlugin。</p>
<p>Last days I’ve been working within a <a href="http://phonegap.com/">Phonegap</a> project for Android devices using Push Notifications. The idea is simple. We need to use the Push Notification <a href="https://github.com/phonegap-build/PushPlugin">Plugin</a> for Android. First we need to register the Google Cloud Messaging for Android service at Google’s <a href="https://code.google.com/apis/console/">console</a>, and then we can send Push notifications to our Android device.</p>
<p>The Push Notification plugin provides a simple example to send notifications using <a href="https://github.com/phonegap-build/PushPlugin/blob/master/Example/server/pushGCM.rb">Ruby</a>. Normally my backend is built with PHP (and sometimes Python) so instead of using the ruby script we are going to build a simple PHP script to send Push Notifications.<span id="more-1738"></span></p>
<p>The script is very simple</p>
<div>
<pre>&lt;?php
$apiKey = "myApiKey";
$regId = "device reg ID";

$pusher = new AndroidPusher\Pusher($apiKey);
$pusher-&gt;notify($regId, "Hola");

print_r($pusher-&gt;getOutputAsArray());
$app-&gt;run();</pre>
</div>
<p>And the whole library you can see here:</p>
<div>
<pre>&lt;?php
namespace AndroidPusher;

class Pusher
{
    const GOOGLE_GCM_URL = '&lt;a href="https://android.googleapis.com/gcm/send"&gt;https://android.googleapis.com/gcm/send&lt;/a&gt;';

    private $apiKey;
    private $proxy;
    private $output;

    public function __construct($apiKey, $proxy = null)
    {
        $this-&gt;apiKey = $apiKey;
        $this-&gt;proxy  = $proxy;
    }

    /**
     * @param string|array $regIds
     * @param string $data
     * @throws \Exception
     */
    public function notify($regIds, $data)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, self::GOOGLE_GCM_URL);
        if (!is_null($this-&gt;proxy)) {
            curl_setopt($ch, CURLOPT_PROXY, $this-&gt;proxy);
        }
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $this-&gt;getHeaders());
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $this-&gt;getPostFields($regIds, $data));

        $result = curl_exec($ch);
        if ($result === false) {
            throw new \Exception(curl_error($ch));
        }

        curl_close($ch);

        $this-&gt;output = $result;
    }

    /**
     * @return array
     */
    public function getOutputAsArray()
    {
        return json_decode($this-&gt;output, true);
    }

    /**
     * @return object
     */
    public function getOutputAsObject()
    {
        return json_decode($this-&gt;output);
    }

    private function getHeaders()
    {
        return [
            'Authorization: key=' . $this-&gt;apiKey,
            'Content-Type: application/json'
        ];
    }

    private function getPostFields($regIds, $data)
    {
        $fields = [
            'registration_ids' =&gt; is_string($regIds) ? [$regIds] : $regIds,
            'data'             =&gt; is_string($data) ? ['message' =&gt; $data] : $data,
        ];

        return json_encode($fields, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
    }
}</pre>
</div>
<p>Maybe we could improve the library with a parser of google’s ouptuput, basically because we need to handle this output to notice if the user has uninstalled the app (and we need the remove his reg-id from our database), but at least now it cover all my needs.</p>
<p>源码下载：You can see the code at <a href="https://github.com/gonzalo123/androidpusher">github</a></p>
<p>参考文章：<a href="http://gonzalo123.com/2013/08/05/sending-android-push-notifications-from-php-to-phonegap-applications/?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+GonzaloAyuso+%28Gonzalo+Ayuso+%7C+Web+Architect%29">sending-android-push-notifications-from-php-to-phonegap-application</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/">基于PHP后台的Android云推送方案</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP 发送博文到百度空间</title>
		<link>http://blog.zhourunsheng.com/2013/05/php-%e5%8f%91%e9%80%81%e5%8d%9a%e6%96%87%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/</link>
		<comments>http://blog.zhourunsheng.com/2013/05/php-%e5%8f%91%e9%80%81%e5%8d%9a%e6%96%87%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/#comments</comments>
		<pubDate>Sun, 19 May 2013 03:59:31 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[百度空间]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1673</guid>
		<description><![CDATA[<p>昨天，测试了PHP方式登录百度空间，可以参见博文《PHP 登录百度空间》，今天通过抓包工具的分析，分析了博文的 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/05/php-%e5%8f%91%e9%80%81%e5%8d%9a%e6%96%87%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/">PHP 发送博文到百度空间</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>昨天，测试了PHP方式登录百度空间，可以参见博文《<a title="PHP 登录百度空间" href="http://blog.zhourunsheng.com/2013/05/php-%E7%99%BB%E5%BD%95%E7%99%BE%E5%BA%A6%E7%A9%BA%E9%97%B4/" target="_blank">PHP 登录百度空间</a>》，今天通过抓包工具的分析，分析了博文的发布过程，实现了博文的自动发布。<span id="more-1673"></span></p>
<p><strong>参照代码如下：</strong></p>
<pre>private function postArticle($baiduzoneurl, $title, $content, $category, $blogpower)
{
$post_data = array();
$post_data["title"] = $title;
$post_data["tags[]"] = $category;
$post_data["content"] = $content;
$post_data["private"] = $blogpower == 0? "" : "1";
$post_data["imgnum"] = "0";
$post_data["bdstoken"] = $this-&gt;bdstoken;
$post_data["qbid"] = "";
$post_data["refer"] = "http://hi.baidu.com/home";
$post_data["multimedia[]"] = "";
$post_data["private1"] = $post_data["private"];
$post_data["synflag"] = "";
$post_data["qing_request_source"] = "new_request";

$this-&gt;doHttpPost("http://hi.baidu.com/pub/submit/createtext", $post_data, "http://hi.baidu.com/pub/show/createtext");
}</pre>
<p>接下来再进行wordpress插件的更新，哈哈，这个就快多了！！！</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/05/php-%e5%8f%91%e9%80%81%e5%8d%9a%e6%96%87%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/">PHP 发送博文到百度空间</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/05/php-%e5%8f%91%e9%80%81%e5%8d%9a%e6%96%87%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP 登录百度空间</title>
		<link>http://blog.zhourunsheng.com/2013/05/php-%e7%99%bb%e5%bd%95%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/</link>
		<comments>http://blog.zhourunsheng.com/2013/05/php-%e7%99%bb%e5%bd%95%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/#comments</comments>
		<pubDate>Sat, 18 May 2013 14:00:20 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[百度空间]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1667</guid>
		<description><![CDATA[<p>前几天，先测试了利用Python脚本登录百度空间，问题已解决，可以参照我的博文《python 脚本登录百度空间 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/05/php-%e7%99%bb%e5%bd%95%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/">PHP 登录百度空间</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>前几天，先测试了利用Python脚本登录百度空间，问题已解决，可以参照我的博文《<a title="python 脚本登录百度空间" href="http://blog.zhourunsheng.com/2013/05/python-%e8%84%9a%e6%9c%ac%e7%99%bb%e5%bd%95%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/" target="_blank">python 脚本登录百度空间</a>》，今天完善了php方式的登录过程，原理同python脚本的登录。<span id="more-1667"></span></p>
<h3>代码如下:</h3>
<p><strong>登录代码</strong></p>
<pre>private function loginBaiduZone($user,$password)
{
//get Cookie
$ret = $this-&gt;doHttpPost("https://passport.baidu.com/v2/api/?getapi&amp;class=login&amp;tpl=mn&amp;tangram=false", "", "");

//get token
$ret = $this-&gt;doHttpPost("https://passport.baidu.com/v2/api/?getapi&amp;class=login&amp;tpl=mn&amp;tangram=false", "", "");
preg_match_all('/login_token=\'(.+)\'/', $ret, $tokens);
$login_token = $tokens[1][0];

//login
$post_data = array();
$post_data['username'] = $user;
$post_data['password'] = $password;
$post_data['token'] = $login_token;
$post_data['charset'] = "UTF-8";
$post_data['callback'] = "parent.bd12Pass.api.login._postCallback";
$post_data['index'] = "0";
$post_data['isPhone'] = "false";
$post_data['mem_pass'] = "on";
$post_data['loginType'] = "1";
$post_data['safeflg'] = "0";
$post_data['staticpage'] = "https://passport.baidu.com/v2Jump.html";
$post_data['tpl'] = "mn";
$post_data['u'] = "http://www.baidu.com/";
$post_data['verifycode'] = "";

$ret = $this-&gt;doHttpPost("http://passport.baidu.com/v2/api/?login", $post_data, "https://passport.baidu.com/v2/?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2F");
}</pre>
<p><strong>Http请求的工具函数</strong></p>
<pre>private function doHttpPost($url, $post_data, $referef)
{
$mcurl = curl_init();
curl_setopt($mcurl, CURLOPT_URL, $url);

if ($post_data != "")
{
curl_setopt($mcurl, CURLOPT_POST, 1);
curl_setopt($mcurl, CURLOPT_POSTFIELDS, $post_data);
}

if ($referef != "")
{
curl_setopt($mcurl, CURLOPT_REFERER, $referef);
}

curl_setopt($mcurl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($mcurl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($mcurl, CURLOPT_HEADER, 1);
curl_setopt($mcurl,    CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.92 Safari/537.1 LBBROWSER");

if ($this-&gt;cookie != "")
{
curl_setopt($mcurl, CURLOPT_COOKIE, $this-&gt;cookie);
}

$data = curl_exec($mcurl);
curl_close($mcurl);

preg_match_all('/Set-Cookie:((.+)=(.+))$/m ', $data, $cookies);
if(is_array($cookies) &amp;&amp; count($cookies) &gt; 1 &amp;&amp; count($cookies[1]) &gt; 0)
{
foreach($cookies[1] as $i =&gt; $k)
{
$cookieinfos = explode(";", $k);
if(is_array($cookieinfos) &amp;&amp; count($cookieinfos) &gt; 1)
{
$this-&gt;cookie .= $cookieinfos[0];
$this-&gt;cookie .= "; ";
}
}
}

return $data;
}</pre>
<p>接下来再测试博文的发布代码。</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/05/php-%e7%99%bb%e5%bd%95%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/">PHP 登录百度空间</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/05/php-%e7%99%bb%e5%bd%95%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Java PHP Json Mysql 中文乱码问题之解决</title>
		<link>http://blog.zhourunsheng.com/2012/06/java-php-json-mysql-%e4%b8%ad%e6%96%87%e4%b9%b1%e7%a0%81%e9%97%ae%e9%a2%98%e4%b9%8b%e8%a7%a3%e5%86%b3/</link>
		<comments>http://blog.zhourunsheng.com/2012/06/java-php-json-mysql-%e4%b8%ad%e6%96%87%e4%b9%b1%e7%a0%81%e9%97%ae%e9%a2%98%e4%b9%8b%e8%a7%a3%e5%86%b3/#comments</comments>
		<pubDate>Fri, 01 Jun 2012 03:15:55 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Json]]></category>
		<category><![CDATA[Mysql]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[中文乱码]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1619</guid>
		<description><![CDATA[<p>问题 客户端采用Java语言编写，服务器端采用PHP语言编写，数据库采用Mysql存储，客户端和服务器之间的交 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2012/06/java-php-json-mysql-%e4%b8%ad%e6%96%87%e4%b9%b1%e7%a0%81%e9%97%ae%e9%a2%98%e4%b9%8b%e8%a7%a3%e5%86%b3/">Java PHP Json Mysql 中文乱码问题之解决</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<h2>问题</h2>
<p>客户端采用Java语言编写，服务器端采用PHP语言编写，数据库采用Mysql存储，客户端和服务器之间的交互采用Json，在传递英文数据的时候没有问题，当传递中文数据数据的时候，就会出现中文乱码问题，mysql里面的中文全部变成问号了。</p>
<h2>解决方案</h2>
<ul>
<li>Mysql数据库，数据表，数据字段采用统一编码UTF-8, 如 utf8_general_ci</li>
<li>客户端Java字符串转成json格式的时候先进行urlencode处理
<pre>JSONObject jo = new JSONObject();
//jo.accumulate("note", note);
jo.accumulate("note", URLEncoder.encode(note));</pre>
</li>
<li>服务器端PHP转换json格式后，插入数据库前先进行urldecode处理
<pre>$data = array(
    'meta_key' =&gt; 'note',
    //'meta_value' =&gt; $params['note'],
    'meta_value' =&gt; urldecode($params['note']),
);</pre>
</li>
<li>经过以上的步骤处理，可完美解决中文乱码问题</li>
</ul>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2012/06/java-php-json-mysql-%e4%b8%ad%e6%96%87%e4%b9%b1%e7%a0%81%e9%97%ae%e9%a2%98%e4%b9%8b%e8%a7%a3%e5%86%b3/">Java PHP Json Mysql 中文乱码问题之解决</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2012/06/java-php-json-mysql-%e4%b8%ad%e6%96%87%e4%b9%b1%e7%a0%81%e9%97%ae%e9%a2%98%e4%b9%8b%e8%a7%a3%e5%86%b3/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>php使用ffmpeg-php扩展库进行视频截图</title>
		<link>http://blog.zhourunsheng.com/2012/04/php%e4%bd%bf%e7%94%a8ffmpeg-php%e6%89%a9%e5%b1%95%e5%ba%93%e8%bf%9b%e8%a1%8c%e8%a7%86%e9%a2%91%e6%88%aa%e5%9b%be/</link>
		<comments>http://blog.zhourunsheng.com/2012/04/php%e4%bd%bf%e7%94%a8ffmpeg-php%e6%89%a9%e5%b1%95%e5%ba%93%e8%bf%9b%e8%a1%8c%e8%a7%86%e9%a2%91%e6%88%aa%e5%9b%be/#comments</comments>
		<pubDate>Sun, 08 Apr 2012 08:23:25 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[多媒体]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1391</guid>
		<description><![CDATA[<p>这几天做项目，其中一个需求是用户上传视频文件到服务器，然后服务器自动截取该视频的一帧作为该视频对应的缩略图，服 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2012/04/php%e4%bd%bf%e7%94%a8ffmpeg-php%e6%89%a9%e5%b1%95%e5%ba%93%e8%bf%9b%e8%a1%8c%e8%a7%86%e9%a2%91%e6%88%aa%e5%9b%be/">php使用ffmpeg-php扩展库进行视频截图</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>这几天做项目，其中一个需求是用户上传视频文件到服务器，然后服务器自动截取该视频的一帧作为该视频对应的缩略图，服务器端语言采用php编写，找了半天资料，发现ffmpeg-php可以满足该需求，所以下面简单介绍一下该扩展库的配置和使用方法。<span id="more-1391"></span></p>
<p>测试服务器环境 WampServer Version 2.1，php5.3.5，ffmpeg-php 配置和使用步骤如下：</p>
<p>1. 下载 ffmpeg-php, <a href="http://carey-blog-image.googlecode.com/files/ffmpeg-php-5.3-win32-all.zip">ffmpeg-php-5.3-win32-all.zip</a></p>
<p>2. 将 php_ffmpeg.dll 放到 PHP 扩展目录，（例如 E:\Program Files\wamp\bin\php\php5.3.5\ext）</p>
<p>3. 将其他的dll文件放到系统目录，（例如 C:\Windows\System32）</p>
<p>4. 修改 php.ini，添加 extension=php_ffmpeg.dll 配置项，结果如下：</p>
<p><code><br />
extension=php_gd2.dll<br />
extension=php_gettext.dll<br />
extension=php_ffmpeg.dll<br />
</code></p>
<p>（<strong><span style="color: #ff0000;">注</span></strong>：Wamp 环境下要修改 apache 目录中的 php.ini,(例如E:\Program Files\wamp\bin\apache\Apache2.2.17\bin\php.ini), 同时要启用gd2 和 gettext 模块）</p>
<p>5. 重新启动 apache 服务器</p>
<p>6.打印输出 phpinfo，查看是否有如下输出，若有，则说明 ffmpeg-php 模块配置正确</p>
<h2><a name="module_ffmpeg"></a>ffmpeg</h2>
<table width="600" border="0" cellpadding="3">
<tbody>
<tr>
<td>ffmpeg-php version</td>
<td>0.6.0-svn</td>
</tr>
<tr>
<td>ffmpeg-php built on</td>
<td>Sep 8 2010 12:13:47</td>
</tr>
<tr>
<td>ffmpeg-php gd support</td>
<td>enabled</td>
</tr>
<tr>
<td>ffmpeg libavcodec version</td>
<td>Lavc52.87.4</td>
</tr>
<tr>
<td>ffmpeg libavformat version</td>
<td>Lavf52.78.3</td>
</tr>
<tr>
<td>ffmpeg swscaler version</td>
<td>SwS0.11.0</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<table width="600" border="0" cellpadding="3">
<tbody>
<tr>
<th>Directive</th>
<th>Local Value</th>
<th>Master Value</th>
</tr>
<tr>
<td>ffmpeg.allow_persistent</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>ffmpeg.show_warnings</td>
<td>0</td>
<td>0</td>
</tr>
</tbody>
</table>
<p>7. 编写测试代码，示例代码截取视频的第二帧为缩略图</p>
<pre>public function testmp4()
{
if(extension_loaded('ffmpeg')){//判断ffmpeg是否载入
$mov = new ffmpeg_movie('E:\\Program Files\\wamp\\www\\BBShow\\test\\guangfa15s.mp4');//视频的路径
dump($mov);
$ff_frame = $mov-&gt;getFrame(2);
$gd_image = $ff_frame-&gt;toGDImage();
$img=$_SERVER['DOCUMENT_ROOT']."/test.jpg";//要生成图片的绝对路径
imagejpeg($gd_image, $img);//创建jpg图像
imagedestroy($gd_image);//销毁一图像
}else{
echo "ffmpeg没有载入";
}
}</pre>
<p>8. 运行测试代码，即可在服务器端生成 jpg 格式的缩略图</p>
<p>9. 有关 ffmpeg-php 的详细使用说明，请参照官方API（<a href="http://ffmpeg-php.sourceforge.net/doc/api/">http://ffmpeg-php.sourceforge.net/doc/api/</a>）。</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2012/04/php%e4%bd%bf%e7%94%a8ffmpeg-php%e6%89%a9%e5%b1%95%e5%ba%93%e8%bf%9b%e8%a1%8c%e8%a7%86%e9%a2%91%e6%88%aa%e5%9b%be/">php使用ffmpeg-php扩展库进行视频截图</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2012/04/php%e4%bd%bf%e7%94%a8ffmpeg-php%e6%89%a9%e5%b1%95%e5%ba%93%e8%bf%9b%e8%a1%8c%e8%a7%86%e9%a2%91%e6%88%aa%e5%9b%be/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress 优化手记</title>
		<link>http://blog.zhourunsheng.com/2012/01/wordpress-%e4%bc%98%e5%8c%96%e6%89%8b%e8%ae%b0/</link>
		<comments>http://blog.zhourunsheng.com/2012/01/wordpress-%e4%bc%98%e5%8c%96%e6%89%8b%e8%ae%b0/#comments</comments>
		<pubDate>Mon, 09 Jan 2012 13:37:39 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1274</guid>
		<description><![CDATA[<p>个人主站建立了有一段的时间了，从域名注册，空间神马的东西鼓捣完了，国内的国外的都试过了，有些对建站有效的优化方 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2012/01/wordpress-%e4%bc%98%e5%8c%96%e6%89%8b%e8%ae%b0/">WordPress 优化手记</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>个人主站建立了有一段的时间了，从域名注册，空间神马的东西鼓捣完了，国内的国外的都试过了，有些对建站有效的优化方法和代码更新，做一下记录，以后更新的时候便于查询。</p>
<p><span id="more-1274"></span></p>
<h3>SEO 优化工作</h3>
<p>主要改动：header.php</p>
<p>改动原因：加入meta信息（网页描述“<span style="color: #ff00ff;">description</span>”和 网页关键词 "<span style="color: #ff00ff;">keywords</span>"）利于搜索引擎索引</p>
<p>代码更新：</p>
<pre>  &lt;?php
  //add by carey zhou on 2011/12/31
  // add meta keywords and description by conditionary
  function the_excerpt_max_charlength($charlength) {
   $excerpt = get_the_excerpt();
   $charlength++;

   if ( mb_strlen( $excerpt ) &gt; $charlength ) {
    $subex = mb_substr( $excerpt, 0, $charlength - 5 );
    $exwords = explode( ' ', $subex );
    $excut = - ( mb_strlen( $exwords[ count( $exwords ) - 1 ] ) );
    if ( $excut &lt; 0 ) {
     return mb_substr( $subex, 0, $excut );
    } else {
     return $subex;
    }
   } else {
    return $excerpt;
   }
  }
  $description = '';
  $keywords = '';

  if ( is_home() || is_front_page() ) {
    $description = '润物无声是周润生的个人博客空间，主要用来分享和记录最新的互联网科技知识，优秀的企业架构解决方案和程序设计开发技术，同时致力于打造一个知识分享的平台。';
    $keywords = 'Technology blog, Internet news, Program development, Architecture design,科技博客,互联网新闻,程序开发,架构设计';
  }
  else if (is_single()) {
    $exerpt = the_excerpt_max_charlength(200);
    if ( $exerpt != '') {
      $description = $exerpt;
    }
    else {
     $description = $post-&gt;post_title ;
    }
    $tags = wp_get_post_tags($post-&gt;ID);
    foreach ($tags as $tag ) {
      $keywords = $tag-&gt;name . ', ' . $keywords;
    }
    $keywords = $keywords . '润物无声';
  }

  if ( $description != '' ): ?&gt;
  &lt;meta name="description" content="&lt;?php echo "$description" ?&gt;" /&gt;
  &lt;?php endif;
  if ( $keywords != ''): ?&gt;
  &lt;meta name="keywords" content="&lt;?php echo "$keywords" ?&gt;" /&gt;
  &lt;?php endif;
  //add by carey zhou on 2011/12/31
  ?&gt;</pre>
<h3>优秀分页界面</h3>
<p>主要改动：loop.php</p>
<p>改动原因：加入分页界面支持，方便浏览博文</p>
<p>其他工作：需要分页插件支持：<a href="http://wordpress.org/extend/plugins/wp-pagenavi">http://wordpress.org/extend/plugins/wp-pagenavi</a></p>
<p>代码更新：</p>
<pre>&lt;div class="post-nav clearfix"&gt;
  &lt;?php if(function_exists(‘wp_pagenavi’)) : ?&gt;
  &lt;?php wp_pagenavi() ?&gt;
  &lt;?php else : ?&gt;
  &lt;p id="previous"&gt;&lt;?php previous_post_link(); ?&gt;&lt;/p&gt;
  &lt;p id="next-post"&gt;&lt;?php next_post_link(); ?&gt;&lt;/p&gt;
  &lt;?php endif; ?&gt;
  &lt;?php do_action('graphene_post_nav'); ?&gt;
&lt;/div&gt;</pre>
<h3>文章阅读次数支持</h3>
<p>主要改动：loop.php</p>
<p>改动原因：加入文章阅读次数的跟踪，便于筛选出用户最关注的博文</p>
<p>其他工作：需要记录文章阅读次数的插件支持：<a href="http://wordpress.org/extend/plugins/wp-postviews">http://wordpress.org/extend/plugins/wp-postviews</a></p>
<p>代码更新：</p>
<pre>  &lt;p class="post-tags"&gt;
  &lt;?php if (has_tag()) {_e('Tags:','graphene'); the_tags(' ', ', ', '');}
  else {_e('This post has no tag','graphene');} ?&gt;
  &lt;span&gt; 阅读次数：
  &lt;?php if(function_exists('the_views')) { the_views();} ?&gt;
  &lt;/span&gt;&lt;/p&gt;</pre>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2012/01/wordpress-%e4%bc%98%e5%8c%96%e6%89%8b%e8%ae%b0/">WordPress 优化手记</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2012/01/wordpress-%e4%bc%98%e5%8c%96%e6%89%8b%e8%ae%b0/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Google Plus API 之 程序设计</title>
		<link>http://blog.zhourunsheng.com/2011/09/google-plus-api-%e4%b9%8b-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/</link>
		<comments>http://blog.zhourunsheng.com/2011/09/google-plus-api-%e4%b9%8b-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/#comments</comments>
		<pubDate>Sat, 17 Sep 2011 05:25:22 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[SAE]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=888</guid>
		<description><![CDATA[<p>昨天Google官方发布新闻(Getting started on the Google+ API)，Goog [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/09/google-plus-api-%e4%b9%8b-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/">Google Plus API 之 程序设计</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>昨天Google官方发布新闻(<a href="http://googlecode.blogspot.com/2011/09/getting-started-on-google-api.html">Getting started on the Google+ <em>API</em></a>)，Google Plus开放了一个只读的API，允许抓取用户的公开Profile和公开的Activity信息流，其中<a href="http://developers.google.com/+/">Google+ Platform</a>的具体开发API介绍详见官网。</p>
<p>今天我就带领大家熟悉一下基于Google+ 开放API的程序设计，本程序基于SAE平台，采用PHP语言开发，基于Java语言和Python语言也都比较好开发，google官方都有开放的lib可以直接使用，不用自己再做底层封装了。<span id="more-888"></span></p>
<h3>实现步骤</h3>
<ol>
<li>访问 <a href="https://code.google.com/apis/console/?api=plus" rel="nofollow">Google API Console</a> 开启Google+ API权限.<br />
<img class="alignnone size-full wp-image-891" title="active_google_plus" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/active_google_plus.png" alt="" width="668" height="40" /></li>
<li>访问 <a href="https://code.google.com/apis/console/?api=plus" rel="nofollow">Google API Console</a> 生成  developer key, OAuth2 client id, OAuth2 client secret, 和 注册 OAuth2 redirect uri.<br />
<img class="alignnone size-full wp-image-890" title="google_plus_client_id" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/google_plus_client_id.png" alt="" width="754" height="151" /><br />
<img class="alignnone size-full wp-image-892" title="google-plus-simple-api-access" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/google-plus-simple-api-access.png" alt="" width="576" height="147" /></li>
<li>下载 google api的php开发库，<a href="http://google-api-php-client.googlecode.com/files/google-api-php-client-0.4.4.tar.gz">google-api-php-client</a>.</li>
<li>更新google api开发库，因为我的运行环境基于新浪云SAE平台，所以要做一些特别的改动：<br />
1. 实现SAE平台的MemCache，在google-api-php-clientsrccache中建立apiSAEMemcacheCache.php文件，具体内容如下</p>
<pre>&lt;?php
/**
 * SAE Memcache
 *
 * @author Carey Zhou &lt;zhourunsheng2008@google.com&gt;
 */
class apiSAEMemcacheCache extends apiCache {
  private $connection = false;

  public function __construct() {
	//do nothing
  }

  private function connect() {
    if (! $this-&gt;connection = @memcache_init()) {
      throw new apiCacheException("Memcache init failed");
    }
  }

  private function check() {
    if (! $this-&gt;connection) {
      $this-&gt;connect();
    }
  }

  /**
   * @inheritDoc
   */
  public function get($key, $expiration = false) {
    $this-&gt;check();
    if (($ret = @memcache_get($this-&gt;connection, $key)) === false) {
      return false;
    }
    if (! $expiration || (time() - $ret['time'] &gt; $expiration)) {
      $this-&gt;delete($key);
      return false;
    }
    return $ret['data'];
  }

  /**
   * @inheritDoc
   */
  public function set($key, $value) {
    $this-&gt;check();
    // we store it with the cache_time default expiration so objects will at least get cleaned eventually.
    if (@memcache_set($this-&gt;connection, $key, array('time' =&gt; time(),
        'data' =&gt; $value), false) == false) {
      throw new apiCacheException("Couldn't store data in cache");
    }
  }

  /**
   * @inheritDoc
   */
  public function delete($key) {
    $this-&gt;check();
    @memcache_delete($this-&gt;connection, $key);
  }
}</pre>
<p>2. 更新 google-api-php-clientsrccacheapiCache.php文件，将我们新添加的SAEMemcache加进去，代码如下：</p>
<pre>require_once "apiSAEMemcacheCache.php";</pre>
<p>3. 更新 google-api-php-clientsrcioapiCurlIO.php文件，修正一个bug【当Header中出现"HTTP/1.1 100 Continue"的时候，原程序会出现解析错误，导致程序崩溃，估计也是SAE环境的特殊性导致的】，具体改正如下：</p>
<p>原来的代码：</p>
<pre>// Parse out the raw response into usable bits
    list($rawResponseHeaders, $responseBody) = explode("rnrn", $data, 2);</pre>
<p>现在的代码：</p>
<pre>// Parse out the raw response into usable bits
    list($rawResponseHeaders, $responseBody) = explode("rnrn", $data, 2);
    if ($rawResponseHeaders == "HTTP/1.1 100 Continue") {
	list($rawResponseHeaders, $responseBody) = explode("rnrn", $responseBody, 2);
    }</pre>
<p>更改原因：如果出现了"HTTP/1.1 100 Continue"的时候，还要继续向下解析一次，即出现了特殊的“双Header现象”，否则的话就会导致Header解析出错，真正的Header没有被解析出来，也混到body里面去了。</li>
<li>google plus 代码编写，获取用户公开的Activity信息流，核心代码如下：
<pre>&lt;?php
/*
 * Google Plus
 *
 * @author Carey Zhou(zhourunsheng2008@gmail.com)
 */
require_once '../libs/google-api-php-client/src/apiClient.php';
require_once '../libs/google-api-php-client/src/contrib/apiPlusService.php';

//sae_set_display_errors(true);

session_start();

$client = new apiClient();
$client-&gt;setApplicationName("Google Plus Application");

$client-&gt;setClientId('800611776987-kp6h5nfv5l9gp5v2qipqhc5l8dkqe0lu.apps.googleusercontent.com');
$client-&gt;setClientSecret('5o-eK5M4iftPxB7_fhNqEAhm');
$client-&gt;setRedirectUri('https://carey.sinaapp.com/googleplus/index.php');
$client-&gt;setDeveloperKey('AIzaSyD6FERuKcWPn2xGUcgFmKW9Ush50nu6PYQ');

$client-&gt;setScopes(array('https://www.googleapis.com/auth/plus.me'));
$plus = new apiPlusService($client);

if (isset($_REQUEST['logout'])) {
  unset($_SESSION['access_token']);
}

if (isset($_GET['code'])) {
  $client-&gt;authenticate();
  $_SESSION['access_token'] = $client-&gt;getAccessToken();
  //header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']);
  $url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
  echo "&lt;script language='javascript' type='text/javascript'&gt;";
  echo "window.location.href='$url'";
  echo "&lt;/script&gt;";
}

if (isset($_SESSION['access_token'])) {
  $client-&gt;setAccessToken($_SESSION['access_token']);
}

if ($client-&gt;getAccessToken()) {
  $me = $plus-&gt;people-&gt;get('me');

  $optParams = array('maxResults' =&gt; 100);
  $activities = $plus-&gt;activities-&gt;listActivities('me', 'public', $optParams);

  // The access token may have been updated lazily.
  $_SESSION['access_token'] = $client-&gt;getAccessToken();
} else {
  $authUrl = $client-&gt;createAuthUrl();
}
?&gt;</pre>
<p>可以注意到，在程序中我们配置了ClientID，ClientSecret，RedirectUri，DeveloperKey和Google+的scope，然后通过OAuth2机制获取用户的授权，进而来获得用户公开的Activity信息流。</li>
</ol>
<h3>程序部署和验证</h3>
<ol>
<li>把上面的代码部署到SAE平台，<a href="https://carey.sinaapp.com/googleplus/index.php">https://carey.sinaapp.com/googleplus/index.php</a><br />
<img class="alignnone size-medium wp-image-901" title="google-plus-carey-sinaapp" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/google-plus-carey-sinaapp-300x161.png" alt="" width="300" height="161" /></li>
<li>点击Connet Me进行OAuth2用户授权<br />
<img class="alignnone size-medium wp-image-902" title="google-plus-user-oauth" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/google-plus-user-oauth-300x161.png" alt="" width="300" height="161" /></li>
<li>点击Allow Access允许访问，则会回到刚才的页面，把用户的信息流抓取出来<br />
<img class="alignnone size-medium wp-image-904" title="google-plus-get-activity" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/google-plus-get-activity-300x161.png" alt="" width="300" height="161" /></li>
</ol>
<h3>小结</h3>
<p>到此为止，google plus 基于PHP开发的基本流程就都OK了，希望google过几天能开放出更多的API来，毕竟google+是个圈，所以圈子的API才是最核心的，O(∩_∩)O哈哈~,期待中</p>
<p>源码下载<a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/google_plus_sae_source.zip">google_plus_sae_source</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/09/google-plus-api-%e4%b9%8b-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/">Google Plus API 之 程序设计</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/09/google-plus-api-%e4%b9%8b-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
