Version: current

      1 <?php
      2 /**
      3  * Library of conversion functions, implemented as static methods.
      4  *
      5  * The methods are all of the form (format)2(format), where the format is one of
      6  *
      7  *  raw: A UTF8 string
      8  *  attr: A UTF8 string suitable for inclusion in an HTML attribute
      9  *  js: A UTF8 string suitable for inclusion in a double-quoted javascript string.
     10  *
     11  *  array: A PHP associative array
     12  *  json: JavaScript object notation
     13  *
     14  *  html: HTML source suitable for use in a page or email
     15  *  text: Plain-text content, suitable for display to a user as-is, or insertion in a plaintext email.
     16  *
     17  * Objects of type {@link ViewableData} can have an "escaping type",
     18  * which determines if they are automatically escaped before output by {@link SSViewer}.
     19  *
     20  * @package sapphire
     21  * @subpackage misc
     22  */
     23 class Convert {
     24
     25     /**
     26      * Convert a value to be suitable for an XML attribute.
     27      *
     28      * @param array|string $val String to escape, or array of strings
     29      * @return array|string
     30      */
     31     static function raw2att($val) {
     32         if(is_array($val)) {
     33             foreach($val as $k => $v) $val[$k] = self::raw2att($v);
     34             return $val;
     35         } else {
     36             return str_replace(array('&','"',"'",'<','>'), array('&amp;','&quot;','&#39;','&lt;','&gt;'), $val);
     37         }
     38     }
     39
     40     /**
     41      * Convert a value to be suitable for an HTML attribute.
     42      *
     43      * This is useful for converting human readable values into
     44      * a value suitable for an ID or NAME attribute.
     45      *
     46      * @see http://www.w3.org/TR/REC-html40/types.html#type-cdata
     47      * @uses Convert::raw2att()
     48      * @param array|string $val String to escape, or array of strings
     49      * @return array|string
     50      */
     51     static function raw2htmlatt($val) {
     52         if(is_array($val)) {
     53             foreach($val as $k => $v) $val[$k] = self::raw2htmlatt($v);
     54             return $val;
     55         } else {
     56             $val = self::rus2lat($val);
     57             $val = self::raw2att($val);
     58             $val = preg_replace('/[^a-zA-Z0-9\-_]*/', '', $val);
     59             return $val;
     60         }
     61     }
     62
     63     /**
     64      * Ensure that text is properly escaped for XML.
     65      *
     66      * @see http://www.w3.org/TR/REC-xml/#dt-escape
     67      * @param array|string $val String to escape, or array of strings
     68      * @return array|string
     69      */
     70     static function raw2xml($val) {
     71         if(is_array($val)) {
     72             foreach($val as $k => $v) $val[$k] = self::raw2xml($v);
     73             return $val;
     74         } else {
     75             return str_replace(array('&','<','>',"\n",'"',"'"), array('&amp;','&lt;','&gt;','<br />','&quot;','&#39;'), $val);
     76         }
     77     }
     78
     79     /**
     80      * Ensure that text is properly escaped for Javascript.
     81      *
     82      * @param array|string $val String to escape, or array of strings
     83      * @return array|string
     84      */
     85     static function raw2js($val) {
     86         if(is_array($val)) {
     87             foreach($val as $k => $v) $val[$k] = self::raw2js($v);
     88             return $val;
     89         } else {
     90             return str_replace(array("\\", '"', "\n", "\r", "'"), array("\\\\", '\"', '\n', '\r', "\\'"), $val);
     91         }
     92     }
     93
     94     /**
     95      * Uses the PHP 5.2 native json_encode function if available,
     96      * otherwise falls back to the Services_JSON class.
     97      *
     98      * @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
     99      * @uses Director::baseFolder()
    100      * @uses Services_JSON
    101      *
    102      * @param mixed $val
    103      * @return string JSON safe string
    104      */
    105     static function raw2json($val) {
    106         if(function_exists('json_encode')) {
    107             return json_encode($val);
    108         } else {
    109             require_once(Director::baseFolder() . '/sapphire/thirdparty/json/JSON.php');
    110             $json = new Services_JSON();
    111             return $json->encode($val);
    112         }
    113     }
    114
    115     static function raw2sql($val) {
    116         if(is_array($val)) {
    117             foreach($val as $k => $v) $val[$k] = self::raw2sql($v);
    118             return $val;
    119         } else {
    120             return DB::getConn()->addslashes($val);
    121         }
    122     }
    123
    124     /**
    125      * Convert XML to raw text.
    126      * @uses html2raw()
    127      * @todo Currently &#xxx; entries are stripped; they should be converted
    128      */
    129     static function xml2raw($val) {
    130         if(is_array($val)) {
    131             foreach($val as $k => $v) $val[$k] = self::xml2raw($v);
    132             return $val;
    133         } else {
    134             // More complex text needs to use html2raw instead
    135             if(strpos($val,'<') !== false) return self::html2raw($val);
    136
    137             $converted = str_replace(array('&amp;','&lt;','&gt;','&quot;','&apos;', '&#39;'), array('&','<','>','"',"'", "'"), $val);
    138             $converted = ereg_replace('&#[0-9]+;', '', $converted);
    139             return $converted;
    140         }
    141     }
    142
    143     /**
    144      * Convert an array into a JSON encoded string.
    145      *
    146      * @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
    147      * @uses Director::baseFolder()
    148      * @uses Services_JSON
    149      *
    150      * @param array $val Array to convert
    151      * @return string JSON encoded string
    152      */
    153     static function array2json($val) {
    154         if(function_exists('json_encode')) {
    155             return json_encode($val);
    156         } else {
    157             require_once(Director::baseFolder() . '/sapphire/thirdparty/json/JSON.php');
    158             $json = new Services_JSON();
    159             return $json->encode($val);
    160         }
    161     }
    162
    163     /**
    164      * Convert a JSON encoded string into an object.
    165      *
    166      * @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
    167      * @uses Director::baseFolder()
    168      * @uses Services_JSON
    169      *
    170      * @param string $val
    171      * @return mixed JSON safe string
    172      */
    173     static function json2obj($val) {
    174         require_once(Director::baseFolder() . '/sapphire/thirdparty/json/JSON.php');
    175         $json = new Services_JSON();
    176         return $json->decode($val);
    177     }
    178
    179     /**
    180      * Convert a JSON string into an array.
    181      *
    182      * @uses json2obj
    183      * @param string $val JSON string to convert
    184      * @return array|boolean
    185      */
    186     static function json2array($val) {
    187         $json = self::json2obj($val);
    188         if(!$json) return false;
    189
    190         $arr = array();
    191         foreach($json as $k => $v) {
    192             $arr[$k] = $v;
    193         }
    194
    195         return $arr;
    196     }
    197
    198     /**
    199      * @uses recursiveXMLToArray()
    200      */
    201     static function xml2array($val) {
    202         $xml = new SimpleXMLElement($val);
    203         return self::recursiveXMLToArray($xml);
    204     }
    205
    206     /**
    207      * Function recursively run from {@link Convert::xml2array()}
    208      * @uses SimpleXMLElement
    209      */
    210     protected static function recursiveXMLToArray($xml) {
    211         if(is_object($xml) && get_class($xml) == 'SimpleXMLElement') {
    212             $attributes = $xml->attributes();
    213             foreach($attributes as $k => $v) {
    214                 if($v) $a[$k] = (string) $v;
    215             }
    216             $x = $xml;
    217             $xml = get_object_vars($xml);
    218         }
    219         if(is_array($xml)) {
    220             if(count($xml) == 0) return (string) $x; // for CDATA
    221             foreach($xml as $key => $value) {
    222                 $r[$key] = self::recursiveXMLToArray($value);
    223             }
    224             if(isset($a)) $r['@'] = $a; // Attributes
    225             return $r;
    226         }
    227         return (string) $xml;
    228     }
    229
    230     /**
    231      * Create a link if the string is a valid URL
    232      * @param string The string to linkify
    233      * @return A link to the URL if string is a URL
    234      */
    235     static function linkIfMatch($string) {
    236         if( preg_match( '/^[a-z+]+\:\/\/[a-zA-Z0-9$-_.+?&=!*\'()%]+$/', $string ) )
    237             return "<a style=\"white-space: nowrap\" href=\"$string\">$string</a>";
    238         else
    239             return $string;
    240     }
    241
    242     /**
    243      * Simple conversion of HTML to plaintext.
    244      *
    245      * @param $data string
    246      * @param $preserveLinks boolean
    247      * @param $wordwrap array
    248      */
    249     static function html2raw($data, $preserveLinks = false, $wordWrap = 0, $config = null) {
    250         $defaultConfig = array(
    251             'PreserveLinks' => false,
    252             'ReplaceBoldAsterisk' => true,
    253             'CompressWhitespace' => true,
    254             'ReplaceImagesWithAlt' => true,
    255         );
    256         if(isset($config)) {
    257             $config = array_merge($defaultConfig,$config);
    258         } else {
    259             $config = $defaultConfig;
    260         }
    261
    262         // sTRIp style and script
    263         /* $data = eregi_replace("<style(^A-Za-z0-9>][^>]*)?>.*</style[^>]*>","", $data);*/
    264         /* $data = eregi_replace("<script(^A-Za-z0-9>][^>]*)?>.*</script[^>]*>","", $data);*/
    265
    266         $data = preg_replace("/<style(^A-Za-z0-9>][^>]*)?>.*?<\/style[^>]*>/i","", $data);
    267         $data = preg_replace("/<script(^A-Za-z0-9>][^>]*)?>.*?<\/script[^>]*>/i","", $data);
    268
    269         if($config['ReplaceBoldAsterisk']) {
    270             $data = preg_replace('%<(strong|b)( [^>]*)?>|</(strong|b)>%i','*',$data);
    271         }
    272
    273         // Expand hyperlinks
    274         if(!$preserveLinks && !$config['PreserveLinks']) {
    275             $data = preg_replace_callback('/<a[^>]*href\s*=\s*"([^"]*)">(.*?)<\/a>/i', function ($m) { return Convert::html2raw($m[2]) . ' [' . $m[1] . ']'; }, $data);
    276             $data = preg_replace_callback('/<a[^>]*href\s*=\s*([^ ]*)>(.*?)<\/a>/i', function ($m) { return Convert::html2raw($m[2]) . ' [' . $m[1] . ']'; }, $data);
    277 /*
    278             $data = preg_replace('/<a[^>]*href\s*=\s*"([^"]*)">(.*?)<\/a>/ie', "Convert::html2raw('\\2').' [\\1]'", $data);
    279             $data = preg_replace('/<a[^>]*href\s*=\s*([^ ]*)>(.*?)<\/a>/ie', "Convert::html2raw('\\2').' [\\1]'", $data);
    280 */
    281         }
    282
    283         // Replace images with their alt tags
    284         if($config['ReplaceImagesWithAlt']) {
    285             $data = eregi_replace('<img[^>]*alt *= *"([^"]*)"[^>]*>', ' \\1 ', $data);
    286             $data = eregi_replace('<img[^>]*alt *= *([^ ]*)[^>]*>', ' \\1 ', $data);
    287         }
    288
    289         // Compress whitespace
    290         if($config['CompressWhitespace']) {
    291             $data = ereg_replace("[\n\r\t ]+", " ", $data);
    292         }
    293
    294         // Parse newline tags
    295         $data = ereg_replace("[ \n\r\t]*<[Hh][1-6]([^A-Za-z0-9>][^>]*)?> *", "\n\n", $data);
    296         $data = ereg_replace("[ \n\r\t]*<[Pp]([^A-Za-z0-9>][^>]*)?> *", "\n\n", $data);
    297         $data = ereg_replace("[ \n\r\t]*<[UuOo][Ll]([^A-Za-z0-9>][^>]*)?> *", "\n\n", $data);
    298         $data = ereg_replace("[ \n\r\t]*<[Dd][Ii][Vv]([^A-Za-z0-9>][^>]*)?> *", "\n\n", $data);
    299         $data = ereg_replace("\n\n\n+","\n\n", $data);
    300
    301         $data = ereg_replace("<[Ll][Ii]([^A-Za-z0-9>][^>]*)?> *", "\n", $data); // "\n* "
    302         $data = ereg_replace("<[Bb][Rr]([^A-Za-z0-9>][^>]*)?> *", "\n", $data);
    303         $data = ereg_replace("<[Tt][Rr]([^A-Za-z0-9>][^>]*)?> *", "\n", $data);
    304         $data = ereg_replace("</[Tt][Dd]([^A-Za-z0-9>][^>]*)?> *", "    ", $data);
    305         $data = preg_replace('/<\/p>/i', "\n\n", $data );
    306
    307         // Replace HTML entities
    308         //$data = preg_replace("/&#([0-9]+);/e", 'chr(\1)', $data);
    309         //$data = str_replace(array("&lt;","&gt;","&amp;","&nbsp;"), array("<", ">", "&", " "), $data);
    310         $data = html_entity_decode($data, ENT_COMPAT , 'UTF-8');
    311         // Remove all tags (but optionally keep links)
    312
    313         // strip_tags seemed to be restricting the length of the output
    314         // arbitrarily. This essentially does the same thing.
    315         if(!$preserveLinks && !$config['PreserveLinks']) {
    316             $data = preg_replace('/<\/?[^>]*>/','', $data);
    317         } else {
    318             $data = strip_tags($data, '<a>');
    319         }
    320         return ($wordWrap) ? trim(wordwrap(trim($data), $wordWrap)) : trim($data);
    321     }
    322
    323     /**
    324      * There are no real specifications on correctly encoding mailto-links,
    325      * but this seems to be compatible with most of the user-agents.
    326      * Does nearly the same as rawurlencode().
    327      * Please only encode the values, not the whole url, e.g.
    328      * "mailto:test@test.com?subject=" . Convert::raw2mailto($subject)
    329      *
    330      * @param $data string
    331      * @return string
    332      * @see http://www.ietf.org/rfc/rfc1738.txt
    333      */
    334     static function raw2mailto($data) {
    335         return str_ireplace(
    336             array("\n",'?','=',' ','(',')','&','@','"','\'',';'),
    337             array('%0A','%3F','%3D','%20','%28','%29','%26','%40','%22','%27','%3B'),
    338             $data
    339         );
    340     }
    341
    342     static function rus2lat($string) {
    343         $string = iconv('UTF-8', 'CP1251//TRANSLIT//IGNORE', $string);
    344         $rus = array("/ŕ/", "/á/", "/â/", "/ă/", "/ä/", "/ĺ/", "/¸/", "/ć/", "/ç/", "/č/", "/é/", "/ę/", "/ë/", "/ě/", "/í/", "/î/", "/ď/", "/đ/", "/ń/", "/ň/", "/ó/", "/ô/", "/ő/", "/ö/", "/÷/", "/ř/", "/ů/", "/ű/", "/ý/", "/ţ/", "/˙/", "/ü/", "/ú/", "/ż/", "/ş/",
    345             "/Ŕ/", "/Á/", "/Â/", "/Ă/", "/Ä/", "/Ĺ/", "/¨/", "/Ć/", "/Ç/", "/Č/", "/É/", "/Ę/", "/Ë/", "/Ě/", "/Í/", "/Î/", "/Ď/", "/Đ/", "/Ń/", "/Ň/", "/Ó/", "/Ô/", "/Ő/", "/Ö/", "/×/", "/Ř/", "/Ů/", "/Ű/", "/Ý/", "/Ţ/", "/ß/", "/Ü/", "/Ú/", "/Ż/", "/Ş/"
    346         );
    347
    348         $lat = array("a", "b", "v", "g", "d", "e", "e", "zh", "z", "i", "j", "k", "l", "m", "n", "o", "p", "r", "s", "t", "u", "f", "h", "c", "ch", "sh", "sh'", "y", "e", "yu", "ya", "'", "'", "i", "e",
    349             "A", "B", "V", "G", "D", "E", "E", "ZH", "Z", "I", "J", "K", "L", "M", "N", "O", "P", "R", "S", "T", "U", "F", "H", "C", "CH", "SH", "SH'", "Y", "E", "YU", "YA", "'", "'", "I", "E"
    350         );
    351         return preg_replace($rus, $lat, $string);
    352     }
    353
    354     /**
    355      * Generate correct name of object for integer number of object
    356      *
    357      * @param $num      int     number of object
    358      * @param $name1    string  name for *[1] object
    359      * @param $name2_4  string  name for *[2-4] objects
    360      * @param $nameOther    string  name for 11-19, *[0,5-9] objects
    361      * @return string
    362      */
    363     static function number2name($num, $name1, $name2_4, $nameOther) {
    364         if ($num >= 5 && $num <= 20) return $nameOther;
    365         $tail = $num % 100;
    366         $num = $num % 10;
    367         if ($num == 1) return $name1;
    368         if ($num >= 2 && $num <= 4 && ($tail < 11 || $tail > 19)) return $name2_4;
    369         return $nameOther;
    370     }
    371 }
    372
    373

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -