phpzend-frameworkcharacter-encodingzend-cache

Zend_Cache: After loading cached data, character encoding seems messed up


First; On my development server (localhost; default XAMPP on OSX) everything works fine, though when I deploy the exact same code (and data) to the staging server (managed Apache2 on Redhat) it breaks.

I'm caching some data using Zend_Cache using the File backend and auto-serialization. Special characters used in the original data display fine, though when they are loaded from cache they're all garbled up.

Anyone got a clue?

PS. Instead of just a workaround, I'm looking for a way to understand what might go "wrong" on the staging server. What could possibly mess this up?

UPDATE The data I'm caching is UTF-8 encoded.

UPDATE When looking at the raw cache files (of a serialized array) there i See one big difference; The data cached on my localhost shows no newlines when the (identical) data cached on the staging server does show newlines.

UPDATE Local server runs PHP 5.3, staging server runs PHP 5.2.10

UPDATE Running on Zend FW 1.10.8


Solution

  • I have almost identical state like you ,

    development machine is windows + php 5.3

    development machine is Linux + php 5.2.14

    ZF version is 1.10

    the only difference i had is : i used to add mb_internal_encoding("UTF-8"); in the bootstrap class

    FYI , I used to cache text (arabic language ) from database all encoded UTF8 when i open the file i see the arabic text as expected .

    UPDATE : 1- here is my complete initCache function just to make it clear

    public function _initCache() {
            mb_internal_encoding("UTF-8");
            $frontendOptions = array(
                'automatic_serialization' => TRUE,
                'lifetime' => 86400
            );
            $backendOptions = array(
                'cache_dir' => APPLICATION_PATH . "/configs/cache/",
                    ///'cache_dir' => sys_get_temp_dir(),
            );
            $cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);
            Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);
            Zend_Registry::set("cache", $cache);
        }
    

    Explanation : 1-Any php version earlier than PHP 6 doesn't have native support for UTF-8 , https://stackoverflow.com/questions/716703/what-is-coming-in-php-6

    2-making php 5.3 or 5.2 deal with UTF8 by using ICONV or MB_STRING

    simply by using var_dump(mb_internal_encoding());

    you can tell that php using ISO-8859-1 internally ,

    you can override it by var_dump(mb_internal_encoding("UTF-8"));

    it would output true (it success to override the internal encoding )

    to be honest i don't know if there is better solution or how bad it is ?? ,

    if you had any better i would be happy to adopt it :)

    UPDATE 2 in case you don't want to use that function , open this file "Zend/Cache/Backend/File.php" and go to the line 976 change this :

    protected function _filePutContents($file, $string)
    {
    
        $result = false;
        $f = @fopen($file, 'ab+');
        if ($f) {
            if ($this->_options['file_locking']) @flock($f, LOCK_EX);
            fseek($f, 0);
            ftruncate($f, 0);
            $tmp = @fwrite($f, $string);
            if (!($tmp === FALSE)) {
                $result = true;
            }
            @fclose($f);
        }
        @chmod($file, $this->_options['cache_file_umask']);
        return $result;
    }
    

    to be this :

    protected function _filePutContents($file, $string)
    {
        $string = mb_convert_encoding($string   , "UTF-8" , "ISO-8859-1"); // i didn't test it , use it at your own risk and i'd rather stick with the first solution 
        $result = false;
        $f = @fopen($file, 'ab+');
        if ($f) {
            if ($this->_options['file_locking']) @flock($f, LOCK_EX);
            fseek($f, 0);
            ftruncate($f, 0);
            $tmp = @fwrite($f, $string);
            if (!($tmp === FALSE)) {
                $result = true;
            }
            @fclose($f);
        }
        @chmod($file, $this->_options['cache_file_umask']);
        return $result;
    }
    

    i didn't test manually but it should work as expected

    Glad it helped !