yingzi 1 year ago
parent
commit
bbbc7c3fcf
100 changed files with 19155 additions and 0 deletions
  1. 27 0
      Mall/Bootstrap.php
  2. 28 0
      Mall/Framework/Cache/AbstractStorage.Class.php
  3. 609 0
      Mall/Framework/Cache/Redis.Class.php
  4. 52 0
      Mall/Framework/Cache/StorageInterface.Class.php
  5. 1188 0
      Mall/Framework/Common/Functions.php
  6. 63 0
      Mall/Framework/Core/Aes.Class.php
  7. 53 0
      Mall/Framework/Core/AutoLoad.Class.php
  8. 202 0
      Mall/Framework/Core/BaseImg.Class.php
  9. 36 0
      Mall/Framework/Core/Cache.Class.php
  10. 139 0
      Mall/Framework/Core/Config.Class.php
  11. 75 0
      Mall/Framework/Core/Cookie.Class.php
  12. 33 0
      Mall/Framework/Core/Db.Class.php
  13. 96 0
      Mall/Framework/Core/ErrorCode.Class.php
  14. 414 0
      Mall/Framework/Core/File.Class.php
  15. 1831 0
      Mall/Framework/Core/FirePHP.Class.php
  16. 382 0
      Mall/Framework/Core/Jwt.Class.php
  17. 215 0
      Mall/Framework/Core/Logs.Class.php
  18. 105 0
      Mall/Framework/Core/RedisQueue.Class.php
  19. 407 0
      Mall/Framework/Core/Request.Class.php
  20. 93 0
      Mall/Framework/Core/ResultWrapper.Class.php
  21. 300 0
      Mall/Framework/Core/Search.Class.php
  22. 148 0
      Mall/Framework/Core/SendMail.Class.php
  23. 73 0
      Mall/Framework/Core/SendSms.Class.php
  24. 180 0
      Mall/Framework/Core/Session.Class.php
  25. 1070 0
      Mall/Framework/Core/SqlHelper.Class.php
  26. 710 0
      Mall/Framework/Core/StatusCode.Class.php
  27. 33 0
      Mall/Framework/Core/Swoole.Class.php
  28. 17 0
      Mall/Framework/Core/Url.Class.php
  29. 110 0
      Mall/Framework/Core/VerificationCode.Class.php
  30. 60 0
      Mall/Framework/Core/View.Class.php
  31. 650 0
      Mall/Framework/Db/Db.Class.php
  32. 385 0
      Mall/Framework/Db/DbMssql.Class.php
  33. 407 0
      Mall/Framework/Db/DbOci.php
  34. 209 0
      Mall/Framework/Factory.Class.php
  35. 272 0
      Mall/Framework/Mail/Mail.Class.php
  36. 118 0
      Mall/Framework/SearchClient/Bulk.Class.php
  37. 468 0
      Mall/Framework/SearchClient/Client.Class.php
  38. 79 0
      Mall/Framework/SearchClient/DSL/Builder.Class.php
  39. 94 0
      Mall/Framework/SearchClient/DSL/Query.Class.php
  40. 117 0
      Mall/Framework/SearchClient/DSL/RangeQuery.Class.php
  41. 117 0
      Mall/Framework/SearchClient/DSL/Stringify.Class.php
  42. 14 0
      Mall/Framework/SearchClient/Exception.Class.php
  43. 73 0
      Mall/Framework/SearchClient/Mapping.Class.php
  44. 148 0
      Mall/Framework/SearchClient/Transport/Base.Class.php
  45. 346 0
      Mall/Framework/SearchClient/Transport/HTTP.Class.php
  46. 59 0
      Mall/Framework/SearchClient/Transport/HTTPException.Class.php
  47. 127 0
      Mall/Framework/Session/SaveHandler/RedisSession.Class.php
  48. 50 0
      Mall/Framework/Session/SaveHandler/SaveHandlerInterface.Class.php
  49. 110 0
      Mall/Framework/Smarty/libs/Autoloader.php
  50. 1549 0
      Mall/Framework/Smarty/libs/Smarty.class.php
  51. 455 0
      Mall/Framework/Smarty/libs/SmartyBC.class.php
  52. 17 0
      Mall/Framework/Smarty/libs/bootstrap.php
  53. 160 0
      Mall/Framework/Smarty/libs/debug.tpl
  54. 117 0
      Mall/Framework/Smarty/libs/plugins/block.textformat.php
  55. 73 0
      Mall/Framework/Smarty/libs/plugins/function.counter.php
  56. 105 0
      Mall/Framework/Smarty/libs/plugins/function.cycle.php
  57. 221 0
      Mall/Framework/Smarty/libs/plugins/function.fetch.php
  58. 252 0
      Mall/Framework/Smarty/libs/plugins/function.html_checkboxes.php
  59. 167 0
      Mall/Framework/Smarty/libs/plugins/function.html_image.php
  60. 209 0
      Mall/Framework/Smarty/libs/plugins/function.html_options.php
  61. 235 0
      Mall/Framework/Smarty/libs/plugins/function.html_radios.php
  62. 397 0
      Mall/Framework/Smarty/libs/plugins/function.html_select_date.php
  63. 374 0
      Mall/Framework/Smarty/libs/plugins/function.html_select_time.php
  64. 176 0
      Mall/Framework/Smarty/libs/plugins/function.html_table.php
  65. 153 0
      Mall/Framework/Smarty/libs/plugins/function.mailto.php
  66. 109 0
      Mall/Framework/Smarty/libs/plugins/function.math.php
  67. 101 0
      Mall/Framework/Smarty/libs/plugins/modifier.capitalize.php
  68. 83 0
      Mall/Framework/Smarty/libs/plugins/modifier.date_format.php
  69. 112 0
      Mall/Framework/Smarty/libs/plugins/modifier.debug_print_var.php
  70. 232 0
      Mall/Framework/Smarty/libs/plugins/modifier.escape.php
  71. 58 0
      Mall/Framework/Smarty/libs/plugins/modifier.regex_replace.php
  72. 39 0
      Mall/Framework/Smarty/libs/plugins/modifier.replace.php
  73. 27 0
      Mall/Framework/Smarty/libs/plugins/modifier.spacify.php
  74. 66 0
      Mall/Framework/Smarty/libs/plugins/modifier.truncate.php
  75. 29 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.cat.php
  76. 32 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.count_characters.php
  77. 27 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.count_paragraphs.php
  78. 27 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.count_sentences.php
  79. 32 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.count_words.php
  80. 35 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.default.php
  81. 119 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.escape.php
  82. 33 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.from_charset.php
  83. 33 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.indent.php
  84. 31 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.lower.php
  85. 21 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.noprint.php
  86. 25 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.string_format.php
  87. 33 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.strip.php
  88. 29 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.strip_tags.php
  89. 33 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.to_charset.php
  90. 50 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.unescape.php
  91. 29 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.upper.php
  92. 51 0
      Mall/Framework/Smarty/libs/plugins/modifiercompiler.wordwrap.php
  93. 89 0
      Mall/Framework/Smarty/libs/plugins/outputfilter.trimwhitespace.php
  94. 34 0
      Mall/Framework/Smarty/libs/plugins/shared.escape_special_chars.php
  95. 36 0
      Mall/Framework/Smarty/libs/plugins/shared.literal_compiler_param.php
  96. 45 0
      Mall/Framework/Smarty/libs/plugins/shared.make_timestamp.php
  97. 55 0
      Mall/Framework/Smarty/libs/plugins/shared.mb_str_replace.php
  98. 54 0
      Mall/Framework/Smarty/libs/plugins/shared.mb_unicode.php
  99. 75 0
      Mall/Framework/Smarty/libs/plugins/shared.mb_wordwrap.php
  100. 19 0
      Mall/Framework/Smarty/libs/plugins/variablefilter.htmlspecialchars.php

+ 27 - 0
Mall/Bootstrap.php

@@ -0,0 +1,27 @@
+<?php
+define('DS', DIRECTORY_SEPARATOR);
+define('ROOT_PATH', __DIR__);
+define('FRAMEWORK_PATH', ROOT_PATH . DS . 'Framework');
+define('FRAMEWORK_NAME', 'Mall');
+define('PACKAGE_EOF', '--END--');
+define('PACKAGE_MAXLENG', 2465792);
+define('PACKAGE_BODY_OFFSET', 12);
+
+# 默认将显示错误关闭
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+ini_set('xdebug.remote_enable', 0);
+
+# 记录所有错误到日志文件
+define('LOG_ERROR', TRUE);
+
+# 是否开启队列日志
+define('QUEUE_RUNLOG', true);
+
+# 设置默认时区
+date_default_timezone_set('PRC');
+
+require_once FRAMEWORK_PATH . DS . 'Common' . DS . 'Functions.php';
+require_once FRAMEWORK_PATH . DS . 'Core' . DS . 'AutoLoad.Class.php';
+
+spl_autoload_register(array('Mall\Framework\Core\AutoLoad', '_autoLoad'));

+ 28 - 0
Mall/Framework/Cache/AbstractStorage.Class.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace Mall\Framework\Cache;
+
+abstract class AbstractStorage implements StorageInterface
+{
+	protected $prefix = 'mallguang::';
+
+    protected $options = array();
+
+    protected $ttl = 0;
+
+	public function __construct($options = null)
+	{
+        if (!is_array($options)) {
+            throw new \Exception('Cache options cannot be found');
+        }
+
+        $this->options = $options;
+        $prefix = FALSE;
+
+        if ($this->options['prefix']) {
+            $prefix = str_replace(':', '', $this->options['prefix']) . '::';
+        }
+
+        $this->prefix = $prefix ?: $this->prefix;
+    }
+}

+ 609 - 0
Mall/Framework/Cache/Redis.Class.php

@@ -0,0 +1,609 @@
+<?php
+
+namespace Mall\Framework\Cache;
+
+use Redis as RedisSource;
+use Mall\Framework\Cache\AbstractStorage as Storage;
+
+class Redis extends Storage
+{
+
+    /**
+     * @var RedisSource|array
+     */
+    protected $resource;
+
+
+    public function __construct($options)
+    {
+        parent::__construct($options);
+
+        //判断redis扩展是否已经安装
+        if(!extension_loaded('redis')){
+            throw new \Exception('Redis extension is not found');
+        }
+
+        $this->resource = new RedisSource();
+        $port = $this->options['port'] ?: 6379;
+
+        if (!$this->resource->connect($this->options['host'], $port, 3)) {
+            throw new \Exception(sprintf(
+                'Cannot connect to redis server on %s:%d',
+                $this->options['host'], $this->options['port']
+            ));
+        }
+        if ($this->options['auth'] && !$this->resource->auth($this->options['auth'])) {
+            throw new \Exception(sprintf(
+                'Auth failed on %s:%d, auth: %s',
+                $this->options['host'], $this->options['port'], $this->options['auth']
+            ));
+        }
+
+        if (isset($this->options['database']) && !$this->resource->select($this->options['database'])) {
+            throw new \Exception(sprintf(
+                'Select Database failed on %s:%d, auth: %s, database:%d',
+                $this->options['host'], $this->options['port'], $this->options['auth'], $this->options['database']
+            ));
+        }
+
+        $this->resource->setOption(RedisSource::OPT_PREFIX, 'Mall_');
+
+    }
+
+    public function set($key, $value = null, $ttl = null)
+    {
+        $key = $this->prefix . $key;
+        $ttl = $ttl ?: 0;
+        $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;
+
+        if ($ttl > 0) {
+            $ret = $this->resource->setex($key, $ttl, $value);
+        } else {
+            $ret = $this->resource->set($key, $value);
+        }
+
+        return $ret;
+    }
+
+    public function get($key)
+    {
+        $key = $this->prefix . $key;
+
+        $value = $this->resource->get($key);
+        $jsonData = json_decode($value, true);
+
+        return ($jsonData === NULL) ? $value : $jsonData;
+    }
+
+    public function incr($key)
+    {
+        $key = $this->prefix . $key;
+
+        return $this->resource->incr($key);
+    }
+
+    /**
+     * 将 key 所储存的值加上增量 increment
+     * @param $key
+     * @return int
+     */
+    public function incrby($key, $increment)
+    {
+        $key = $this->prefix . $key;
+
+        return $this->resource->incrBy($key, $increment);
+    }
+
+    public function del($key)
+    {
+        if(is_array($key)){
+            foreach ($key as $k => $v){
+                $key[$k] = $this->prefix . $v;
+            }
+        }else{
+            $key = $this->prefix . $key;
+        }
+        $ret = $this->resource->del($key);
+        return $ret;
+    }
+
+    public function has($key)
+    {
+        $key = $this->prefix . $key;
+
+        $ret = $this->resource->exists($key);
+
+        return $ret;
+    }
+
+    public function delete($key)
+    {
+        $key = $this->prefix . $key;
+
+        $ret = $this->resource->delete($key) > 0;
+
+        return $ret;
+    }
+
+    public function push($key, $value, $type = 'end')
+    {
+        $key = $this->prefix . $key;
+        $ret = FALSE;
+
+        if ($type == 'end') {
+            $ret = $this->resource->rpush($key, $value);
+        } else if ($type == 'start') {
+            $ret = $this->resource->lpush($key, $value);
+        }
+
+        return $ret;
+    }
+
+    public function pop($key, $type = 'start')
+    {
+        $key = $this->prefix . $key;
+        $ret =FALSE;
+
+        if ($type == 'end') {
+            $ret = $this->resource->rpop($key);
+        } else if ($type == 'start') {
+            $ret = $this->resource->lpop($key);
+        }
+
+        return $ret;
+    }
+
+    /**
+     * 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
+     * @param $key
+     * @param $start
+     * @param $stop
+     * @return array
+     */
+    public function lrange($key, $start, $stop)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->lRange($key, $start, $stop);
+    }
+
+    /**
+     * 返回列表 key 中,下标为 index 的元素
+     * @param $key
+     * @param $start
+     * @param $stop
+     * @return array
+     */
+    public function lindex($key, $index)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->lIndex($key, $index);
+    }
+
+    /**
+     * 根据参数 count 的值,移除列表中与参数 value 相等的元素
+     */
+    public function lrem($key, $value, $count = 0)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->lRem($key, $value, $count);
+    }
+
+    /**
+     * @param $key
+     * @param array['field1'=>'value', 'field2'=>'value'] $values
+     * @return int
+     */
+    public function hmset($key, $values){
+        $key = $this->prefix . $key;
+
+        return $this->resource->hMSet($key, $values);
+    }
+
+    /**
+     * @param $key
+     * @param  array['field1', 'field2'] $value
+     *
+     * @return int
+     */
+    public function hmget($key, $fields){
+        $key = $this->prefix . $key;
+        return $this->resource->hMGet($key, $fields);
+    }
+
+    public function hset($key, $field, $value){
+        $key = $this->prefix . $key;
+        return $this->resource->hSet($key, $field, $value);
+    }
+
+    public function hget($key, $field){
+        $key = $this->prefix . $key;
+        return $this->resource->hGet($key, $field);
+    }
+
+    public function hdel($key, $field){
+        $key = $this->prefix . $key;
+        return $this->resource->hDel($key, $field);
+    }
+
+    public function hkeys($key){
+        $key = $this->prefix . $key;
+        return $this->resource->hKeys($key);
+    }
+
+    public function hvalues($key){
+        $key = $this->prefix . $key;
+        return $this->resource->hVals($key);
+    }
+
+    /**
+     * @param $key
+     * @param $field
+     * @return bool
+     */
+    public function hExists($key, $field)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->hExists($key, $field);
+    }
+
+    public function hIncrBy($key, $field,$value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->hIncrBy($key, $field,$value);
+    }
+
+    public function hIncrByFloat($key, $field,$value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->hIncrByFloat($key, $field,$value);
+    }
+
+    /**
+     * 集合
+     *
+     * @param $key
+     * @param $value
+     *
+     * @return bool
+     */
+    public function sadd($key, $value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->sAdd($key, $value);
+    }
+
+    /**
+     * 集合sadd统计
+     *
+     * @param $key
+     *
+     * @return int
+     */
+    public function scard($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->sCard($key);
+    }
+
+    /**
+     * 移除集 key 中的一个或多个成员,不存在的成员将被忽略。
+     */
+    public function srem($key, $value){
+        $key = $this->prefix . $key;
+        return $this->resource->sRem($key, $value);
+    }
+
+    /**
+     * 随机取集 key 中的一个或多个成员,不存在的成员将被忽略。
+     * @param $value 获取的条数,默认1
+     */
+    public function srandmember($key, $value=1){
+        $key = $this->prefix . $key;
+        return $this->resource->sRandMember($key,$value);
+    }
+
+    /**
+     * 设置客户端
+     */
+    public function setOption($option, $value)
+    {
+        return $this->resource->setOption($option, $value);
+    }
+
+    /**
+     * 迭代查询
+     * @params string $key 集合名
+     * @params string $keyword 要查询的关键词
+     */
+    public function scan($key, $keyword)
+    {
+        $key = $this->prefix . $key;
+        $iterator = null;
+        while ($members = $this->resource->sScan($key, $iterator, $keyword)) {
+            foreach ($members as $member) {
+                echo $member . PHP_EOL;
+            }
+        }
+    }
+
+
+    /**
+     * 有序集合
+     *
+     * @param $key
+     * @param $value
+     *
+     * @return bool
+     */
+    public function zadd($key, $score , $value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zAdd($key, $score, $value);
+    }
+
+    /**
+     * 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员
+     */
+    public function zrangebyscore($key, $start, $end, array $options = array()){
+        $key = $this->prefix . $key;
+        return $this->resource->zRangeByScore($key, $start, $end, $options);
+    }
+
+    /**
+     * 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。
+     */
+    public function zrem($key, $value){
+        $key = $this->prefix . $key;
+        return $this->resource->zRem($key, $value);
+    }
+
+    /**
+     * 除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员
+     */
+    public function zremrangebyscore($key, $start, $end){
+        $key = $this->prefix . $key;
+        return $this->resource->zRemRangeByScore($key, $start, $end);
+    }
+
+    /**
+     * 判断member元素是否是集合Key的成员
+     *
+     * @param $key
+     * @param $value
+     *
+     * @return bool
+     */
+    public function sismember($key, $value)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->sIsMember($key, $value);
+    }
+
+    /**
+     * 返回集合中的所有成员
+     *
+     * @param $key
+     *
+     * @return bool
+     */
+    public function smembers($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->sMembers($key);
+    }
+
+    /**
+     * 返回集合之间的差集
+     *
+     * @param string $key1
+     * @param string $key2
+     *
+     * @return object
+     */
+    public function sdiff($key1, $key2)
+    {
+        $key1 = $this->prefix . $key1;
+        $key2 = $this->prefix . $key2;
+
+        return $this->resource->sDiff($key1, $key2);
+    }
+
+    /**
+     * 返回集合之间的交集
+     *
+     * @param string $key1
+     * @param string $key2
+     *
+     * @return object
+     */
+    public function sinter($key1, $key2)
+    {
+        $key1 = $this->prefix . $key1;
+        $key2 = $this->prefix . $key2;
+
+        return $this->resource->sInter($key1, $key2);
+    }
+
+    /**
+     * 计算集合之间的交集并写入一个新的key中
+     *
+     * @param string $deskey  要存入的新key
+     * @param string $key1
+     * @param string $key2
+     *
+     * @return int 新集合得数量
+     */
+    public function sinterStore($deskey, $key1, $key2)
+    {
+        $deskey = $this->prefix .$deskey;
+        $key1 = $this->prefix . $key1;
+        $key2 = $this->prefix . $key2;
+
+        return $this->resource->sInterStore($deskey, $key1, $key2);
+    }
+
+    /**
+     * 返回集合之间的并集
+     *
+     * @param string $key1
+     * @param string $key2
+     *
+     * @return object
+     */
+    public function sunion($key1, $key2)
+    {
+        $key1 = $this->prefix . $key1;
+        $key2 = $this->prefix . $key2;
+
+        return $this->resource->sUnion($key1, $key2);
+    }
+
+    /**
+     * 事物支持
+     */
+    public function multi()
+    {
+        return $this->resource->multi();
+    }
+
+    /**
+     * 执行事务
+     */
+    public function exec()
+    {
+        return $this->resource->exec();
+    }
+
+    /**
+     * 返回整数为列表键长度
+     *
+     * @param string $key
+     *
+     * @return int
+     */
+    public function llen($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->llen($key);
+    }
+
+    /**
+     * 有序集合中对指定成员的分数加上增量 increment
+     *
+     * @param $key
+     * @param $value
+     * @param $member
+     *
+     * @return float
+     */
+    public function zincrby($key, $value, $member)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zIncrBy($key, $value, $member);
+    }
+
+    /**
+     * 返回有序集中,成员的分数值
+     *
+     * @param $key
+     * @param $member
+     *
+     * @return float
+     */
+    public function zscore($key, $member)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zScore($key, $member);
+    }
+
+    /**
+     * 获取有序集合的成员数
+     *
+     * @param $key
+     *
+     * @return int
+     */
+    public function zcard($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zCard($key);
+    }
+
+    /**
+     * 返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量
+     *
+     * @return int
+     */
+    public function zcount($key, $min, $max)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zCount($key, $min, $max);
+    }
+
+    /**
+     * 返回有序集 key 中,指定区间内的成员。
+     *
+     * @param $key
+     * @param $start
+     * @param $end
+     *
+     * @return array
+     */
+    public function zrange($key, $start, $end, $withscores = null)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zRange($key, $start, $end, $withscores);
+    }
+
+    /**
+     * 返回有序集 key 中,指定区间内的成员,按score从大到小的顺序
+     *
+     * @param $key
+     * @param $start
+     * @param $end
+     *
+     * @return array
+     */
+    public function zRevRange($key, $start, $end, $withscores = null)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->zRevRange($key, $start, $end, $withscores);
+    }
+
+    /**
+     * 获取所有的keys
+     */
+    public function keys($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->keys($key);
+    }
+
+    /**
+     * 获取配置项的值
+     */
+    public function getOption($name)
+    {
+        return $this->resource->getOption($name);
+    }
+
+    /**
+     * 设置key的生存时间
+     */
+    public function expire($key, $time)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->expire($key, $time);
+    }
+
+    /**
+     * 获取键到期的剩余时间(秒)
+     * @param $key
+     * @return bool|int
+     */
+    public function ttl($key)
+    {
+        $key = $this->prefix . $key;
+        return $this->resource->ttl($key);
+    }
+}

+ 52 - 0
Mall/Framework/Cache/StorageInterface.Class.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Mall\Framework\Cache;
+
+interface StorageInterface
+{
+	/**
+	 * @param string $key
+     *
+	 * @return mixed
+	 */
+	public function get($key);
+
+    /**
+     * @param $key
+     * @param null $value
+     * @param null $ttl
+     *
+     * @return mixed
+     */
+    public function set($key, $value = null, $ttl = null);
+
+    /**
+     * @param string $key
+     *
+     * @return bool
+     */
+    public function has($key);
+
+    /**
+     * @param string $key
+     *
+     * @return bool
+     */
+    public function delete($key);
+
+    /**
+     * @param string $key
+     * @param mixed $value
+     * @param string $type start | end
+     *
+     * @return int|bool The new value on success, false on failure
+     */
+    public function push($key, $value, $type = 'end');
+    /**
+     * @param string $key
+     * @param string $type start | end
+     * @return int|bool The new value on success, false on failure
+     */
+    public function pop($key, $type = 'start');
+
+}

+ 1188 - 0
Mall/Framework/Common/Functions.php

@@ -0,0 +1,1188 @@
+<?php
+
+function getArrayItem(array $array,$key = 0, $defaultValue = '')
+{
+    if (!is_array($array)){
+        return $defaultValue;
+    }
+    return (isset($array[$key]) && !empty($array[$key])) ? $array[$key] : $defaultValue;
+}
+
+function loadFile($filepath, $return = false)
+{
+    if (file_exists($filepath)) {
+        if ($return) {
+            return include $filepath;
+        } else {
+            include $filepath;
+        }
+    } else {
+        throw new \Exception($filepath . ' file cannot be found');
+    }
+}
+
+function xssCheck()
+{
+    if (isset($_SERVER['REQUEST_URI'])) {
+        $temp = strtoupper(urldecode(urldecode($_SERVER['REQUEST_URI'])));
+        if (strpos($temp, '<') !== false || strpos($temp, '"') !== false || strpos($temp, 'CONTENT-TRANSFER-ENCODING') !== false) {
+            throw new \Exception('request_tainting' . $temp);
+        }
+    }
+
+    return true;
+}
+
+/**
+ * @param $module string 服务模块名
+ * @throws Exception
+ */
+function loadServiceConfig($module)
+{
+    if (!$module) {
+        throw new \Exception('module not null');
+    }
+
+    $serviceConfig = ROOT_PATH . DS . '..' . DS . 'Service' . DS . ucfirst($module) . DS . 'Config.php';
+
+    return loadFile($serviceConfig, true);
+}
+
+/**
+ * 写入php错误日志
+ *
+ * @param string $errno 错误编号
+ * @param string $errmsg 错误信息
+ * @param string $filename 错误文件
+ * @param string $linenum 错误行数
+ * @param mixed $vars 错误参数
+ *
+ */
+function php_error_log($errno, $errmsg, $filename, $linenum, $vars)
+{
+    if (!defined('E_STRICT')) define('E_STRICT', 2048);
+
+    $errortype = array(
+        E_ERROR           => 'Error',
+        E_WARNING         => 'Warning',
+        E_PARSE           => 'Parsing Error',
+        E_NOTICE          => 'Notice',
+        E_CORE_ERROR      => 'Core Error',
+        E_CORE_WARNING    => 'Core Warning',
+        E_COMPILE_ERROR   => 'Compile Error',
+        E_COMPILE_WARNING => 'Compile Warning',
+        E_USER_ERROR      => 'User Error',
+        E_USER_WARNING    => 'User Warning',
+        E_USER_NOTICE     => 'User Notice',
+        E_STRICT          => 'Runtime Notice'
+    );
+    $user_errors = array(E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE);
+    $filename = str_replace("\\", '/', $filename);
+    $dt = date('Y-m-d H:i:s');
+
+    $err = "#####" . $dt . "\n";
+    $err .= "```" . "\n";
+    $err .= "datetime     :" . $dt . "\n";
+    $err .= "errornum     :" . $errno . "\n";
+    $err .= "errormsg     :" . $errortype[$errno] . "\n";
+    $err .= "errortype    :" . $errmsg . "\n";
+    $err .= "scriptname   :" . $filename . "\n";
+    if (in_array($errno, $user_errors)) {
+        $err .= "scriptname   :" . wddx_serialize_value($vars, "Variables") . "\n";
+    }
+    $err .= "scriptlinenum:" . $linenum . "\n";
+    $err .= "```\n";
+
+    $logDir = PROJECT_PATH . DS . 'Storage' . DS . 'Logs' . DS . 'SysError' . DS;
+    $logFile = $logDir . date('m-d') . '.md';
+    if (!is_dir($logDir)) {
+        @mkdir($logDir);
+    }
+
+    @error_log($err, 3, $logFile);
+    @chmod($logFile, 0777);
+}
+
+
+function shutdown_function()
+{
+    $e = error_get_last();
+    if (isset($e['type'])) {
+        if (in_array($e['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR))) {
+            php_error_log($e['type'], $e['message'], $e['file'], $e['line'], NULL);
+        }
+    }
+}
+
+
+if (defined('LOG_ERROR') && LOG_ERROR) {
+    register_shutdown_function('shutdown_function');
+}
+
+function isMobile($mobile)
+{
+    return preg_match('#^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^16[\d]{9}$|^17[0,6,7,8,3,5]{1}\d{8}$|^18[\d]{9}$|^19[\d]{9}$#', $mobile)
+        ? true
+        : false;
+}
+
+/**电子邮箱格式校验*/
+function isEmail($email)
+{
+    $pattern = "/^([0-9A-Za-z\\-_\\.]+)@([0-9a-z]+\\.[a-z]{2,3}(\\.[a-z]{2})?)$/i";
+
+    return preg_match_all($pattern, $email) ? true : false;
+}
+
+
+/**
+ * 利用curl模拟浏览器发送请求
+ *
+ * @param string $url 请求的URL
+ * @param array|string $post post数据
+ * @param int $timeout 执行超时时间
+ * @param boolean $sendcookie 是否发送当前cookie
+ * @param array $options 可选的CURL参数
+ * @return array
+ */
+function request($url, $post = null, $timeout = 40, $sendcookie = true, $options = array(), $ssl = false, $sslDate = array(), $useragent = false, $proxy = false, $http_build_query = true)
+{
+    if (empty($useragent)) {
+        if (isset($_SERVER['HTTP_USER_AGENT'])) {
+            $useragent = $_SERVER['HTTP_USER_AGENT'];
+        } else {
+            $useragent = 'internalloginuseragent';
+        }
+    }
+
+    if ($proxy) {
+        $ip_long = array(
+            array('607649792', '608174079'), //36.56.0.0-36.63.255.255
+            array('1038614528', '1039007743'), //61.232.0.0-61.237.255.255
+            array('1783627776', '1784676351'), //106.80.0.0-106.95.255.255
+            array('2035023872', '2035154943'), //121.76.0.0-121.77.255.255
+            array('2078801920', '2079064063'), //123.232.0.0-123.235.255.255
+            array('-1950089216', '-1948778497'), //139.196.0.0-139.215.255.255
+            array('-1425539072', '-1425014785'), //171.8.0.0-171.15.255.255
+            array('-1236271104', '-1235419137'), //182.80.0.0-182.92.255.255
+            array('-770113536', '-768606209'), //210.25.0.0-210.47.255.255
+            array('-569376768', '-564133889'), //222.16.0.0-222.95.255.255
+        );
+
+        $rand_key = mt_rand(0, 9);
+        $ip = long2ip(mt_rand($ip_long[$rand_key][0], $ip_long[$rand_key][1]));//随机生成国内某个ip
+
+        $result = getNewProxyId();
+        if ($result['error'] != '') {
+            echo $result['error'];
+        } else {
+            $rand_ip = $result['ip'];
+        }
+
+
+        $baseHeader = array(
+            "CLIENT-IP:{$ip}",
+            "X-FORWARDED-FOR:{$ip}",
+        );
+
+        if (isset($options[CURLOPT_HTTPHEADER])) {
+            $options[CURLOPT_HTTPHEADER] = array_merge($options[CURLOPT_HTTPHEADER], $baseHeader);
+        }
+    }
+
+    $ch = curl_init($url);
+    curl_setopt($ch, CURLOPT_HEADER, 0);
+    curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
+    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 35);
+    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : 40);
+
+    if (defined('CURLOPT_IPRESOLVE') && defined('CURL_IPRESOLVE_V4')) {
+        curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+    }
+
+    if ($sendcookie) {
+        $cookie = '';
+        foreach ($_COOKIE as $key => $val) {
+            $cookie .= rawurlencode($key) . '=' . rawurlencode($val) . ';';
+        }
+        curl_setopt($ch, CURLOPT_COOKIE, $cookie);
+    }
+    if ($post) {
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, (is_array($post) && $http_build_query) ? http_build_query($post) : $post);
+    }
+
+    if (!ini_get('safe_mode') && ini_get('open_basedir') == '') {
+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
+    }
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+
+    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);  //检查证书中是否设置域名且是否与提供的主机名匹配
+    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);  //是否只信任CA颁发的证书
+    curl_setopt($ch, CURLOPT_SSLVERSION, 1);  //CURL_SSLVERSION_TLSv1
+    if ($ssl) {
+        /*curl_setopt($ch, CURLOPT_SSLCERT, '/apps/jindouyun/Public/upload/apiclient_cert.pem');
+        curl_setopt($ch, CURLOPT_SSLKEY, '/apps/jindouyun/Public/upload/apiclient_key.pem');*/
+        curl_setopt($ch, CURLOPT_SSLCERT, $sslDate['cert']);
+        curl_setopt($ch, CURLOPT_SSLKEY, $sslDate['key']);
+    }
+
+    foreach ($options as $key => $value) {
+        curl_setopt($ch, $key, $value);
+    }
+
+    if ($proxy && isset($rand_ip)) {
+        curl_setopt($ch, CURLOPT_PROXY, 'http://' . $rand_ip);
+    }
+
+
+    $ret = curl_exec($ch);
+    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+    $content_length = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
+    if (!$content_length) $content_length = curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD);
+    $info = curl_getinfo($ch);
+    $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
+    $errorMsg = curl_error($ch);
+    curl_close($ch);
+    return array(
+        'httpcode'       => $httpcode,
+        'info'           => $info,
+        'errorMsg'       => $errorMsg,
+        'content_length' => $content_length,
+        'content_type'   => $content_type,
+        'content'        => $ret
+    );
+}
+
+/**
+ * 获取最新的代理ip
+ * 代理测试工具 http://proxies.site-digger.com/proxy-test/
+ **/
+function getNewProxyId()
+{
+    //$url = 'http://webapi.http.zhimacangku.com/getip?num=1&type=2&pro=&city=0&yys=0&port=1&time=1&ts=1&ys=1&cs=1&lb=1&sb=0&pb=4&mr=1&regions=';
+    $url = 'http://http.tiqu.qingjuhe.cn/getip?num=1&type=2&pack=35299&port=1&lb=1&pb=4&regions=';
+    $data = request($url);
+    if ($data['httpcode'] == 200) {
+        $content = json_decode($data['content'], true);
+        if ($content['success'] == 'true') {
+            foreach ($content['data'] as $key => $value) {
+                $rand_ip = $value['ip'] . ':' . $value['port'];
+            }
+        } else {
+            $errorMessage = '获取代理ip失败' . $content['code'] . $content['msg'] . PHP_EOL;
+        }
+    }
+
+    if (isset($rand_ip)) {
+        return [
+            'ip'    => $rand_ip,
+            'error' => '',
+        ];
+    } else {
+        return [
+            'ip'    => '',
+            'error' => $errorMessage,
+        ];
+    }
+}
+
+/**
+ * 获取当前毫秒时间戳
+ */
+function msectime()
+{
+    list($msec, $sec) = explode(' ', microtime());
+    return (int)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
+}
+
+
+/**
+ * 断点调试输出方法
+ *
+ * @param mixed $msg 输出的内容
+ *
+ * @return string
+ */
+function V($msg)
+{
+    echo "<pre>";
+    var_export($msg);
+    exit();
+}
+
+/**
+ * 校验日期格式是否正确
+ *
+ * @param string $date 日期
+ * @param array $formats 需要检验的格式数组
+ *
+ * @return bool|int
+ */
+function checkDateIsValid($date, $formats = ["Y-m-d", "Y/m/d", "Y-m-d H:i:s", "Y/m/d H:i:s"])
+{
+    $unixTime = strtotime($date);
+    if (!$unixTime) {
+        return false;
+    }
+
+    foreach ($formats as $format) {
+        if (date($format, $unixTime) == $date) {
+            return $unixTime;
+        }
+    }
+
+    return false;
+}
+
+/**
+ * 二维数组根据某个字段排序
+ *
+ * @param array $needArray 需要排序的二维数组
+ * @param string $field 根据的字段排序
+ * @param array $sort 排序规则ASC 升序 | DESC 降序
+ *
+ * @return array;
+ */
+function arrayMultiSort(array $needArray, $field, $sort = 'ASC')
+{
+    $rule = [];
+    $arrSort = [];
+
+    $sort = strtoupper($sort);
+    $rule['direction'] = 'SORT_' . $sort;
+    $rule['field'] = $field;
+
+    foreach ($needArray AS $uniqid => $row) {
+        foreach ($row AS $key => $value) {
+            $arrSort[$key][$uniqid] = $value;
+        }
+    }
+    if ($rule['direction']) {
+        array_multisort($arrSort[$rule['field']], constant($rule['direction']), $needArray);
+    }
+
+    return $needArray;
+}
+
+/**
+ * 利用firephp输出调试到header
+ *
+ * 需要安装:
+ * firefox:FireBug | chrome:FirePHP for Chrome
+ *
+ * @param mixed $message 要输出的信息
+ * @param bool $showtime 是否显示执行时间
+ */
+function console($message, $showtime = false)
+{
+    static $lasttime = TIME;
+    static $_getInstance;
+
+    $thistime = microtime(true);
+    $usedtime = $thistime - $lasttime;
+    $lasttime = $thistime;
+
+    $label = $showtime ? sprintf("%09.5fs", $usedtime) : NULL;
+
+    if (is_null($_getInstance)) {
+        $_getInstance = \Mall\Framework\Core\FirePHP::getInstance(true);
+    }
+
+    $_getInstance->info($message, $label);
+}
+
+/**
+ * 检测数组的值不允许为空
+ *
+ * @param array $params 需要检测的数组
+ * @param array $checkKey 需要检测的key默认全部检测
+ * @param array $excludeKey 过虑不检测的Key
+ *
+ * @return string
+ */
+function checkNotEmpty($params, $excludeKey = [], $checkKey = [])
+{
+    $newParams = [];
+    if ($checkKey && is_array($checkKey)) {
+        foreach ($checkKey as $key) {
+            if (isset($params[$key])) {
+                $newParams[$key] = $params[$key];
+            }
+        }
+    }
+
+    if ($newParams) {
+        $params = $newParams;
+    }
+
+    if (is_array($params)) {
+        foreach ($params as $k => $v) {
+            if (in_array($k, $excludeKey, true)) {
+                continue;
+            }
+
+            if (is_array($v)) {
+                checkNotEmpty($v);
+            }
+
+            if (empty($v)) {
+                return $k . ' 不允许为空';
+            }
+        }
+    }
+}
+
+/**
+ * 友好格式化时间
+ * @param int $timestamp 时间
+ * @param array $formats
+ * @return string
+ */
+function formatDateTime($timestamp, $formats = null)
+{
+    if ($formats == null) {
+        $formats = array(
+            'DAY'           => '%s天前',
+            'DAY_HOUR'      => '%s天%s小时前',
+            'HOUR'          => '%s小时',
+            'HOUR_MINUTE'   => '%s小时%s分前',
+            'MINUTE'        => '%s分钟前',
+            'MINUTE_SECOND' => '%s分钟%s秒前',
+            'SECOND'        => '%s秒前',
+        );
+    }
+
+    /* 计算出时间差 */
+    $seconds = time() - $timestamp;
+    $minutes = floor($seconds / 60);
+    $hours = floor($minutes / 60);
+    $days = floor($hours / 24);
+    if ($days > 0 && $days < 31) {
+        $diffFormat = 'DAY';
+    } elseif ($days == 0) {
+        $diffFormat = ($hours > 0) ? 'HOUR' : 'MINUTE';
+        if ($diffFormat == 'HOUR') {
+            $diffFormat .= ($minutes > 0 && ($minutes - $hours * 60) > 0) ? '_MINUTE' : '';
+        } else {
+            $diffFormat = (($seconds - $minutes * 60) > 0 && $minutes > 0)
+                ? $diffFormat . '_SECOND' : 'SECOND';
+        }
+    } else {
+        $diffFormat = 'TURE_DATE_TIME';//超出30天, 正常时间显示
+    }
+
+    $dateDiff = null;
+    switch ($diffFormat) {
+        case 'DAY':
+            $dateDiff = sprintf($formats[$diffFormat], $days);
+            break;
+        case 'DAY_HOUR':
+            $dateDiff = sprintf($formats[$diffFormat], $days, $hours - $days * 60);
+            break;
+        case 'HOUR':
+            $dateDiff = sprintf($formats[$diffFormat], $hours);
+            break;
+        case 'HOUR_MINUTE':
+            $dateDiff = sprintf($formats[$diffFormat], $hours, $minutes - $hours * 60);
+            break;
+        case 'MINUTE':
+            $dateDiff = sprintf($formats[$diffFormat], $minutes);
+            break;
+        case 'MINUTE_SECOND':
+            $dateDiff = sprintf($formats[$diffFormat], $minutes, $seconds - $minutes * 60);
+            break;
+        case 'SECOND':
+            $dateDiff = sprintf($formats[$diffFormat], $seconds);
+            break;
+        default:
+            $dateDiff = date('Y-m-d H:i:s',$timestamp);
+    }
+    return $dateDiff;
+}
+
+/**
+ * 数组交叉排序并合并
+ *
+ * @param array $needo
+ * @param array $needt
+ *
+ * @return array
+ */
+function sortCross(array $needo = [], array $needt = [])
+{
+    $newData = [];
+    $needoCount = count($needo);
+    $needtCount = count($needt);
+    $size = $needoCount > $needtCount ? $needoCount : $needtCount;
+
+    for ($i = 0; $i < $size; $i++) {
+        if (isset($needo[$i])) {
+            array_push($newData, $needo[$i]);
+        }
+        if (isset($needt[$i])) {
+            array_push($newData, $needt[$i]);
+        }
+    }
+
+    return $newData;
+}
+
+/**
+ * 二维数组去重
+ *
+ * @param $list
+ * @param $key
+ *
+ * @return array
+ */
+function assocUnique($list, $key)
+{
+    $arr = array();
+    for ($i = 0; $i < count($list); $i++) {
+        if (isset($list[$i][$key])) {
+            if (!isset($arr[$list[$i][$key]])) {
+                $arr[$list[$i][$key]] = $list[$i];
+            }
+        }
+    }
+    return array_values($arr);
+}
+
+/**
+ * 为相对路径的缩略图加绝对地址
+ *
+ * @param string|array $thumbs 图片集合
+ * @param string $key 图片地址的Key
+ *
+ * @return array
+ */
+function absoluteImg($thumbs, $key = '')
+{
+    if ($thumbs) {
+        if (is_array($thumbs)) {
+            foreach ($thumbs as &$t) {
+                if ($key) {
+                    $value = $t[$key];
+                } else {
+                    $value = $t;
+                }
+                if (!$value) {
+                    continue;
+                }
+                if (!filter_var($value, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED)) {
+                    if ($key) {
+                        $t[$key] = URL_UPLOAD . $value;
+                    } else {
+                        $t = URL_UPLOAD . $value;
+                    }
+                }
+            }
+        }
+
+        if (is_string($thumbs)) {
+            if (!filter_var($thumbs, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED)) {
+                $thumbs = URL_UPLOAD . $thumbs;
+            }
+        }
+    }
+
+    return $thumbs;
+}
+
+/**
+ * 对内容字段进行 int 整形转换
+ *
+ * @param array $params
+ *
+ * @return array
+ */
+function contentIntValFormat(array $params)
+{
+    $intVal = [
+        'id',
+        'appid',
+        'has_thumb',
+        'catid',
+        'created',
+        'createdby',
+        'published',
+        'publishedby',
+        'modified',
+        'modifiedby',
+        'digg',
+        'pv',
+        'virtual_pv',
+        'status',
+        'indexid',
+        'contentid',
+        'pagecount',
+        'weight',
+        'commend',
+        'noexpiration',
+        'commend_pv',
+        'iscount'
+    ];
+    foreach ($params as $k => $param) {
+        if (in_array($k, $intVal, true)) {
+            $params[$k] = intval($param);
+        }
+    }
+
+    return $params;
+}
+
+/**
+ * 根据用户年月日算出星座
+ *
+ * @param string $dob 出生年月 1990-12-18
+ *
+ * @return string
+ */
+function constellation($dob)
+{
+    $dob = date("m-d", strtotime($dob));
+    list($month, $day) = explode("-", $dob);
+
+    $constellation = '';
+    if (($month == 3 || $month == 4) && ($day > 22 || $day < 21)) {
+        $constellation = "Aries";
+    } else if (($month == 4 || $month == 5) && ($day > 22 || $day < 22)) {
+        $constellation = "Taurus";
+    } else if (($month == 5 || $month == 6) && ($day > 23 || $day < 22)) {
+        $constellation = "Gemini";
+    } else if (($month == 6 || $month == 7) && ($day > 23 || $day < 23)) {
+        $constellation = "Cancer";
+    } else if (($month == 7 || $month == 8) && ($day > 24 || $day < 22)) {
+        $constellation = "Leo";
+    } else if (($month == 8 || $month == 9) && ($day > 23 || $day < 24)) {
+        $constellation = "Virgo";
+    } elseif (($month == 9 || $month == 10) && ($day > 25 || $day < 24)) {
+        $constellation = "Libra";
+    } else if (($month == 10 || $month == 11) && ($day > 25 || $day < 23)) {
+        $constellation = "Scorpio";
+    } else if (($month == 11 || $month == 12) && ($day > 24 || $day < 23)) {
+        $constellation = "Sagittarius";
+    } else if (($month == 12 || $month == 1) && ($day > 24 || $day < 21)) {
+        $constellation = "Cpricorn";
+    } else if (($month == 1 || $month == 2) && ($day > 22 || $day < 20)) {
+        $constellation = "Aquarius";
+    } else if (($month == 2 || $month == 3) && ($day > 21 || $day < 21)) {
+        $constellation = "Pisces";
+    }
+
+    return $constellation;
+}
+
+/**
+ * 随机生成短信验证码
+ * @param int $length 验证码长度
+ * @return int
+ */
+function generate_code($length = 6)
+{
+    return rand(pow(10, ($length - 1)), pow(10, $length) - 1);
+}
+
+/**
+ * 计算两点地理坐标之间的距离
+ * @param  string $longitude1 起点经度
+ * @param  string $latitude1 起点纬度
+ * @param  string $longitude2 终点经度
+ * @param  string $latitude2 终点纬度
+ * @param  int $unit 单位 1:米 2:公里
+ * @param  int $decimal 精度 保留小数位数
+ * @return string
+ */
+function getDistance($longitude1, $latitude1, $longitude2, $latitude2, $unit = 2, $decimal = 2)
+{
+
+    $EARTH_RADIUS = 6370.996; // 地球半径系数
+    $PI = 3.1415926;
+
+    $radLat1 = $latitude1 * $PI / 180.0;
+    $radLat2 = $latitude2 * $PI / 180.0;
+
+    $radLng1 = $longitude1 * $PI / 180.0;
+    $radLng2 = $longitude2 * $PI / 180.0;
+
+    $a = $radLat1 - $radLat2;
+    $b = $radLng1 - $radLng2;
+
+    $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
+    $distance = $distance * $EARTH_RADIUS * 1000;
+
+    if ($unit == 2) {
+        $distance = $distance / 1000;
+    }
+
+    return round($distance, $decimal);
+
+}
+
+/**
+ * @param int $month 月份
+ * @param int $year 年
+ *
+ * @return mixed
+ */
+function getMonthBeginToEnd($month, $year = 0)
+{
+    $beginDate = 0;
+    $endDate = 0;
+
+    if ($month = intval($month)) {
+        if (!$year) {
+            $year = date('Y', time());
+        }
+
+        $beginDate = strtotime("{$year}-{$month}-01");
+        $endDate = strtotime(date('Y-m-t', $beginDate));
+    }
+
+    return [
+        'beginTime' => $beginDate,
+        'endTime'   => $endDate
+    ];
+}
+
+/**
+ * 格式化商品价格
+ *
+ * @access  public
+ * @param   float $price 商品价格
+ * @return  string
+ */
+function priceFormat($price)
+{
+    return number_format($price, 2, '.', '');
+}
+
+/**
+ * 数组转xml内容
+ *
+ * @param   array $arr 数组
+ * @return  string
+ */
+function arrayToXml($arr)
+{
+    $xml = '';
+    foreach ($arr as $key => $value) {
+        if (is_string($value)) {
+            $xml .= '<' . $key . '><![CDATA[' . $value . ']]></' . $key . '>';
+        } else {
+            $xml .= '<' . $key . '>' . $value . '</' . $key . '>';
+        }
+    }
+    return $xml;
+}
+
+//数组转xml
+function arrayToWeiXinXml($arr)
+{
+    $xml = '<xml>';
+    foreach($arr as $key => $value){
+        if (is_string($value)) {
+            $xml .= '<' . $key . '><![CDATA[' . $value . ']]></' . $key . '>';
+        } else {
+            $xml .= '<' . $key . '>' . $value . '</' . $key . '>';
+        }
+    }
+    $xml .='</xml>';
+    return $xml;
+}
+
+/**
+ * xml转json格式数据
+ *
+ * @param string $xml xml格式文件内容
+ * @return string
+ */
+function XML2JSON($xml)
+{
+
+    function normalizeSimpleXML($obj, &$result)
+    {
+        $data = $obj;
+        if (is_object($data)) {
+            $data = get_object_vars($data);
+        }
+        if (is_array($data)) {
+            foreach ($data as $key => $value) {
+                $res = null;
+                normalizeSimpleXML($value, $res);
+                if (($key == '@attributes') && ($key)) {
+                    $result = $res;
+                } else {
+                    $result[$key] = $res;
+                }
+            }
+        } else {
+            $result = $data;
+        }
+    }
+
+    normalizeSimpleXML(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), $result);
+    return json_encode($result, JSON_UNESCAPED_UNICODE);
+}
+
+/**
+ * 分页方法
+ */
+function pageToOffset($page = 1, $pageSize = 10)
+{
+    return [
+        'limit'  => $pageSize,
+        'offset' => ($page - 1) * $pageSize,
+    ];
+}
+
+/**
+ * PHP截取UTF-8字符串,解决半字符问题。
+ * 英文、数字(半角)为1字节(8位),中文(全角)为2字节
+ * 取出的字符串, 当$len小于等于0时, 会返回整个字符串
+ * @param string $str 源字符串
+ * @param int $len 左边的子串的长度
+ */
+function utf_substr($str, $len)
+{
+    $new_str = [];
+    for ($i = 0; $i < $len; $i++) {
+        $temp_str = substr($str, 0, 1);
+        if (ord($temp_str) > 127) {
+            if ($i < $len) {
+                $new_str[] = substr($str, 0, 3);
+                $str = substr($str, 3);
+            }
+        } else {
+            $new_str[] = substr($str, 0, 1);
+            $str = substr($str, 1);
+        }
+    }
+    return join($new_str);
+}
+
+
+function isCardNo($vStr)
+{
+    $vCity = array(
+        '11', '12', '13', '14', '15', '21', '22',
+        '23', '31', '32', '33', '34', '35', '36',
+        '37', '41', '42', '43', '44', '45', '46',
+        '50', '51', '52', '53', '54', '61', '62',
+        '63', '64', '65', '71', '81', '82', '91'
+    );
+    if (!preg_match('/^([\d]{17}[xX\d]|[\d]{15})$/', $vStr)) return false;
+    if (!in_array(substr($vStr, 0, 2), $vCity)) return false;
+    $vStr = preg_replace('/[xX]$/i', 'a', $vStr);
+    $vLength = strlen($vStr);
+    if ($vLength == 18) {
+        $vBirthday = substr($vStr, 6, 4) . '-' . substr($vStr, 10, 2) . '-' . substr($vStr, 12, 2);
+    } else {
+        $vBirthday = '19' . substr($vStr, 6, 2) . '-' . substr($vStr, 8, 2) . '-' . substr($vStr, 10, 2);
+    }
+    if (date('Y-m-d', strtotime($vBirthday)) != $vBirthday) return false;
+    if ($vLength == 18) {
+        $vSum = 0;
+        for ($i = 17; $i >= 0; $i--) {
+            $vSubStr = substr($vStr, 17 - $i, 1);
+            $vSum += (pow(2, $i) % 11) * (($vSubStr == 'a') ? 10 : intval($vSubStr, 11));
+        }
+        if ($vSum % 11 != 1) return false;
+    }
+    return true;
+}
+
+/**
+ * try_catch 统一写入日志方法
+ * @param object $logger Factory::logs
+ * @param $exception  捕获的异常内容
+ */
+function catchError($logger, $exception)
+{
+    $error = '错误类型:' . get_class($exception) . PHP_EOL;
+    $error .= '错误代码:' . $exception->getCode() . PHP_EOL;
+    $error .= '错误信息:' . $exception->getMessage() . PHP_EOL;
+    $error .= '错误堆栈:' . $exception->getTraceAsString() . PHP_EOL;
+
+    $logger && $logger->log($error, 'error');
+}
+
+/**
+ * 资金元单位转化为分单位
+ * @param float 元
+ * @return int 分
+ */
+function yuanToFen($yuan)
+{
+    $fen = strval($yuan * 100);
+    return intval($fen);
+}
+
+/**
+ * 资金分单位转化为元单位
+ * @param int 分
+ * @return float 元
+ */
+function fenToYuan($fen)
+{
+    $fen = $fen / 100;
+    return $fen;
+}
+
+
+/**
+ * 千克转为克
+ * @param string|float
+ * @return int
+ */
+function kgTog($kg)
+{
+    $g = strval($kg * 1000);
+    return $g = intval($g);
+}
+
+
+/**
+ * 克转为千克
+ * @param string|float
+ * @return int
+ */
+
+function gToKg($kg)
+{
+    $kg = $kg / 1000;
+    return $kg;
+}
+
+
+/**
+ * 元/千克 转为 分/克 并保留8位有效数字
+ * @param string|float
+ * @return int
+ */
+function kgYuanTogFenDanWei($KgYuan)
+{
+    $gFen = $KgYuan / 10;
+    return $gFen;
+}
+
+function floatDiv($param)
+{
+    return $param / 100000000;
+}
+
+function floatMul($param)
+{
+    return $param * 100000000;
+}
+
+
+/**
+ * 分/克 转为 元/千克
+ * @param string|float
+ * @return int
+ */
+function gFenToKgYuanDanWei($gFen)
+{
+    $kgYuan = $gFen * 10;
+    return $kgYuan;
+}
+
+/**
+ * 获取当前的时间(毫秒)
+ * @return float
+ */
+function getMillisecond()
+{
+    list($t1, $t2) = explode(' ', microtime());
+    return (float)sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);
+}
+
+
+/**
+ * 多维数组转树状结构
+ *
+ * @param $tree
+ * @param int $rootId
+ * @return array
+ */
+function arr2tree($tree, $rootId = 0)
+{
+
+    $return = [];
+    foreach ($tree as $leaf) {
+        if ($leaf['pid'] == $rootId) {
+            foreach ($tree as $subleaf) {
+                if ($subleaf['pid'] == $leaf['id']) {
+                    $leaf['children'] = arr2tree($tree, $leaf['id']);
+                    break;
+                }
+            }
+            $return[] = $leaf;
+        }
+    }
+
+    return $return;
+}
+
+/**
+ * <生成订单号>
+ * 规则说明: 来源(pc/ios/android)1位 + 订单类型标示(海淘/门票)1位 + 时间戳转日期(20171107)8位 + 用户id(2位) + 毫秒时间提取(4位)
+ * @param $orderFrom
+ * @param $orderTypeId
+ * @param $userId
+ * @return int
+ */
+function createOrderSn($orderFrom, $orderTypeId, $userId)
+{
+    $no = $orderFrom . $orderTypeId . date('Ymd') . substr(strval($userId + 100), -2) . substr(microtime(), 2, 4) . sprintf('%02d', rand(1000, 9999));
+
+    return $no;
+}
+
+/**
+ * 按照日期生成流水号
+ * @param string $maxNo 当天最后一条数据编号
+ */
+function createSerialNumberByDate($maxNo = '',$dateLength = 8, $lenght = 4)
+{
+    $number = 0;
+    if( !empty($maxNo) ){
+        $todayLastNo = explode('-',$maxNo);
+        $number = intval($todayLastNo[1]);
+    }
+    $number = $number+1;
+    switch ($dateLength){
+        case 12: // 根据年月日时分生成流水号
+            $date = date('YmdHi',time());
+            break;
+        case 14: // 根据年月日时分秒生成流水号
+            $date = date('YmdHis',time());
+            break;
+        default: // 根据年月日生成流水号
+            $date = date('Ymd',time());
+    }
+
+    return $date.'-'.str_pad($number,$lenght,'0',STR_PAD_LEFT);
+}
+
+/**
+ * 生成编码
+ * @param $prefix 编码前缀 例如:采购入库单 CGRK
+ * @param $id 自动id
+ * @param int $length 编码长度
+ * @return string
+ */
+function createCode($prefix, $id, $length = 6)
+{
+    $idLength = strlen((string)$id);
+    $leaveLength = $length - $idLength;
+    $zeroStr = "";
+    if ($leaveLength > 0) {
+        $zeroStr = str_repeat('0', $leaveLength);
+    }
+    return strtoupper($prefix) . $zeroStr . $id;
+}
+
+/**
+ * 随机生成16位字符串
+ * @return string 生成的字符串
+ */
+function getRandomStr($lenght = 16)
+{
+
+    $str = "";
+    $str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
+    $max = strlen($str_pol) - 1;
+    for ($i = 0; $i < $lenght; $i++) {
+        $str .= $str_pol[mt_rand(0, $max)];
+    }
+    return $str;
+}
+
+
+/*
+ * uuid 唯一序列码
+ */
+
+function uuid($prefix = '')
+{
+    $chars = md5(uniqid(mt_rand(), true));
+    $uuid  = substr($chars,0,8) . '-';
+    $uuid .= substr($chars,8,4) . '-';
+    $uuid .= substr($chars,12,4) . '-';
+    $uuid .= substr($chars,16,4) . '-';
+    $uuid .= substr($chars,20,12);
+    return $prefix . $uuid;
+}
+
+/**
+ * 获取 当天,昨天,本周,本月,本年时间戳
+ */
+
+function gettimestamp($name = 'today',$param = ''){
+    switch ($name){
+        case 'today'://今天
+            $timeamp['start'] = strtotime(date('Y-m-d'));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+        case 'yesterday'://昨天
+            $timeamp['start'] = strtotime(date('Y-m-d',strtotime('-1 day')));
+            $timeamp['end'] = strtotime(date('Y-m-d'));
+            break;
+        case 'beforyesterday'://前天
+            $timeamp['start'] = strtotime(date('Y-m-d',strtotime('-2 day')));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('-1 day')));
+            break;
+        case 'beforweek'://本周
+            $timeamp['start'] = strtotime(date("Y-m-d H:i:s",mktime(0, 0 , 0,date("m"),date("d")-date("w")+1,date("Y"))));
+            $timeamp['end'] = strtotime(date("Y-m-d H:i:s",mktime(23,59,59,date("m"),date("d")-date("w")+7,date("Y"))));
+            break;
+        case 'nowmonth'://本月
+            $timeamp['start'] = strtotime(date('Y-m-01'));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+        case 'permonth'://上月
+            $timeamp['start'] = strtotime(date('Y-m-01',strtotime('-1 month')));
+            $timeamp['end'] = strtotime(date('Y-m-01'));
+            break;
+        case 'preweek'://上周 注意我们是从周一开始算
+            $timeamp['start'] = strtotime(date('Y-m-d',strtotime('-2 week Monday')));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('-1 week Monday +1 day')));
+            break;
+        case 'nowweek'://本周
+            $timeamp['start'] = strtotime(date('Y-m-d',strtotime('-1 week Monday')));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+        case 'preday'://近三个月
+//            $timeamp['start'] = strtotime(date('Y-m-d'),strtotime($param.' day'));
+            $timeamp['start'] = strtotime("-0 year -3 month -0 day");
+            $timeamp['end'] = strtotime(date('Y-m-d'));
+            break;
+        case 'nextday'://30
+            $timeamp['start'] = strtotime(date('Y-m-d'));
+            $timeamp['end'] = strtotime(date('Y-m-d'),strtotime($param.' day'));
+            break;
+        case 'preyear'://去年
+            $timeamp['start'] = strtotime(date('Y-01-01',strtotime('-1 year')));
+            $timeamp['end'] = strtotime(date('Y-12-31',strtotime('-1 year')));
+            break;
+        case 'nowyear'://今年
+            $timeamp['start'] = strtotime(date('Y-01-01'));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+        case 'quarter'://季度
+            $quarter = empty($param) ? ceil((date('n'))/3) : $param;//获取当前季度
+            $timeamp['start'] = mktime(0, 0, 0,$quarter*3-2,1,date('Y'));
+            $timeamp['end'] = mktime(0, 0, 0,$quarter*3+1,1,date('Y'));
+//            $timeamp['end'] = mktime(23,59,59,$quarter*3,date('t',mktime(0, 0 , 0,$quarter*3,1,date("Y"))),date('Y'));
+            break;
+        default:
+            $timeamp['start'] = strtotime(date('Y-m-d'));
+            $timeamp['end'] = strtotime(date('Y-m-d',strtotime('+1 day')));
+            break;
+    }
+    return $timeamp;
+}
+

+ 63 - 0
Mall/Framework/Core/Aes.Class.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Aes
+{
+    private $_iv = "0102030405060708";
+
+    private $_encryptKey;
+
+    private static $_instance;
+
+    public function __construct ($encryptKey = '')
+    {
+        $this->_encryptKey = $encryptKey ?: defined('SITE_SECRET') ?: 'h7WPzPi87}MC7AxM6Jz)L?9%NXy*jB';
+    }
+
+    public static function getInstance($encryptKey = [])
+    {
+        if (!self::$_instance instanceof self) {
+            self::$_instance = new self($encryptKey);
+        }
+
+        return self::$_instance;
+    }
+
+    public function encrypt($encryptStr)
+    {
+        $localIV = $this->_iv;
+        $encryptKey = $this->_encryptKey;
+
+        $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, $localIV);
+
+        mcrypt_generic_init($module, $encryptKey, $localIV);
+
+        $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
+        $pad = $block - (strlen($encryptStr) % $block);
+        $encryptStr .= str_repeat(chr($pad), $pad);
+
+        $encrypted = mcrypt_generic($module, $encryptStr);
+
+        mcrypt_generic_deinit($module);
+        mcrypt_module_close($module);
+
+        return base64_encode($encrypted);
+
+    }
+
+    public function decrypt($encryptStr)
+    {
+        $localIV = $this->_iv;
+        $encryptKey = $this->_encryptKey;
+
+        $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, $localIV);
+
+        mcrypt_generic_init($module, $encryptKey, $localIV);
+
+        $encryptedData = base64_decode($encryptStr);
+        $encryptedData = mdecrypt_generic($module, $encryptedData);
+
+        return $encryptedData;
+    }
+}

+ 53 - 0
Mall/Framework/Core/AutoLoad.Class.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class AutoLoad
+{
+    static function _autoLoad($class)
+    {
+        //var_dump($class);
+        //xssCheck();
+
+        if(function_exists('apcu_fetch') && ini_get('apc.enabled')){
+            if ($file_path = apcu_fetch('autoload_'.PROJECT_DOMAIN.$class)) {
+                loadFile($file_path);
+                return true;
+            }
+        }
+
+        $class_path = str_replace('\\', '/', $class) . '.Class.php';
+
+        // util工具类特殊处理
+        if(strpos($class_path, 'Util') === 0){
+            $real_path = DS.$class_path;
+        }else{
+            $real_path = substr($class_path, strpos($class_path, '/'));
+        }
+
+
+        switch($real_path){
+            case strpos($class_path, FRAMEWORK_NAME.'/') !== FALSE:
+                $file_path = ROOT_PATH . $real_path;
+                break;
+            case strpos($class_path, 'Smarty') !== FALSE:
+                return false;
+                break;
+            case strpos($class_path, 'Service') !== FALSE:
+                if(!defined('SWOOLESERVER_NAME')){
+                    throw new \Exception('请去swoole服务项目启动脚本配置服务目录名称,重启生效!!!');
+                }
+                $file_path = ROOT_PATH . DS . '..' . DS . SWOOLESERVER_NAME . $real_path;
+                break;
+            default:
+                $file_path = PROJECT_PATH . $real_path;
+        }
+
+        if(function_exists('apcu_fetch') && ini_get('apc.enabled')){
+            apcu_add('autoload_'.PROJECT_DOMAIN.$class, $file_path);
+        }
+        loadFile($file_path);
+    }
+
+
+}

+ 202 - 0
Mall/Framework/Core/BaseImg.Class.php

@@ -0,0 +1,202 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class BaseImg
+{
+    // 上传文件名
+    protected $saveName;
+    
+    // 上传Base64文件
+    protected $file;
+
+    // 上传的文件正则匹配后的信息
+    protected $info;
+
+    // 错误信息
+    private $error = '';
+
+    protected static $_instance;
+
+    private function __construct(){}
+
+    public static function getInstance()
+    {
+        $key = md5('baseImg64');
+
+        if (!isset(self::$_instance[$key])) {
+            self::$_instance[$key] = new self();
+        }
+
+        return self::$_instance[$key];
+    }
+
+    /**
+     * @param string $file base64的图片
+     * @param  bool $replace 同名文件是否覆盖
+     * @param string $dirName 制定目录名称
+     *
+     * @return bool|File
+     */
+    public function move($file, $replace = true, $dirName = '')
+    {
+        $this->file = $file;
+        $path = UPLOAD_FILE_PATH;
+
+        // 检测合法性
+        if (!$this->baseIsValid()) {
+            $this->error = '非法上传文件';
+            return false;
+        }
+
+        // 验证上传
+        if (!$this->baseCheckImg($this->info[2])) {
+            $this->error = '非法图像文件';
+            return false;
+        }
+
+        $path = rtrim($path, DS) . DS;
+        // 文件保存命名规则
+        $saveName = $this->baseBuildSaveName($dirName);
+        $filename = $path . $saveName;
+
+        // 检测目录
+        if (false === $this->checkPath(dirname($filename))) {
+            return false;
+        }
+
+        /* 不覆盖同名文件 */
+        if (!$replace && is_file($filename)) {
+            $this->error = '存在同名文件' . $filename;
+            return false;
+        }
+
+        if(!file_put_contents($filename, base64_decode(str_replace($this->info[1], '', $file)))){
+            $this->error = '图像上传失败';
+            return false;
+        }
+
+        // 返回 File对象实例
+        $this->setSaveName($saveName);
+        return $this;
+    }
+
+    protected function baseIsValid()
+    {
+        preg_match('/^(data:\s*image\/(\w+);base64,)/', $this->file, $result);
+
+        $this->info = $result;
+
+        return $result ? true : false;
+    }
+
+    protected function baseCheckImg($extension)
+    {
+        if (!in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf'], true)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    protected function baseBuildSaveName($dirName = '')
+    {
+        if($dirName){
+            return $dirName .DS. date('Ymd') . DS . md5(microtime(true)) . '.' . $this->info[2];
+        }
+        return date('Ymd') . DS . md5(microtime(true)) . '.' . $this->info[2];
+    }
+
+    /**
+     * 检查目录是否可写
+     * @param  string $path 目录
+     * @return boolean
+     */
+    protected function checkPath($path)
+    {
+        if (is_dir($path)) {
+            return true;
+        }
+
+        if (mkdir($path, 0755, true)) {
+            return true;
+        } else {
+            $this->error = "目录 {$path} 创建失败!";
+            return false;
+        }
+    }
+
+    /**
+     * 获取上传文件的文件名
+     * @return string
+     */
+    public function getSaveName()
+    {
+        return $this->saveName;
+    }
+
+    /**
+     * 设置上传文件的保存文件名
+     * @param  string $saveName
+     * @return $this
+     */
+    public function setSaveName($saveName)
+    {
+        $this->saveName = $saveName;
+        return $this;
+    }
+
+    /**
+     * 获取错误信息
+     * @return mixed
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    public function baseImgMove($thumbs, $selfUploadUrl = '')
+    {
+        $actionImg = [];
+
+        if (!empty($thumbs)) {
+            if (is_array($thumbs) && !empty($thumbs)) {
+                foreach ($thumbs as $thumb) {
+                    if ($thumb) {
+                        if ($path = self::_baseImgValidate($thumb, $selfUploadUrl)) {
+                            $actionImg[] = $path;
+                        }
+                    }
+                }
+            } else {
+                if (is_string($thumbs)) {
+                    if ($path = self::_baseImgValidate($thumbs, $selfUploadUrl)) {
+                        $actionImg[] = $path;
+                    }
+                }
+            }
+        }
+
+        return json_encode($actionImg);
+    }
+
+    private function _baseImgValidate($thumb, $selfUploadUrl)
+    {
+        if(filter_var($thumb, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED)) {
+            preg_match('#^'.preg_quote($selfUploadUrl).'#', $thumb, $myThumb);
+            if ($myThumb) {
+                $actionImg = str_replace($selfUploadUrl, '', $thumb);
+            } else {
+                $actionImg = $thumb;
+            }
+        } else {
+            if ($file = self::move($thumb)) {
+                $actionImg = $file->getSaveName();
+            } else {
+                $actionImg = $thumb;
+            }
+        }
+
+        return $actionImg ? $actionImg : false;
+    }
+}

+ 36 - 0
Mall/Framework/Core/Cache.Class.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Cache {
+
+    protected static $_instance;
+
+    private function __construct(){}
+
+    public function connect($storage, $options=array())
+    {
+        $cache = FALSE;
+        $class = 'Mall\\Framework\\Cache\\'.ucwords(strtolower($storage));
+
+        if(class_exists($class)) {
+            $cache = new $class($options);
+        }
+
+        return $cache;
+    }
+
+    public static function getInstance($options = [])
+    {
+        $options = $options ?: Config::getInstance()->get('cache');
+        $storage = $options['storage'] ?: 'Redis';
+        $key = $storage . md5(implode(',', $options));
+
+        if (!isset(self::$_instance[$key])) {
+            $obj = new self();
+            self::$_instance[$key] = $obj->connect($storage, $options);
+        }
+
+        return self::$_instance[$key];
+    }
+}

+ 139 - 0
Mall/Framework/Core/Config.Class.php

@@ -0,0 +1,139 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Config {
+
+    static private $instance;
+
+    /**
+     * 所有app的配置集
+     * array(
+     * 		md5($app_conf_path) => array(
+     *
+     * 		),
+     * 		... ,
+     * );
+     * @var array
+     */
+    static protected $appConfigs = array();
+
+    /**
+     *
+     * 当前app的配置
+     * @var array
+     */
+    protected $appConfig;
+
+    /**
+     *
+     * 不允许直接 new
+     */
+    private function __construct() {}
+
+    /**
+     *
+     * 载入配置,如果成功,返回instance
+     * @param string $app_conf_path 配置文件的路径
+     */
+    static public function load($app_conf_path) {
+        $app_conf_path = realpath($app_conf_path);
+        $app_conf_key = md5($app_conf_path);
+        if (!isset(self::$appConfigs[$app_conf_key])) {
+
+
+            $config = include($app_conf_path);
+
+            if (!is_array($config)) {
+                throw new \Exception("load app_conf_path fail: {$app_conf_path}");
+            }
+            # 将conf的path存进来
+            $config['conf_path'] = $app_conf_path;
+
+            self::$appConfigs[$app_conf_key] = $config;
+        }
+
+        if (is_null(self::$instance)) {
+            self::$instance = new self();
+        }
+
+        self::$instance->appConfig = self::$appConfigs[$app_conf_key];
+
+        return self::$instance;
+    }
+
+    /**
+     *
+     * 单例模式
+     */
+    static public function getInstance() {
+        if (is_null(self::$instance)) {
+            self::$instance = new self();
+    }
+
+        if (is_null(self::$instance->appConfig)) {
+
+            throw new \Exception('instance appconfig is null, pleace run Mall\Core\Config::load !');
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     *
+     * 获取指定配置项的值
+     * @param string $key
+     * @return mixed
+     */
+    public function get($key) {
+        if (empty($key)) {
+            return false;
+        }
+
+        $keys = explode('.', $key);
+
+        $value = $this->appConfig;
+
+        foreach ($keys as $tmpKey) {
+            if (!isset($value[$tmpKey])) {
+                return false;
+            }
+            $value = $value[$tmpKey];
+        }
+        return $value;
+    }
+
+    /**
+     *
+     * 获取指定配置项下的得某一项
+     * @param string $configKey  配置文件下的某一项
+     * @param string $appointKey 指定项下的某一个节点
+     * @return mixed
+     */
+    public function getAppoint($configKey, $appointKey) {
+        if (!$configKey) {
+            return false;
+        }
+
+        $allConfig = $this->appConfig;
+
+        if(empty($allConfig[$configKey])){
+            return [];
+        }
+
+        if(isset($allConfig[$configKey][$appointKey])){
+            return $allConfig[$configKey][$appointKey];
+        }else{
+            return $allConfig[$configKey];
+        }
+    }
+
+    /**
+     *
+     * 获取所有app的配置集
+     * @return array
+     */
+     public function all() {
+        return $this->appConfig;
+    }
+}

+ 75 - 0
Mall/Framework/Core/Cookie.Class.php

@@ -0,0 +1,75 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Cookie
+{
+    private $prefix = '';
+
+    private $path = '/';
+
+    private $domain = '';
+
+    private static $_instance;
+
+    public function __clone()
+    {
+        trigger_error('Clone is not allow!', E_USER_ERROR);
+    }
+
+    public static function getInstance($options = [])
+    {
+        $options = $options ?: Config::getInstance()->get('cookie');
+
+        if (!self::$_instance instanceof self) {
+            self::$_instance = new self;
+            self::$_instance->setOptions($options);
+        }
+
+        return self::$_instance;
+    }
+
+    public function setOptions($options)
+    {
+        $this->prefix = $options['prefix'];
+        $this->path = $options['path'];
+        $this->domain = $options['domain'];
+    }
+
+    /**
+     * 设置一个cookie值
+     *
+     * @param string $var
+     * @param mixed $value
+     * @param int $time 存活时间
+     */
+    public function set($var, $value = null, $time = 0)
+    {
+        if (is_null($value)) {
+            $time = time() - 3600;
+        } elseif ($time > 0 && $time < 31536000) {
+            $time += time();
+        }
+
+        $s = $_SERVER['SERVER_PORT'] == '443' ? 1 : 0;
+        $var = $this->prefix . $var;
+        $_COOKIE[$var] = $value;
+        if (is_array($value)) {
+            foreach ($value as $k => $v) {
+                setcookie($var . '[' . $k . ']', $v, $time, $this->path, $this->domain, $s);
+            }
+        } else {
+            setcookie($var, $value, $time, $this->path, $this->domain, $s);
+        }
+    }
+
+    /**
+     * @param $var
+     * @return bool
+     */
+    public function get($var)
+    {
+        $var = $this->prefix . $var;
+        return isset($_COOKIE[$var]) ? $_COOKIE[$var] : false;
+    }
+}

+ 33 - 0
Mall/Framework/Core/Db.Class.php

@@ -0,0 +1,33 @@
+<?php
+namespace Mall\Framework\Core;
+
+class Db
+{
+
+    static protected $instance;
+
+    static public function getInstance($options = [])
+    {
+        $options = $options ?: Config::getInstance()->get('db');
+        $options['driver'] =  strtolower($options['driver']);
+
+        $key = $options['driver'] . md5(implode(',', $options));
+        if (!isset(self::$instance[$key])) {
+            switch ($options['driver'])
+            {
+                case 'mssql':
+                    $class = 'Mall\\Framework\\Db\\DbMssql';
+                    break;
+                case 'oci':
+                    $class = 'Mall\\Framework\\Db\\DbOci';
+                    break;
+                default:
+                    $class = 'Mall\\Framework\\Db\\Db';
+                    break;
+            }
+            self::$instance[$key] = new $class($options);
+        }
+
+        return self::$instance[$key];
+    }
+}

+ 96 - 0
Mall/Framework/Core/ErrorCode.Class.php

@@ -0,0 +1,96 @@
+<?php
+/**
+ * 统一的错误编码 方便Service 和 其它项目之间Swoole传输判断
+ */
+namespace Mall\Framework\Core;
+
+class ErrorCode
+{
+    /** @var int $notShowError 后端错误,前端不用展示*/
+    public static $notShowError = 9999;
+
+
+    /** @var int $paramError 参数错误 */
+    public static $paramError = 1001;
+
+    /** @var int $userNotFount 用户不存在 */
+    public static $userNotFount = 1002;
+
+    /** @var int $notAllowAccess 非法的请求 */
+    public static $notAllowAccess = 1003;
+
+    /** @var int $mobileCodeFail 验证码错误 */
+    public static $mobileCodeFail = 1004;
+
+    /** @var int $dberror 数据库操作错误 */
+    public static $dberror = 1005;
+
+    /** @var int $accountfail 登录帐号密码错误 */
+    public static $accountfail = 1006;
+
+    /** @var int $swooleRecvError swoole服务未返回消息 */
+    public static $swooleRecvError = 1007;
+
+    /** @var int $loginedexpire  登陆过期 */
+    public static $loginedexpire = 1008;
+
+    /** @var int $redisWriteError  redis写入失败 */
+    public static $redisWriteError = 1009;
+
+    /** @var int $notHaveAclAccess 没有权限访问 */
+    public static $notHaveAclAccess = 1010;
+
+    /** @var int $signfail 签名错误 */
+    public static $signfail = 1011;
+
+    /** @var int $mobileishaved 注册手机号已经存在 */
+    public static $mobileishaved = 1012;
+
+    /** @var int $uploadEroor 上传操作失败 */
+    public static $uploadEroor = 1013;
+
+    /** @var int $configEroor 配置文件错误 */
+    public static $configEroor = 1014;
+
+    /** @var int $apiNotResult 接口返回数据错误 */
+    public static $apiNotResult = 1015;
+
+    /** @var int $serviceError 服务错误 */
+    public static $serviceError = 1016;
+
+    /** @var int $notAllowToken 非法或无效的Token */
+    public static $notAllowToken = 1017;
+
+    /** @var int $contentNotExists 资讯内容不存在 */
+    public static $contentNotExists = 1018;
+
+    /** @var int $nickNameIsHaved 昵称已经注册过了 */
+    public static $nickNameIsHaved = 1019;
+
+    /** @var int $actionIsDo 重复得操作 */
+    public static $actionIsDo = 1020;
+
+    /** @var int $weixinPayError 微信支付接口返回错误 */
+    public static $weixinPayError = 1021;
+
+    /** @var int $invalidAccess 库存不足 */
+    public static $invalidAccess = 1021;
+    /** @var int $invalidAccess 销售单重复提交 */
+    public static $resubmit = 1022;
+
+    /** @var int $invalidAccess 商品被删除 */
+    public static $goodsDelete = 1023;
+
+    /** @var int $invalidAccess 初始密码登录 */
+    public static $defaultPassword = 1024;
+
+    public static $OK = 0;
+
+    public static $IllegalAesKey = 900004;
+    public static $ValidateSignatureError = 900005;
+    public static $ComputeSignatureError = 900006;
+    public static $EncryptAESError = 900007;
+    public static $DecryptAESError = 900008;
+    public static $ValidateSuiteKeyError = 900010;
+
+}

+ 414 - 0
Mall/Framework/Core/File.Class.php

@@ -0,0 +1,414 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use SplFileObject;
+
+class File extends SplFileObject
+{
+    /**
+     * 错误信息
+     * @var string
+     */
+    private $error = '';
+    // 当前完整文件名
+    protected $filename;
+    // 上传文件名
+    protected $saveName;
+    // 文件上传命名规则
+    protected $rule = 'date';
+    // 文件上传验证规则
+    protected $validate = [];
+    // 单元测试
+    protected $isTest;
+    // 上传文件信息
+    protected $info;
+    // 文件hash信息
+    protected $hash = [];
+
+    protected static $_instance;
+
+    public static function getInstance($filename = '', $mode = 'r')
+    {
+        $key = md5('file');
+
+        if (!isset(self::$_instance[$key])) {
+            self::$_instance[$key] = new self($filename, $mode);
+        }
+
+        return self::$_instance[$key];
+    }
+
+
+    public function __construct($filename = '', $mode = 'r')
+    {
+        parent::__construct($filename, $mode);
+        $this->filename = $this->getRealPath() ?: $this->getPathname();
+    }
+
+    /**
+     * 是否测试
+     * @param  bool $test 是否测试
+     * @return $this
+     */
+    public function isTest($test = false)
+    {
+        $this->isTest = $test;
+        return $this;
+    }
+
+    /**
+     * 设置上传信息
+     * @param  array $info 上传文件信息
+     * @return $this
+     */
+    public function setUploadInfo($info)
+    {
+        $this->info = $info;
+        return $this;
+    }
+
+    /**
+     * 获取上传文件的信息
+     * @param  string $name
+     * @return array|string
+     */
+    public function getInfo($name = '')
+    {
+        return isset($this->info[$name]) ? $this->info[$name] : $this->info;
+    }
+
+    /**
+     * 获取上传文件的文件名
+     * @return string
+     */
+    public function getSaveName()
+    {
+        return $this->saveName;
+    }
+
+    /**
+     * 设置上传文件的保存文件名
+     * @param  string $saveName
+     * @return $this
+     */
+    public function setSaveName($saveName)
+    {
+        $this->saveName = $saveName;
+        return $this;
+    }
+
+    /**
+     * 获取文件的哈希散列值
+     * @return $string
+     */
+    public function hash($type = 'sha1')
+    {
+        if (!isset($this->hash[$type])) {
+            $this->hash[$type] = hash_file($type, $this->filename);
+        }
+        return $this->hash[$type];
+    }
+
+    /**
+     * 检查目录是否可写
+     * @param  string $path 目录
+     * @return boolean
+     */
+    protected function checkPath($path)
+    {
+        if (is_dir($path)) {
+            return true;
+        }
+
+        if (mkdir($path, 0777, true)) {
+            return true;
+        } else {
+            $this->error = "目录 {$path} 创建失败!";
+            return false;
+        }
+    }
+
+    /**
+     * 获取文件类型信息
+     * @return string
+     */
+    public function getMime()
+    {
+        $finfo = finfo_open(FILEINFO_MIME_TYPE);
+        return finfo_file($finfo, $this->filename);
+    }
+
+    /**
+     * 设置文件的命名规则
+     * @param  string $rule 文件命名规则
+     * @return $this
+     */
+    public function rule($rule)
+    {
+        $this->rule = $rule;
+        return $this;
+    }
+
+    /**
+     * 设置上传文件的验证规则
+     * @param  array $rule 验证规则
+     * @return $this
+     */
+    public function validate($rule = [])
+    {
+        $this->validate = $rule;
+        return $this;
+    }
+
+    /**
+     * 检测是否合法的上传文件
+     * @return bool
+     */
+    public function isValid()
+    {
+        if ($this->isTest) {
+            return is_file($this->filename);
+        }
+        return is_uploaded_file($this->filename);
+    }
+
+    /**
+     * 检测上传文件
+     * @param  array $rule 验证规则
+     * @return bool
+     */
+    public function check($rule = [])
+    {
+        $rule = $rule ?: $this->validate;
+
+        /* 检查文件大小 */
+        if (isset($rule['size']) && !$this->checkSize($rule['size'])) {
+            $this->error = '上传文件大小不符!';
+            return false;
+        }
+        /* 检查文件Mime类型 */
+        if (isset($rule['type']) && !$this->checkMime($rule['type'])) {
+            $this->error = '上传文件MIME类型不允许!';
+            return false;
+        }
+        /* 检查文件后缀 */
+        if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) {
+            $this->error = '上传文件后缀不允许';
+            return false;
+        }
+
+        /* 检查图像文件 */
+        if (!$this->checkImg()) {
+            $this->error = '非法图像文件!';
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 检测上传文件后缀
+     * @param  array|string $ext 允许后缀
+     * @return bool
+     */
+    public function checkExt($ext)
+    {
+        if (is_string($ext)) {
+            $ext = explode(',', $ext);
+        }
+        $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
+        if (!in_array($extension, $ext)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 检测图像文件
+     * @return bool
+     */
+    public function checkImg()
+    {
+        $extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
+        /* 对图像文件进行严格检测 */
+        if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6])) {
+            return false;
+        }
+        return true;
+    }
+
+    // 判断图像类型
+    protected function getImageType($image)
+    {
+        if (function_exists('exif_imagetype')) {
+            return exif_imagetype($image);
+        } else {
+            $info = getimagesize($image);
+            return $info[2];
+        }
+    }
+
+    /**
+     * 检测上传文件大小
+     * @param  integer $size 最大大小
+     * @return bool
+     */
+    public function checkSize($size)
+    {
+        if ($this->getSize() > $size) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 检测上传文件类型
+     * @param  array|string $mime 允许类型
+     * @return bool
+     */
+    public function checkMime($mime)
+    {
+        if (is_string($mime)) {
+            $mime = explode(',', $mime);
+        }
+        if (!in_array(strtolower($this->getMime()), $mime)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 移动文件
+     //* @param  string $path 保存路径
+     * @param  string|bool $savename 保存的文件名 默认自动生成
+     * @param  boolean $replace 同名文件是否覆盖
+     * @return false|File false-失败 否则返回File实例
+     */
+    public function move($savename = true, $replace = true)
+    {
+        $path = UPLOAD_FILE_PATH;
+        // 文件上传失败,捕获错误代码
+        if (!empty($this->info['error'])) {
+            $this->error($this->info['error']);
+            return false;
+        }
+
+        // 检测合法性
+        if (!$this->isValid()) {
+            $this->error = '非法上传文件';
+            return false;
+        }
+
+        // 验证上传
+        if (!$this->check()) {
+            return false;
+        }
+        $path = rtrim($path, DS) . DS;
+        // 文件保存命名规则
+        $saveName = $this->buildSaveName($savename);
+        $filename = $path . $saveName;
+
+        // 检测目录
+        if (false === $this->checkPath(dirname($filename))) {
+            return false;
+        }
+
+        /* 不覆盖同名文件 */
+        if (!$replace && is_file($filename)) {
+            $this->error = '存在同名文件' . $filename;
+            return false;
+        }
+
+        /* 移动文件 */
+        if ($this->isTest) {
+            rename($this->filename, $filename);
+        } elseif (!move_uploaded_file($this->filename, $filename)) {
+            $this->error = '文件上传保存错误!';
+            return false;
+        }
+        // 返回 File对象实例
+        //$file = new self($filename);
+        $this->setSaveName($saveName);
+        $this->setUploadInfo($this->info);
+        return $this;
+    }
+
+    /**
+     * 获取保存文件名
+     * @param  string|bool $savename 保存的文件名 默认自动生成
+     * @return string
+     */
+    protected function buildSaveName($savename)
+    {
+        if (true === $savename) {
+            // 自动生成文件名
+            if ($this->rule instanceof \Closure) {
+                $savename = call_user_func_array($this->rule, [$this]);
+            } else {
+                switch ($this->rule) {
+                    case 'date':
+                        $savename = date('Ymd') . DS . md5(microtime(true));
+                        break;
+                    default:
+                        if (in_array($this->rule, hash_algos())) {
+                            $hash = $this->hash($this->rule);
+                            $savename = substr($hash, 0, 2) . DS . substr($hash, 2);
+                        } elseif (is_callable($this->rule)) {
+                            $savename = call_user_func($this->rule);
+                        } else {
+                            $savename = date('Ymd') . DS . md5(microtime(true));
+                        }
+                }
+            }
+        } elseif ('' === $savename) {
+            $savename = $this->getInfo('name');
+        }
+        if (!strpos($savename, '.')) {
+            $savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
+        }
+        return $savename;
+    }
+
+    /**
+     * 获取错误代码信息
+     * @param int $errorNo  错误号
+     */
+    private function error($errorNo)
+    {
+        switch ($errorNo) {
+            case 1:
+            case 2:
+                $this->error = '上传文件大小超过了最大值!';
+                break;
+            case 3:
+                $this->error = '文件只有部分被上传!';
+                break;
+            case 4:
+                $this->error = '没有文件被上传!';
+                break;
+            case 6:
+                $this->error = '找不到临时文件夹!';
+                break;
+            case 7:
+                $this->error = '文件写入失败!';
+                break;
+            default:
+                $this->error = '未知上传错误!';
+        }
+    }
+
+    /**
+     * 获取错误信息
+     * @return mixed
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    public function __call($method, $args)
+    {
+        return $this->hash($method);
+    }
+}

+ 1831 - 0
Mall/Framework/Core/FirePHP.Class.php

@@ -0,0 +1,1831 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+// Authors:
+// - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
+// - qbbr, Sokolov Innokenty <sokolov.innokenty@gmail.com>, Copyright 2011, New BSD License
+// - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT Licensea
+
+/**
+ * *** BEGIN LICENSE BLOCK *****
+ *
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ *
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * ***** END LICENSE BLOCK *****
+ *
+ * @copyright       Copyright (C) 2007+ Christoph Dorn
+ * @author          Christoph Dorn <christoph@christophdorn.com>
+ * @license         [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ * @package         FirePHPCore
+ */
+
+/**
+ * @see http://code.google.com/p/firephp/issues/detail?id=112
+ */
+if (!defined('E_STRICT')) {
+    define('E_STRICT', 2048);
+}
+if (!defined('E_RECOVERABLE_ERROR')) {
+    define('E_RECOVERABLE_ERROR', 4096);
+}
+if (!defined('E_DEPRECATED')) {
+    define('E_DEPRECATED', 8192);
+}
+if (!defined('E_USER_DEPRECATED')) {
+    define('E_USER_DEPRECATED', 16384);
+}
+
+/**
+ * Sends the given data to the FirePHP Firefox Extension.
+ * The data can be displayed in the Firebug Console or in the
+ * "Server" request tab.
+ *
+ * For more information see: http://www.firephp.org/
+ *
+ * @copyright       Copyright (C) 2007+ Christoph Dorn
+ * @author          Christoph Dorn <christoph@christophdorn.com>
+ * @license         [MIT License](http://www.opensource.org/licenses/mit-license.php)
+ * @package         FirePHPCore
+ */
+class FirePHP {
+
+    /**
+     * FirePHP version
+     *
+     * @var string
+     */
+    const VERSION = '0.3';    // @pinf replace '0.3' with '%%VERSION%%'
+
+    /**
+     * Firebug LOG level
+     *
+     * Logs a message to firebug console.
+     *
+     * @var string
+     */
+    const LOG = 'LOG';
+
+    /**
+     * Firebug INFO level
+     *
+     * Logs a message to firebug console and displays an info icon before the message.
+     *
+     * @var string
+     */
+    const INFO = 'INFO';
+
+    /**
+     * Firebug WARN level
+     *
+     * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.
+     *
+     * @var string
+     */
+    const WARN = 'WARN';
+
+    /**
+     * Firebug ERROR level
+     *
+     * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
+     *
+     * @var string
+     */
+    const ERROR = 'ERROR';
+
+    /**
+     * Dumps a variable to firebug's server panel
+     *
+     * @var string
+     */
+    const DUMP = 'DUMP';
+
+    /**
+     * Displays a stack trace in firebug console
+     *
+     * @var string
+     */
+    const TRACE = 'TRACE';
+
+    /**
+     * Displays an exception in firebug console
+     *
+     * Increments the firebug error count.
+     *
+     * @var string
+     */
+    const EXCEPTION = 'EXCEPTION';
+
+    /**
+     * Displays an table in firebug console
+     *
+     * @var string
+     */
+    const TABLE = 'TABLE';
+
+    /**
+     * Starts a group in firebug console
+     *
+     * @var string
+     */
+    const GROUP_START = 'GROUP_START';
+
+    /**
+     * Ends a group in firebug console
+     *
+     * @var string
+     */
+    const GROUP_END = 'GROUP_END';
+
+    /**
+     * Singleton instance of FirePHP
+     *
+     * @var FirePHP
+     */
+    protected static $instance = null;
+
+    /**
+     * Flag whether we are logging from within the exception handler
+     *
+     * @var boolean
+     */
+    protected $inExceptionHandler = false;
+
+    /**
+     * Flag whether to throw PHP errors that have been converted to ErrorExceptions
+     *
+     * @var boolean
+     */
+    protected $throwErrorExceptions = true;
+
+    /**
+     * Flag whether to convert PHP assertion errors to Exceptions
+     *
+     * @var boolean
+     */
+    protected $convertAssertionErrorsToExceptions = true;
+
+    /**
+     * Flag whether to throw PHP assertion errors that have been converted to Exceptions
+     *
+     * @var boolean
+     */
+    protected $throwAssertionExceptions = false;
+
+    /**
+     * Wildfire protocol message index
+     *
+     * @var integer
+     */
+    protected $messageIndex = 1;
+
+    /**
+     * Options for the library
+     *
+     * @var array
+     */
+    protected $options = array('maxDepth' => 10,
+        'maxObjectDepth' => 5,
+        'maxArrayDepth' => 5,
+        'useNativeJsonEncode' => true,
+        'includeLineNumbers' => true);
+
+    /**
+     * Filters used to exclude object members when encoding
+     *
+     * @var array
+     */
+    protected $objectFilters = array(
+        'firephp' => array('objectStack', 'instance', 'json_objectStack'),
+        'firephp_test_class' => array('objectStack', 'instance', 'json_objectStack')
+    );
+
+    /**
+     * A stack of objects used to detect recursion during object encoding
+     *
+     * @var object
+     */
+    protected $objectStack = array();
+
+    /**
+     * Flag to enable/disable logging
+     *
+     * @var boolean
+     */
+    protected $enabled = true;
+
+    /**
+     * The insight console to log to if applicable
+     *
+     * @var object
+     */
+    protected $logToInsightConsole = null;
+
+    /**
+     * When the object gets serialized only include specific object members.
+     *
+     * @return array
+     */
+    public function __sleep()
+    {
+        return array('options', 'objectFilters', 'enabled');
+    }
+
+    /**
+     * Gets singleton instance of FirePHP
+     *
+     * @param boolean $autoCreate
+     * @return FirePHP
+     */
+    public static function getInstance($autoCreate = false)
+    {
+        if ($autoCreate === true && !self::$instance) {
+            self::init();
+        }
+        return self::$instance;
+    }
+
+    /**
+     * Creates FirePHP object and stores it for singleton access
+     *
+     * @return FirePHP
+     */
+    public static function init()
+    {
+        return self::setInstance(new self());
+    }
+
+    /**
+     * Set the instance of the FirePHP singleton
+     *
+     * @param FirePHP $instance The FirePHP object instance
+     * @return FirePHP
+     */
+    public static function setInstance($instance)
+    {
+        return self::$instance = $instance;
+    }
+
+    /**
+     * Set an Insight console to direct all logging calls to
+     *
+     * @param object $console The console object to log to
+     * @return void
+     */
+    public function setLogToInsightConsole($console)
+    {
+        if (is_string($console)) {
+            if (get_class($this) != 'FirePHP_Insight' && !is_subclass_of($this, 'FirePHP_Insight')) {
+                throw new Exception('FirePHP instance not an instance or subclass of FirePHP_Insight!');
+            }
+            $this->logToInsightConsole = $this->to('request')->console($console);
+        } else {
+            $this->logToInsightConsole = $console;
+        }
+    }
+
+    /**
+     * Enable and disable logging to Firebug
+     *
+     * @param boolean $enabled TRUE to enable, FALSE to disable
+     * @return void
+     */
+    public function setEnabled($enabled)
+    {
+        $this->enabled = $enabled;
+    }
+
+    /**
+     * Check if logging is enabled
+     *
+     * @return boolean TRUE if enabled
+     */
+    public function getEnabled()
+    {
+        return $this->enabled;
+    }
+
+    /**
+     * Specify a filter to be used when encoding an object
+     *
+     * Filters are used to exclude object members.
+     *
+     * @param string $class The class name of the object
+     * @param array $filter An array of members to exclude
+     * @return void
+     */
+    public function setObjectFilter($class, $filter)
+    {
+        $this->objectFilters[strtolower($class)] = $filter;
+    }
+
+    /**
+     * Set some options for the library
+     *
+     * Options:
+     *  - maxDepth: The maximum depth to traverse (default: 10)
+     *  - maxObjectDepth: The maximum depth to traverse objects (default: 5)
+     *  - maxArrayDepth: The maximum depth to traverse arrays (default: 5)
+     *  - useNativeJsonEncode: If true will use json_encode() (default: true)
+     *  - includeLineNumbers: If true will include line numbers and filenames (default: true)
+     *
+     * @param array $options The options to be set
+     * @return void
+     */
+    public function setOptions($options)
+    {
+        $this->options = array_merge($this->options, $options);
+    }
+
+    /**
+     * Get options from the library
+     *
+     * @return array The currently set options
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Set an option for the library
+     *
+     * @param string $name
+     * @param mixed $value
+     * @return void
+     * @throws Exception
+     */
+    public function setOption($name, $value)
+    {
+        if (!isset($this->options[$name])) {
+            throw $this->newException('Unknown option: ' . $name);
+        }
+        $this->options[$name] = $value;
+    }
+
+    /**
+     * Get an option from the library
+     *
+     * @param string $name
+     * @return mixed
+     * @throws Exception
+     */
+    public function getOption($name)
+    {
+        if (!isset($this->options[$name])) {
+            throw $this->newException('Unknown option: ' . $name);
+        }
+        return $this->options[$name];
+    }
+
+    /**
+     * Register FirePHP as your error handler
+     *
+     * Will throw exceptions for each php error.
+     *
+     * @return mixed Returns a string containing the previously defined error handler (if any)
+     */
+    public function registerErrorHandler($throwErrorExceptions = false)
+    {
+        //NOTE: The following errors will not be caught by this error handler:
+        //      E_ERROR, E_PARSE, E_CORE_ERROR,
+        //      E_CORE_WARNING, E_COMPILE_ERROR,
+        //      E_COMPILE_WARNING, E_STRICT
+
+        $this->throwErrorExceptions = $throwErrorExceptions;
+
+        return set_error_handler(array($this, 'errorHandler'));
+    }
+
+    /**
+     * FirePHP's error handler
+     *
+     * Throws exception for each php error that will occur.
+     *
+     * @param integer $errno
+     * @param string $errstr
+     * @param string $errfile
+     * @param integer $errline
+     * @param array $errcontext
+     */
+    public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
+    {
+        // Don't throw exception if error reporting is switched off
+        if (error_reporting() == 0) {
+            return;
+        }
+        // Only throw exceptions for errors we are asking for
+        if (error_reporting() & $errno) {
+
+            $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
+            if ($this->throwErrorExceptions) {
+                throw $exception;
+            } else {
+                $this->fb($exception);
+            }
+        }
+    }
+
+    /**
+     * Register FirePHP as your exception handler
+     *
+     * @return mixed Returns the name of the previously defined exception handler,
+     *               or NULL on error.
+     *               If no previous handler was defined, NULL is also returned.
+     */
+    public function registerExceptionHandler()
+    {
+        return set_exception_handler(array($this, 'exceptionHandler'));
+    }
+
+    /**
+     * FirePHP's exception handler
+     *
+     * Logs all exceptions to your firebug console and then stops the script.
+     *
+     * @param Exception $exception
+     * @throws Exception
+     */
+    function exceptionHandler($exception)
+    {
+        $this->inExceptionHandler = true;
+
+        header('HTTP/1.1 500 Internal Server Error');
+
+        try {
+            $this->fb($exception);
+        } catch (Exception $e) {
+            echo 'We had an exception: ' . $e;
+        }
+
+        $this->inExceptionHandler = false;
+    }
+
+    /**
+     * Register FirePHP driver as your assert callback
+     *
+     * @param boolean $convertAssertionErrorsToExceptions
+     * @param boolean $throwAssertionExceptions
+     * @return mixed Returns the original setting or FALSE on errors
+     */
+    public function registerAssertionHandler($convertAssertionErrorsToExceptions = true, $throwAssertionExceptions = false)
+    {
+        $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions;
+        $this->throwAssertionExceptions = $throwAssertionExceptions;
+
+        if ($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) {
+            throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!');
+        }
+
+        return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
+    }
+
+    /**
+     * FirePHP's assertion handler
+     *
+     * Logs all assertions to your firebug console and then stops the script.
+     *
+     * @param string $file File source of assertion
+     * @param integer $line Line source of assertion
+     * @param mixed $code Assertion code
+     */
+    public function assertionHandler($file, $line, $code)
+    {
+        if ($this->convertAssertionErrorsToExceptions) {
+
+            $exception = new ErrorException('Assertion Failed - Code[ ' . $code . ' ]', 0, null, $file, $line);
+
+            if ($this->throwAssertionExceptions) {
+                throw $exception;
+            } else {
+                $this->fb($exception);
+            }
+
+        } else {
+            $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File' => $file, 'Line' => $line));
+        }
+    }
+
+    /**
+     * Start a group for following messages.
+     *
+     * Options:
+     *   Collapsed: [true|false]
+     *   Color:     [#RRGGBB|ColorName]
+     *
+     * @param string $name
+     * @param array $options OPTIONAL Instructions on how to log the group
+     * @return true
+     * @throws Exception
+     */
+    public function group($name, $options = null)
+    {
+
+        if (!$name) {
+            throw $this->newException('You must specify a label for the group!');
+        }
+
+        if ($options) {
+            if (!is_array($options)) {
+                throw $this->newException('Options must be defined as an array!');
+            }
+            if (array_key_exists('Collapsed', $options)) {
+                $options['Collapsed'] = ($options['Collapsed']) ? 'true' : 'false';
+            }
+        }
+
+        return $this->fb(null, $name, FirePHP::GROUP_START, $options);
+    }
+
+    /**
+     * Ends a group you have started before
+     *
+     * @return true
+     * @throws Exception
+     */
+    public function groupEnd()
+    {
+        return $this->fb(null, null, FirePHP::GROUP_END);
+    }
+
+    /**
+     * Log object with label to firebug console
+     *
+     * @see FirePHP::LOG
+     * @param mixes $object
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function log($object, $label = null, $options = array())
+    {
+        return $this->fb($object, $label, FirePHP::LOG, $options);
+    }
+
+    /**
+     * Log object with label to firebug console
+     *
+     * @see FirePHP::INFO
+     * @param mixes $object
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function info($object, $label = null, $options = array())
+    {
+        return $this->fb($object, $label, FirePHP::INFO, $options);
+    }
+
+    /**
+     * Log object with label to firebug console
+     *
+     * @see FirePHP::WARN
+     * @param mixes $object
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function warn($object, $label = null, $options = array())
+    {
+        return $this->fb($object, $label, FirePHP::WARN, $options);
+    }
+
+    /**
+     * Log object with label to firebug console
+     *
+     * @see FirePHP::ERROR
+     * @param mixes $object
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function error($object, $label = null, $options = array())
+    {
+        return $this->fb($object, $label, FirePHP::ERROR, $options);
+    }
+
+    /**
+     * Dumps key and variable to firebug server panel
+     *
+     * @see FirePHP::DUMP
+     * @param string $key
+     * @param mixed $variable
+     * @return true
+     * @throws Exception
+     */
+    public function dump($key, $variable, $options = array())
+    {
+        if (!is_string($key)) {
+            throw $this->newException('Key passed to dump() is not a string');
+        }
+        if (strlen($key) > 100) {
+            throw $this->newException('Key passed to dump() is longer than 100 characters');
+        }
+        if (!preg_match_all('/^[a-zA-Z0-9-_\.:]*$/', $key, $m)) {
+            throw $this->newException('Key passed to dump() contains invalid characters [a-zA-Z0-9-_\.:]');
+        }
+        return $this->fb($variable, $key, FirePHP::DUMP, $options);
+    }
+
+    /**
+     * Log a trace in the firebug console
+     *
+     * @see FirePHP::TRACE
+     * @param string $label
+     * @return true
+     * @throws Exception
+     */
+    public function trace($label)
+    {
+        return $this->fb($label, FirePHP::TRACE);
+    }
+
+    /**
+     * Log a table in the firebug console
+     *
+     * @see FirePHP::TABLE
+     * @param string $label
+     * @param string $table
+     * @return true
+     * @throws Exception
+     */
+    public function table($label, $table, $options = array())
+    {
+        return $this->fb($table, $label, FirePHP::TABLE, $options);
+    }
+
+    /**
+     * Insight API wrapper
+     *
+     * @see Insight_Helper::to()
+     */
+    public static function to()
+    {
+        $instance = self::getInstance();
+        if (!method_exists($instance, '_to')) {
+            throw new Exception('FirePHP::to() implementation not loaded');
+        }
+        $args = func_get_args();
+        return call_user_func_array(array($instance, '_to'), $args);
+    }
+
+    /**
+     * Insight API wrapper
+     *
+     * @see Insight_Helper::plugin()
+     */
+    public static function plugin()
+    {
+        $instance = self::getInstance();
+        if (!method_exists($instance, '_plugin')) {
+            throw new Exception('FirePHP::plugin() implementation not loaded');
+        }
+        $args = func_get_args();
+        return call_user_func_array(array($instance, '_plugin'), $args);
+    }
+
+    /**
+     * Check if FirePHP is installed on client
+     *
+     * @return boolean
+     */
+    public function detectClientExtension()
+    {
+        // Check if FirePHP is installed on client via User-Agent header
+        if (@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si', $this->getUserAgent(), $m) &&
+            version_compare($m[1][0], '0.0.6', '>=')) {
+            return true;
+        } else
+            // Check if FirePHP is installed on client via X-FirePHP-Version header
+            if (@preg_match_all('/^([\.\d]*)$/si', $this->getRequestHeader('X-FirePHP-Version'), $m) &&
+                version_compare($m[1][0], '0.0.6', '>=')) {
+                return true;
+            }
+        return false;
+    }
+
+    /**
+     * Log varible to Firebug
+     *
+     * @see http://www.firephp.org/Wiki/Reference/Fb
+     * @param mixed $object The variable to be logged
+     * @return boolean Return TRUE if message was added to headers, FALSE otherwise
+     * @throws Exception
+     */
+    public function fb($object)
+    {
+        if ($this instanceof FirePHP_Insight && method_exists($this, '_logUpgradeClientMessage')) {
+            if (!FirePHP_Insight::$upgradeClientMessageLogged) { // avoid infinite recursion as _logUpgradeClientMessage() logs a message
+                $this->_logUpgradeClientMessage();
+            }
+        }
+
+        static $insightGroupStack = array();
+
+        if (!$this->getEnabled()) {
+            return false;
+        }
+
+        if ($this->headersSent($filename, $linenum)) {
+            // If we are logging from within the exception handler we cannot throw another exception
+            if ($this->inExceptionHandler) {
+                // Simply echo the error out to the page
+                echo '<div style="border: 2px solid red; font-family: Arial; font-size: 12px; background-color: lightgray; padding: 5px;"><span style="color: red; font-weight: bold;">FirePHP ERROR:</span> Headers already sent in <b>' . $filename . '</b> on line <b>' . $linenum . '</b>. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.</div>';
+            } else {
+                throw $this->newException('Headers already sent in ' . $filename . ' on line ' . $linenum . '. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
+            }
+        }
+
+        $type = null;
+        $label = null;
+        $options = array();
+
+        if (func_num_args() == 1) {
+        } else if (func_num_args() == 2) {
+            switch (func_get_arg(1)) {
+                case self::LOG:
+                case self::INFO:
+                case self::WARN:
+                case self::ERROR:
+                case self::DUMP:
+                case self::TRACE:
+                case self::EXCEPTION:
+                case self::TABLE:
+                case self::GROUP_START:
+                case self::GROUP_END:
+                    $type = func_get_arg(1);
+                    break;
+                default:
+                    $label = func_get_arg(1);
+                    break;
+            }
+        } else if (func_num_args() == 3) {
+            $type = func_get_arg(2);
+            $label = func_get_arg(1);
+        } else if (func_num_args() == 4) {
+            $type = func_get_arg(2);
+            $label = func_get_arg(1);
+            $options = func_get_arg(3);
+        } else {
+            throw $this->newException('Wrong number of arguments to fb() function!');
+        }
+
+        if ($this->logToInsightConsole !== null && (get_class($this) == 'FirePHP_Insight' || is_subclass_of($this, 'FirePHP_Insight'))) {
+            $trace = debug_backtrace();
+            if (!$trace) return false;
+            for ($i = 0; $i < sizeof($trace); $i++) {
+                if (isset($trace[$i]['class'])) {
+                    if ($trace[$i]['class'] == 'FirePHP' || $trace[$i]['class'] == 'FB') {
+                        continue;
+                    }
+                }
+                if (isset($trace[$i]['file'])) {
+                    $path = $this->_standardizePath($trace[$i]['file']);
+                    if (substr($path, -18, 18) == 'FirePHPCore/fb.php' || substr($path, -29, 29) == 'FirePHPCore/FirePHP.php') {
+                        continue;
+                    }
+                }
+                if (isset($trace[$i]['function']) && $trace[$i]['function'] == 'fb' &&
+                    isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                    continue;
+                }
+                if (isset($trace[$i]['class']) && $trace[$i]['class'] == 'FB' &&
+                    isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                    continue;
+                }
+                break;
+            }
+            // adjust trace offset
+            $msg = $this->logToInsightConsole->option('encoder.trace.offsetAdjustment', $i);
+
+            if ($object instanceof Exception) {
+                $type = self::EXCEPTION;
+            }
+            if ($label && $type != self::TABLE && $type != self::GROUP_START) {
+                $msg = $msg->label($label);
+            }
+            switch ($type) {
+                case self::DUMP:
+                case self::LOG:
+                    return $msg->log($object);
+                case self::INFO:
+                    return $msg->info($object);
+                case self::WARN:
+                    return $msg->warn($object);
+                case self::ERROR:
+                    return $msg->error($object);
+                case self::TRACE:
+                    return $msg->trace($object);
+                case self::EXCEPTION:
+                    return $this->plugin('error')->handleException($object, $msg);
+                case self::TABLE:
+                    if (isset($object[0]) && !is_string($object[0]) && $label) {
+                        $object = array($label, $object);
+                    }
+                    return $msg->table($object[0], array_slice($object[1], 1), $object[1][0]);
+                case self::GROUP_START:
+                    $insightGroupStack[] = $msg->group(md5($label))->open();
+                    return $msg->log($label);
+                case self::GROUP_END:
+                    if (count($insightGroupStack) == 0) {
+                        throw new Error('Too many groupEnd() as opposed to group() calls!');
+                    }
+                    $group = array_pop($insightGroupStack);
+                    return $group->close();
+                default:
+                    return $msg->log($object);
+            }
+        }
+
+        if (!$this->detectClientExtension()) {
+            return false;
+        }
+
+        $meta = array();
+        $skipFinalObjectEncode = false;
+
+        if ($object instanceof Exception) {
+
+            $meta['file'] = $this->_escapeTraceFile($object->getFile());
+            $meta['line'] = $object->getLine();
+
+            $trace = $object->getTrace();
+            if ($object instanceof ErrorException
+                && isset($trace[0]['function'])
+                && $trace[0]['function'] == 'errorHandler'
+                && isset($trace[0]['class'])
+                && $trace[0]['class'] == 'FirePHP') {
+
+                $severity = false;
+                switch ($object->getSeverity()) {
+                    case E_WARNING:
+                        $severity = 'E_WARNING';
+                        break;
+
+                    case E_NOTICE:
+                        $severity = 'E_NOTICE';
+                        break;
+
+                    case E_USER_ERROR:
+                        $severity = 'E_USER_ERROR';
+                        break;
+
+                    case E_USER_WARNING:
+                        $severity = 'E_USER_WARNING';
+                        break;
+
+                    case E_USER_NOTICE:
+                        $severity = 'E_USER_NOTICE';
+                        break;
+
+                    case E_STRICT:
+                        $severity = 'E_STRICT';
+                        break;
+
+                    case E_RECOVERABLE_ERROR:
+                        $severity = 'E_RECOVERABLE_ERROR';
+                        break;
+
+                    case E_DEPRECATED:
+                        $severity = 'E_DEPRECATED';
+                        break;
+
+                    case E_USER_DEPRECATED:
+                        $severity = 'E_USER_DEPRECATED';
+                        break;
+                }
+
+                $object = array('Class' => get_class($object),
+                    'Message' => $severity . ': ' . $object->getMessage(),
+                    'File' => $this->_escapeTraceFile($object->getFile()),
+                    'Line' => $object->getLine(),
+                    'Type' => 'trigger',
+                    'Trace' => $this->_escapeTrace(array_splice($trace, 2)));
+                $skipFinalObjectEncode = true;
+            } else {
+                $object = array('Class' => get_class($object),
+                    'Message' => $object->getMessage(),
+                    'File' => $this->_escapeTraceFile($object->getFile()),
+                    'Line' => $object->getLine(),
+                    'Type' => 'throw',
+                    'Trace' => $this->_escapeTrace($trace));
+                $skipFinalObjectEncode = true;
+            }
+            $type = self::EXCEPTION;
+
+        } else if ($type == self::TRACE) {
+
+            $trace = debug_backtrace();
+            if (!$trace) return false;
+            for ($i = 0; $i < sizeof($trace); $i++) {
+
+                if (isset($trace[$i]['class'])
+                    && isset($trace[$i]['file'])
+                    && ($trace[$i]['class'] == 'FirePHP'
+                        || $trace[$i]['class'] == 'FB')
+                    && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
+                        || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.php')) {
+                    /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
+                } else
+                    if (isset($trace[$i]['class'])
+                        && isset($trace[$i+1]['file'])
+                        && $trace[$i]['class'] == 'FirePHP'
+                        && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                        /* Skip fb() */
+                    } else
+                        if ($trace[$i]['function'] == 'fb'
+                            || $trace[$i]['function'] == 'trace'
+                            || $trace[$i]['function'] == 'send') {
+
+                            $object = array('Class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : '',
+                                'Type' => isset($trace[$i]['type']) ? $trace[$i]['type'] : '',
+                                'Function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : '',
+                                'Message' => $trace[$i]['args'][0],
+                                'File' => isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '',
+                                'Line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : '',
+                                'Args' => isset($trace[$i]['args']) ? $this->encodeObject($trace[$i]['args']) : '',
+                                'Trace' => $this->_escapeTrace(array_splice($trace, $i + 1)));
+
+                            $skipFinalObjectEncode = true;
+                            $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
+                            $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
+                            break;
+                        }
+            }
+
+        } else
+            if ($type == self::TABLE) {
+
+                if (isset($object[0]) && is_string($object[0])) {
+                    $object[1] = $this->encodeTable($object[1]);
+                } else {
+                    $object = $this->encodeTable($object);
+                }
+
+                $skipFinalObjectEncode = true;
+
+            } else if ($type == self::GROUP_START) {
+
+                if (!$label) {
+                    throw $this->newException('You must specify a label for the group!');
+                }
+
+            } else {
+                if ($type === null) {
+                    $type = self::LOG;
+                }
+            }
+
+        if ($this->options['includeLineNumbers']) {
+            if (!isset($meta['file']) || !isset($meta['line'])) {
+
+                $trace = debug_backtrace();
+                for ($i = 0; $trace && $i < sizeof($trace); $i++) {
+
+                    if (isset($trace[$i]['class'])
+                        && isset($trace[$i]['file'])
+                        && ($trace[$i]['class'] == 'FirePHP'
+                            || $trace[$i]['class'] == 'FB')
+                        && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
+                            || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.php')) {
+                        /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
+                    } else
+                        if (isset($trace[$i]['class'])
+                            && isset($trace[$i + 1]['file'])
+                            && $trace[$i]['class'] == 'FirePHP'
+                            && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                            /* Skip fb() */
+                        } else
+                            if (isset($trace[$i]['file'])
+                                && substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php') {
+                                /* Skip FB::fb() */
+                            } else {
+                                $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
+                                $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
+                                break;
+                            }
+                }
+            }
+        } else {
+            unset($meta['file']);
+            unset($meta['line']);
+        }
+
+        $this->setHeader('X-Wf-Protocol-1', 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
+        $this->setHeader('X-Wf-1-Plugin-1', 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/' . self::VERSION);
+
+        $structureIndex = 1;
+        if ($type == self::DUMP) {
+            $structureIndex = 2;
+            $this->setHeader('X-Wf-1-Structure-2', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
+        } else {
+            $this->setHeader('X-Wf-1-Structure-1', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
+        }
+
+        if ($type == self::DUMP) {
+            $msg = '{"' . $label . '":' . $this->jsonEncode($object, $skipFinalObjectEncode) . '}';
+        } else {
+            $msgMeta = $options;
+            $msgMeta['Type'] = $type;
+            if ($label !== null) {
+                $msgMeta['Label'] = $label;
+            }
+            if (isset($meta['file']) && !isset($msgMeta['File'])) {
+                $msgMeta['File'] = $meta['file'];
+            }
+            if (isset($meta['line']) && !isset($msgMeta['Line'])) {
+                $msgMeta['Line'] = $meta['line'];
+            }
+            $msg = '[' . $this->jsonEncode($msgMeta) . ',' . $this->jsonEncode($object, $skipFinalObjectEncode) . ']';
+        }
+
+        $parts = explode("\n", chunk_split($msg, 5000, "\n"));
+
+        for ($i = 0; $i < count($parts); $i++) {
+
+            $part = $parts[$i];
+            if ($part) {
+
+                if (count($parts) > 2) {
+                    // Message needs to be split into multiple parts
+                    $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
+                        (($i == 0) ? strlen($msg) : '')
+                        . '|' . $part . '|'
+                        . (($i < count($parts) - 2) ? '\\' : ''));
+                } else {
+                    $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
+                        strlen($part) . '|' . $part . '|');
+                }
+
+                $this->messageIndex++;
+
+                if ($this->messageIndex > 99999) {
+                    throw $this->newException('Maximum number (99,999) of messages reached!');
+                }
+            }
+        }
+
+        $this->setHeader('X-Wf-1-Index', $this->messageIndex - 1);
+
+        return true;
+    }
+
+    /**
+     * Standardizes path for windows systems.
+     *
+     * @param string $path
+     * @return string
+     */
+    protected function _standardizePath($path)
+    {
+        return preg_replace('/\\\\+/', '/', $path);
+    }
+
+    /**
+     * Escape trace path for windows systems
+     *
+     * @param array $trace
+     * @return array
+     */
+    protected function _escapeTrace($trace)
+    {
+        if (!$trace) return $trace;
+        for ($i = 0; $i < sizeof($trace); $i++) {
+            if (isset($trace[$i]['file'])) {
+                $trace[$i]['file'] = $this->_escapeTraceFile($trace[$i]['file']);
+            }
+            if (isset($trace[$i]['args'])) {
+                $trace[$i]['args'] = $this->encodeObject($trace[$i]['args']);
+            }
+        }
+        return $trace;
+    }
+
+    /**
+     * Escape file information of trace for windows systems
+     *
+     * @param string $file
+     * @return string
+     */
+    protected function _escapeTraceFile($file)
+    {
+        /* Check if we have a windows filepath */
+        if (strpos($file, '\\')) {
+            /* First strip down to single \ */
+
+            $file = preg_replace('/\\\\+/', '\\', $file);
+
+            return $file;
+        }
+        return $file;
+    }
+
+    /**
+     * Check if headers have already been sent
+     *
+     * @param string $filename
+     * @param integer $linenum
+     */
+    protected function headersSent(&$filename, &$linenum)
+    {
+        return headers_sent($filename, $linenum);
+    }
+
+    /**
+     * Send header
+     *
+     * @param string $name
+     * @param string $value
+     */
+    protected function setHeader($name, $value)
+    {
+        return header($name . ': ' . $value);
+    }
+
+    /**
+     * Get user agent
+     *
+     * @return string|false
+     */
+    protected function getUserAgent()
+    {
+        if (!isset($_SERVER['HTTP_USER_AGENT'])) return false;
+        return $_SERVER['HTTP_USER_AGENT'];
+    }
+
+    /**
+     * Get all request headers
+     *
+     * @return array
+     */
+    public static function getAllRequestHeaders()
+    {
+        static $_cachedHeaders = false;
+        if ($_cachedHeaders !== false) {
+            return $_cachedHeaders;
+        }
+        $headers = array();
+        if (function_exists('getallheaders')) {
+            foreach (getallheaders() as $name => $value) {
+                $headers[strtolower($name)] = $value;
+            }
+        } else {
+            foreach ($_SERVER as $name => $value) {
+                if (substr($name, 0, 5) == 'HTTP_') {
+                    $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value;
+                }
+            }
+        }
+        return $_cachedHeaders = $headers;
+    }
+
+    /**
+     * Get a request header
+     *
+     * @return string|false
+     */
+    protected function getRequestHeader($name)
+    {
+        $headers = self::getAllRequestHeaders();
+        if (isset($headers[strtolower($name)])) {
+            return $headers[strtolower($name)];
+        }
+        return false;
+    }
+
+    /**
+     * Returns a new exception
+     *
+     * @param string $message
+     * @return Exception
+     */
+    protected function newException($message)
+    {
+        return new Exception($message);
+    }
+
+    /**
+     * Encode an object into a JSON string
+     *
+     * Uses PHP's jeson_encode() if available
+     *
+     * @param object $object The object to be encoded
+     * @param boolean $skipObjectEncode
+     * @return string The JSON string
+     */
+    public function jsonEncode($object, $skipObjectEncode = false)
+    {
+        if (!$skipObjectEncode) {
+            $object = $this->encodeObject($object);
+        }
+
+        if (function_exists('json_encode')
+            && $this->options['useNativeJsonEncode'] != false) {
+
+            return json_encode($object);
+        } else {
+            return $this->json_encode($object);
+        }
+    }
+
+    /**
+     * Encodes a table by encoding each row and column with encodeObject()
+     *
+     * @param array $table The table to be encoded
+     * @return array
+     */
+    protected function encodeTable($table)
+    {
+        if (!$table) return $table;
+
+        $newTable = array();
+        foreach ($table as $row) {
+
+            if (is_array($row)) {
+                $newRow = array();
+
+                foreach ($row as $item) {
+                    $newRow[] = $this->encodeObject($item);
+                }
+
+                $newTable[] = $newRow;
+            }
+        }
+
+        return $newTable;
+    }
+
+    /**
+     * Encodes an object including members with
+     * protected and private visibility
+     *
+     * @param object $object The object to be encoded
+     * @param integer $Depth The current traversal depth
+     * @return array All members of the object
+     */
+    protected function encodeObject($object, $objectDepth = 1, $arrayDepth = 1, $maxDepth = 1)
+    {
+        if ($maxDepth > $this->options['maxDepth']) {
+            return '** Max Depth (' . $this->options['maxDepth'] . ') **';
+        }
+
+        $return = array();
+
+        if (is_resource($object)) {
+
+            return '** ' . (string) $object . ' **';
+
+        } else if (is_object($object)) {
+
+            if ($objectDepth > $this->options['maxObjectDepth']) {
+                return '** Max Object Depth (' . $this->options['maxObjectDepth'] . ') **';
+            }
+
+            foreach ($this->objectStack as $refVal) {
+                if ($refVal === $object) {
+                    return '** Recursion (' . get_class($object) . ') **';
+                }
+            }
+            array_push($this->objectStack, $object);
+
+            $return['__className'] = $class = get_class($object);
+            $classLower = strtolower($class);
+
+            $reflectionClass = new ReflectionClass($class);
+            $properties = array();
+            foreach ($reflectionClass->getProperties() as $property) {
+                $properties[$property->getName()] = $property;
+            }
+
+            $members = (array)$object;
+
+            foreach ($properties as $plainName => $property) {
+
+                $name = $rawName = $plainName;
+                if ($property->isStatic()) {
+                    $name = 'static:' . $name;
+                }
+                if ($property->isPublic()) {
+                    $name = 'public:' . $name;
+                } else if ($property->isPrivate()) {
+                    $name = 'private:' . $name;
+                    $rawName = "\0" . $class . "\0" . $rawName;
+                } else if ($property->isProtected()) {
+                    $name = 'protected:' . $name;
+                    $rawName = "\0" . '*' . "\0" . $rawName;
+                }
+
+                if (!(isset($this->objectFilters[$classLower])
+                    && is_array($this->objectFilters[$classLower])
+                    && in_array($plainName, $this->objectFilters[$classLower]))) {
+
+                    if (array_key_exists($rawName, $members) && !$property->isStatic()) {
+                        $return[$name] = $this->encodeObject($members[$rawName], $objectDepth + 1, 1, $maxDepth + 1);
+                    } else {
+                        if (method_exists($property, 'setAccessible')) {
+                            $property->setAccessible(true);
+                            $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
+                        } else
+                            if ($property->isPublic()) {
+                                $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
+                            } else {
+                                $return[$name] = '** Need PHP 5.3 to get value **';
+                            }
+                    }
+                } else {
+                    $return[$name] = '** Excluded by Filter **';
+                }
+            }
+
+            // Include all members that are not defined in the class
+            // but exist in the object
+            foreach ($members as $rawName => $value) {
+
+                $name = $rawName;
+
+                if ($name{0} == "\0") {
+                    $parts = explode("\0", $name);
+                    $name = $parts[2];
+                }
+
+                $plainName = $name;
+
+                if (!isset($properties[$name])) {
+                    $name = 'undeclared:' . $name;
+
+                    if (!(isset($this->objectFilters[$classLower])
+                        && is_array($this->objectFilters[$classLower])
+                        && in_array($plainName, $this->objectFilters[$classLower]))) {
+
+                        $return[$name] = $this->encodeObject($value, $objectDepth + 1, 1, $maxDepth + 1);
+                    } else {
+                        $return[$name] = '** Excluded by Filter **';
+                    }
+                }
+            }
+
+            array_pop($this->objectStack);
+
+        } elseif (is_array($object)) {
+
+            if ($arrayDepth > $this->options['maxArrayDepth']) {
+                return '** Max Array Depth (' . $this->options['maxArrayDepth'] . ') **';
+            }
+
+            foreach ($object as $key => $val) {
+
+                // Encoding the $GLOBALS PHP array causes an infinite loop
+                // if the recursion is not reset here as it contains
+                // a reference to itself. This is the only way I have come up
+                // with to stop infinite recursion in this case.
+                if ($key == 'GLOBALS'
+                    && is_array($val)
+                    && array_key_exists('GLOBALS', $val)) {
+                    $val['GLOBALS'] = '** Recursion (GLOBALS) **';
+                }
+
+                if (!$this->is_utf8($key)) {
+                    $key = utf8_encode($key);
+                }
+
+                $return[$key] = $this->encodeObject($val, 1, $arrayDepth + 1, $maxDepth + 1);
+            }
+        } else {
+            if ($this->is_utf8($object)) {
+                return $object;
+            } else {
+                return utf8_encode($object);
+            }
+        }
+        return $return;
+    }
+
+    /**
+     * Returns true if $string is valid UTF-8 and false otherwise.
+     *
+     * @param mixed $str String to be tested
+     * @return boolean
+     */
+    protected function is_utf8($str)
+    {
+        if (function_exists('mb_detect_encoding')) {
+            return (
+                mb_detect_encoding($str, 'UTF-8', true) == 'UTF-8' &&
+                ($str === null || $this->jsonEncode($str, true) !== 'null')
+            );
+        }
+        $c = 0;
+        $b = 0;
+        $bits = 0;
+        $len = strlen($str);
+        for ($i = 0; $i < $len; $i++) {
+            $c = ord($str[$i]);
+            if ($c > 128) {
+                if (($c >= 254)) return false;
+                elseif ($c >= 252) $bits = 6;
+                elseif ($c >= 248) $bits = 5;
+                elseif ($c >= 240) $bits = 4;
+                elseif ($c >= 224) $bits = 3;
+                elseif ($c >= 192) $bits = 2;
+                else return false;
+                if (($i + $bits) > $len) return false;
+                while($bits > 1) {
+                    $i++;
+                    $b = ord($str[$i]);
+                    if ($b < 128 || $b > 191) return false;
+                    $bits--;
+                }
+            }
+        }
+        return ($str === null || $this->jsonEncode($str, true) !== 'null');
+    }
+
+    /**
+     * Converts to and from JSON format.
+     *
+     * JSON (JavaScript Object Notation) is a lightweight data-interchange
+     * format. It is easy for humans to read and write. It is easy for machines
+     * to parse and generate. It is based on a subset of the JavaScript
+     * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
+     * This feature can also be found in  Python. JSON is a text format that is
+     * completely language independent but uses conventions that are familiar
+     * to programmers of the C-family of languages, including C, C++, C#, Java,
+     * JavaScript, Perl, TCL, and many others. These properties make JSON an
+     * ideal data-interchange language.
+     *
+     * This package provides a simple encoder and decoder for JSON notation. It
+     * is intended for use with client-side Javascript applications that make
+     * use of HTTPRequest to perform server communication functions - data can
+     * be encoded into JSON notation for use in a client-side javascript, or
+     * decoded from incoming Javascript requests. JSON format is native to
+     * Javascript, and can be directly eval()'ed with no further parsing
+     * overhead
+     *
+     * All strings should be in ASCII or UTF-8 format!
+     *
+     * LICENSE: Redistribution and use in source and binary forms, with or
+     * without modification, are permitted provided that the following
+     * conditions are met: Redistributions of source code must retain the
+     * above copyright notice, this list of conditions and the following
+     * disclaimer. Redistributions in binary form must reproduce the above
+     * copyright notice, this list of conditions and the following disclaimer
+     * in the documentation and/or other materials provided with the
+     * distribution.
+     *
+     * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+     * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+     * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+     * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+     * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+     * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+     * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+     * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+     * DAMAGE.
+     *
+     * @category
+     * @package     Services_JSON
+     * @author      Michal Migurski <mike-json@teczno.com>
+     * @author      Matt Knapp <mdknapp[at]gmail[dot]com>
+     * @author      Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
+     * @author      Christoph Dorn <christoph@christophdorn.com>
+     * @copyright   2005 Michal Migurski
+     * @version     CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
+     * @license     http://www.opensource.org/licenses/bsd-license.php
+     * @link        http://pear.php.net/pepr/pepr-proposal-show.php?id=198
+     */
+
+
+    /**
+     * Keep a list of objects as we descend into the array so we can detect recursion.
+     */
+    private $json_objectStack = array();
+
+
+    /**
+     * convert a string from one UTF-8 char to one UTF-16 char
+     *
+     * Normally should be handled by mb_convert_encoding, but
+     * provides a slower PHP-only method for installations
+     * that lack the multibye string extension.
+     *
+     * @param    string  $utf8   UTF-8 character
+     * @return   string  UTF-16 character
+     * @access   private
+     */
+    private function json_utf82utf16($utf8)
+    {
+        // oh please oh please oh please oh please oh please
+        if (function_exists('mb_convert_encoding')) {
+            return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
+        }
+
+        switch (strlen($utf8)) {
+            case 1:
+                // this case should never be reached, because we are in ASCII range
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return $utf8;
+
+            case 2:
+                // return a UTF-16 character from a 2-byte UTF-8 char
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return chr(0x07 & (ord($utf8{0}) >> 2))
+                . chr((0xC0 & (ord($utf8{0}) << 6))
+                    | (0x3F & ord($utf8{1})));
+
+            case 3:
+                // return a UTF-16 character from a 3-byte UTF-8 char
+                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                return chr((0xF0 & (ord($utf8{0}) << 4))
+                    | (0x0F & (ord($utf8{1}) >> 2)))
+                . chr((0xC0 & (ord($utf8{1}) << 6))
+                    | (0x7F & ord($utf8{2})));
+        }
+
+        // ignoring UTF-32 for now, sorry
+        return '';
+    }
+
+    /**
+     * encodes an arbitrary variable into JSON format
+     *
+     * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
+     *                           see argument 1 to Services_JSON() above for array-parsing behavior.
+     *                           if var is a strng, note that encode() always expects it
+     *                           to be in ASCII or UTF-8 format!
+     *
+     * @return   mixed   JSON string representation of input var or an error if a problem occurs
+     * @access   public
+     */
+    private function json_encode($var)
+    {
+        if (is_object($var)) {
+            if (in_array($var, $this->json_objectStack)) {
+                return '"** Recursion **"';
+            }
+        }
+
+        switch (gettype($var)) {
+            case 'boolean':
+                return $var ? 'true' : 'false';
+
+            case 'NULL':
+                return 'null';
+
+            case 'integer':
+                return (int) $var;
+
+            case 'double':
+            case 'float':
+                return (float) $var;
+
+            case 'string':
+                // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
+                $ascii = '';
+                $strlen_var = strlen($var);
+
+                /*
+                 * Iterate over every character in the string,
+                 * escaping with a slash or encoding to UTF-8 where necessary
+                 */
+                for ($c = 0; $c < $strlen_var; ++$c) {
+
+                    $ord_var_c = ord($var{$c});
+
+                    switch (true) {
+                        case $ord_var_c == 0x08:
+                            $ascii .= '\b';
+                            break;
+                        case $ord_var_c == 0x09:
+                            $ascii .= '\t';
+                            break;
+                        case $ord_var_c == 0x0A:
+                            $ascii .= '\n';
+                            break;
+                        case $ord_var_c == 0x0C:
+                            $ascii .= '\f';
+                            break;
+                        case $ord_var_c == 0x0D:
+                            $ascii .= '\r';
+                            break;
+
+                        case $ord_var_c == 0x22:
+                        case $ord_var_c == 0x2F:
+                        case $ord_var_c == 0x5C:
+                            // double quote, slash, slosh
+                            $ascii .= '\\' . $var{$c};
+                            break;
+
+                        case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
+                            // characters U-00000000 - U-0000007F (same as ASCII)
+                            $ascii .= $var{$c};
+                            break;
+
+                        case (($ord_var_c & 0xE0) == 0xC0):
+                            // characters U-00000080 - U-000007FF, mask 110XXXXX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
+                            $c += 1;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xF0) == 0xE0):
+                            // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                ord($var{$c + 1}),
+                                ord($var{$c + 2}));
+                            $c += 2;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xF8) == 0xF0):
+                            // characters U-00010000 - U-001FFFFF, mask 11110XXX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                ord($var{$c + 1}),
+                                ord($var{$c + 2}),
+                                ord($var{$c + 3}));
+                            $c += 3;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xFC) == 0xF8):
+                            // characters U-00200000 - U-03FFFFFF, mask 111110XX
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                ord($var{$c + 1}),
+                                ord($var{$c + 2}),
+                                ord($var{$c + 3}),
+                                ord($var{$c + 4}));
+                            $c += 4;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+
+                        case (($ord_var_c & 0xFE) == 0xFC):
+                            // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+                            $char = pack('C*', $ord_var_c,
+                                ord($var{$c + 1}),
+                                ord($var{$c + 2}),
+                                ord($var{$c + 3}),
+                                ord($var{$c + 4}),
+                                ord($var{$c + 5}));
+                            $c += 5;
+                            $utf16 = $this->json_utf82utf16($char);
+                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
+                            break;
+                    }
+                }
+
+                return '"' . $ascii . '"';
+
+            case 'array':
+                /*
+                 * As per JSON spec if any array key is not an integer
+                 * we must treat the the whole array as an object. We
+                 * also try to catch a sparsely populated associative
+                 * array with numeric keys here because some JS engines
+                 * will create an array with empty indexes up to
+                 * max_index which can cause memory issues and because
+                 * the keys, which may be relevant, will be remapped
+                 * otherwise.
+                 *
+                 * As per the ECMA and JSON specification an object may
+                 * have any string as a property. Unfortunately due to
+                 * a hole in the ECMA specification if the key is a
+                 * ECMA reserved word or starts with a digit the
+                 * parameter is only accessible using ECMAScript's
+                 * bracket notation.
+                 */
+
+                // treat as a JSON object
+                if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
+
+                    $this->json_objectStack[] = $var;
+
+                    $properties = array_map(array($this, 'json_name_value'),
+                        array_keys($var),
+                        array_values($var));
+
+                    array_pop($this->json_objectStack);
+
+                    foreach ($properties as $property) {
+                        if ($property instanceof Exception) {
+                            return $property;
+                        }
+                    }
+
+                    return '{' . join(',', $properties) . '}';
+                }
+
+                $this->json_objectStack[] = $var;
+
+                // treat it like a regular array
+                $elements = array_map(array($this, 'json_encode'), $var);
+
+                array_pop($this->json_objectStack);
+
+                foreach ($elements as $element) {
+                    if ($element instanceof Exception) {
+                        return $element;
+                    }
+                }
+
+                return '[' . join(',', $elements) . ']';
+
+            case 'object':
+                $vars = self::encodeObject($var);
+
+                $this->json_objectStack[] = $var;
+
+                $properties = array_map(array($this, 'json_name_value'),
+                    array_keys($vars),
+                    array_values($vars));
+
+                array_pop($this->json_objectStack);
+
+                foreach ($properties as $property) {
+                    if ($property instanceof Exception) {
+                        return $property;
+                    }
+                }
+
+                return '{' . join(',', $properties) . '}';
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * array-walking function for use in generating JSON-formatted name-value pairs
+     *
+     * @param    string  $name   name of key to use
+     * @param    mixed   $value  reference to an array element to be encoded
+     *
+     * @return   string  JSON-formatted name-value pair, like '"name":value'
+     * @access   private
+     */
+    private function json_name_value($name, $value)
+    {
+        // Encoding the $GLOBALS PHP array causes an infinite loop
+        // if the recursion is not reset here as it contains
+        // a reference to itself. This is the only way I have come up
+        // with to stop infinite recursion in this case.
+        if ($name == 'GLOBALS'
+            && is_array($value)
+            && array_key_exists('GLOBALS', $value)) {
+            $value['GLOBALS'] = '** Recursion **';
+        }
+
+        $encodedValue = $this->json_encode($value);
+
+        if ($encodedValue instanceof Exception) {
+            return $encodedValue;
+        }
+
+        return $this->json_encode(strval($name)) . ':' . $encodedValue;
+    }
+
+    /**
+     * @deprecated
+     */
+    public function setProcessorUrl($URL)
+    {
+        trigger_error('The FirePHP::setProcessorUrl() method is no longer supported', E_USER_DEPRECATED);
+    }
+
+    /**
+     * @deprecated
+     */
+    public function setRendererUrl($URL)
+    {
+        trigger_error('The FirePHP::setRendererUrl() method is no longer supported', E_USER_DEPRECATED);
+    }
+}

+ 382 - 0
Mall/Framework/Core/Jwt.Class.php

@@ -0,0 +1,382 @@
+<?php
+namespace Mall\Framework\Core;
+
+use \DomainException;
+use \InvalidArgumentException;
+use \UnexpectedValueException;
+use \DateTime;
+
+/**
+ * JSON Web Token implementation, based on this spec:
+ * https://tools.ietf.org/html/rfc7519
+ *
+ * PHP version 5
+ *
+ * @category Authentication
+ * @package  Authentication_JWT
+ * @author   Neuman Vong <neuman@twilio.com>
+ * @author   Anant Narayanan <anant@php.net>
+ * @license  http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
+ * @link     https://github.com/firebase/php-jwt
+ */
+class Jwt
+{
+
+    /**
+     * When checking nbf, iat or expiration times,
+     * we want to provide some extra leeway time to
+     * account for clock skew.
+     */
+    public static $leeway = 0;
+
+    /**
+     * Allow the current timestamp to be specified.
+     * Useful for fixing a value within unit testing.
+     *
+     * Will default to PHP time() value if null.
+     */
+    public static $timestamp = null;
+
+    public static $supported_algs = array(
+        'HS256' => array('hash_hmac', 'SHA256'),
+        'HS512' => array('hash_hmac', 'SHA512'),
+        'HS384' => array('hash_hmac', 'SHA384'),
+        'RS256' => array('openssl', 'SHA256'),
+        'RS384' => array('openssl', 'SHA384'),
+        'RS512' => array('openssl', 'SHA512'),
+    );
+
+    /**
+     * Decodes a JWT string into a PHP object.
+     *
+     * @param string        $jwt            The JWT
+     * @param string|array  $key            The key, or map of keys.
+     *                                      If the algorithm used is asymmetric, this is the public key
+     * @param array         $allowed_algs   List of supported verification algorithms
+     *                                      Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
+     *
+     * @return object The JWT's payload as a PHP object
+     *
+     * @throws UnexpectedValueException     Provided JWT was invalid
+     * @throws SignatureInvalidException    Provided JWT was invalid because the signature verification failed
+     * @throws BeforeValidException         Provided JWT is trying to be used before it's eligible as defined by 'nbf'
+     * @throws BeforeValidException         Provided JWT is trying to be used before it's been created as defined by 'iat'
+     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
+     *
+     * @uses jsonDecode
+     * @uses urlsafeB64Decode
+     */
+    public static function decode($jwt, $key, $allowed_algs = array())
+    {
+        $timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;
+
+        if (empty($key)) {
+            throw new InvalidArgumentException('Key may not be empty');
+        }
+        if (!is_array($allowed_algs)) {
+            throw new InvalidArgumentException('Algorithm not allowed');
+        }
+        $tks = explode('.', $jwt);
+        if (count($tks) != 3) {
+            throw new UnexpectedValueException('Wrong number of segments');
+        }
+        list($headb64, $bodyb64, $cryptob64) = $tks;
+        if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
+            throw new UnexpectedValueException('Invalid header encoding');
+        }
+        if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
+            throw new UnexpectedValueException('Invalid claims encoding');
+        }
+        if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
+            throw new UnexpectedValueException('Invalid signature encoding');
+        }
+        if (empty($header->alg)) {
+            throw new UnexpectedValueException('Empty algorithm');
+        }
+        if (empty(static::$supported_algs[$header->alg])) {
+            throw new UnexpectedValueException('Algorithm not supported');
+        }
+        if (!in_array($header->alg, $allowed_algs)) {
+            throw new UnexpectedValueException('Algorithm not allowed');
+        }
+        if (is_array($key) || $key instanceof \ArrayAccess) {
+            if (isset($header->kid)) {
+                if (!isset($key[$header->kid])) {
+                    throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
+                }
+                $key = $key[$header->kid];
+            } else {
+                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
+            }
+        }
+
+        // Check the signature
+        if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
+            throw new UnexpectedValueException('Signature verification failed');
+        }
+
+        // Check if the nbf if it is defined. This is the time that the
+        // token can actually be used. If it's not yet that time, abort.
+        if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
+            throw new UnexpectedValueException(
+                'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
+            );
+        }
+
+        // Check that this token has been created before 'now'. This prevents
+        // using tokens that have been created for later use (and haven't
+        // correctly used the nbf claim).
+        if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
+            throw new UnexpectedValueException(
+                'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
+            );
+        }
+
+        // Check if this token has expired.
+        if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
+            throw new UnexpectedValueException('Expired token');
+        }
+
+        return $payload;
+    }
+
+    /**
+     * Converts and signs a PHP object or array into a JWT string.
+     *
+     * @param object|array  $payload    PHP object or array
+     * @param string        $key        The secret key.
+     *                                  If the algorithm used is asymmetric, this is the private key
+     * @param string        $alg        The signing algorithm.
+     *                                  Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
+     * @param mixed         $keyId
+     * @param array         $head       An array with header elements to attach
+     *
+     * @return string A signed JWT
+     *
+     * @uses jsonEncode
+     * @uses urlsafeB64Encode
+     */
+    public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
+    {
+        $header = array('typ' => 'JWT', 'alg' => $alg);
+        if ($keyId !== null) {
+            $header['kid'] = $keyId;
+        }
+        if ( isset($head) && is_array($head) ) {
+            $header = array_merge($head, $header);
+        }
+        $segments = array();
+        $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
+        $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
+        $signing_input = implode('.', $segments);
+
+        $signature = static::sign($signing_input, $key, $alg);
+        $segments[] = static::urlsafeB64Encode($signature);
+
+        return implode('.', $segments);
+    }
+
+    /**
+     * Sign a string with a given key and algorithm.
+     *
+     * @param string            $msg    The message to sign
+     * @param string|resource   $key    The secret key
+     * @param string            $alg    The signing algorithm.
+     *                                  Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
+     *
+     * @return string An encrypted message
+     *
+     * @throws DomainException Unsupported algorithm was specified
+     */
+    public static function sign($msg, $key, $alg = 'HS256')
+    {
+        if (empty(static::$supported_algs[$alg])) {
+            throw new DomainException('Algorithm not supported');
+        }
+        list($function, $algorithm) = static::$supported_algs[$alg];
+        switch($function) {
+            case 'hash_hmac':
+                return hash_hmac($algorithm, $msg, $key, true);
+            case 'openssl':
+                $signature = '';
+                $success = openssl_sign($msg, $signature, $key, $algorithm);
+                if (!$success) {
+                    throw new DomainException("OpenSSL unable to sign data");
+                } else {
+                    return $signature;
+                }
+        }
+    }
+
+    /**
+     * Verify a signature with the message, key and method. Not all methods
+     * are symmetric, so we must have a separate verify and sign method.
+     *
+     * @param string            $msg        The original message (header and body)
+     * @param string            $signature  The original signature
+     * @param string|resource   $key        For HS*, a string key works. for RS*, must be a resource of an openssl public key
+     * @param string            $alg        The algorithm
+     *
+     * @return bool
+     *
+     * @throws DomainException Invalid Algorithm or OpenSSL failure
+     */
+    private static function verify($msg, $signature, $key, $alg)
+    {
+        if (empty(static::$supported_algs[$alg])) {
+            throw new DomainException('Algorithm not supported');
+        }
+
+        list($function, $algorithm) = static::$supported_algs[$alg];
+        switch($function) {
+            case 'openssl':
+                $success = openssl_verify($msg, $signature, $key, $algorithm);
+                if ($success === 1) {
+                    return true;
+                } elseif ($success === 0) {
+                    return false;
+                }
+                // returns 1 on success, 0 on failure, -1 on error.
+                throw new DomainException(
+                    'OpenSSL error: ' . openssl_error_string()
+                );
+            case 'hash_hmac':
+            default:
+                $hash = hash_hmac($algorithm, $msg, $key, true);
+                if (function_exists('hash_equals')) {
+                    return hash_equals($signature, $hash);
+                }
+                $len = min(static::safeStrlen($signature), static::safeStrlen($hash));
+
+                $status = 0;
+                for ($i = 0; $i < $len; $i++) {
+                    $status |= (ord($signature[$i]) ^ ord($hash[$i]));
+                }
+                $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
+
+                return ($status === 0);
+        }
+    }
+
+    /**
+     * Decode a JSON string into a PHP object.
+     *
+     * @param string $input JSON string
+     *
+     * @return object Object representation of JSON string
+     *
+     * @throws DomainException Provided string was invalid JSON
+     */
+    public static function jsonDecode($input)
+    {
+        if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
+            /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
+             * to specify that large ints (like Steam Transaction IDs) should be treated as
+             * strings, rather than the PHP default behaviour of converting them to floats.
+             */
+            $obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
+        } else {
+            /** Not all servers will support that, however, so for older versions we must
+             * manually detect large ints in the JSON string and quote them (thus converting
+             *them to strings) before decoding, hence the preg_replace() call.
+             */
+            $max_int_length = strlen((string) PHP_INT_MAX) - 1;
+            $json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
+            $obj = json_decode($json_without_bigints);
+        }
+
+        if (function_exists('json_last_error') && $errno = json_last_error()) {
+            static::handleJsonError($errno);
+        } elseif ($obj === null && $input !== 'null') {
+            throw new DomainException('Null result with non-null input');
+        }
+        return $obj;
+    }
+
+    /**
+     * Encode a PHP object into a JSON string.
+     *
+     * @param object|array $input A PHP object or array
+     *
+     * @return string JSON representation of the PHP object or array
+     *
+     * @throws DomainException Provided object could not be encoded to valid JSON
+     */
+    public static function jsonEncode($input)
+    {
+        $json = json_encode($input);
+        if (function_exists('json_last_error') && $errno = json_last_error()) {
+            static::handleJsonError($errno);
+        } elseif ($json === 'null' && $input !== null) {
+            throw new DomainException('Null result with non-null input');
+        }
+        return $json;
+    }
+
+    /**
+     * Decode a string with URL-safe Base64.
+     *
+     * @param string $input A Base64 encoded string
+     *
+     * @return string A decoded string
+     */
+    public static function urlsafeB64Decode($input)
+    {
+        $remainder = strlen($input) % 4;
+        if ($remainder) {
+            $padlen = 4 - $remainder;
+            $input .= str_repeat('=', $padlen);
+        }
+        return base64_decode(strtr($input, '-_', '+/'));
+    }
+
+    /**
+     * Encode a string with URL-safe Base64.
+     *
+     * @param string $input The string you want encoded
+     *
+     * @return string The base64 encode of what you passed in
+     */
+    public static function urlsafeB64Encode($input)
+    {
+        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
+    }
+
+    /**
+     * Helper method to create a JSON error.
+     *
+     * @param int $errno An error number from json_last_error()
+     *
+     * @return void
+     */
+    private static function handleJsonError($errno)
+    {
+        $messages = array(
+            JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
+            JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
+            JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
+            JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
+            JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
+        );
+        throw new DomainException(
+            isset($messages[$errno])
+            ? $messages[$errno]
+            : 'Unknown JSON error: ' . $errno
+        );
+    }
+
+    /**
+     * Get the number of bytes in cryptographic strings.
+     *
+     * @param string
+     *
+     * @return int
+     */
+    private static function safeStrlen($str)
+    {
+        if (function_exists('mb_strlen')) {
+            return mb_strlen($str, '8bit');
+        }
+        return strlen($str);
+    }
+}

+ 215 - 0
Mall/Framework/Core/Logs.Class.php

@@ -0,0 +1,215 @@
+<?php
+namespace Mall\Framework\Core;
+
+class Logs
+{
+    const LEVEL_TRACE          = 'trace';
+    const LEVEL_WARNING        = 'warning';
+    const LEVEL_ERROR          = 'error';
+    const LEVEL_INFO           = 'info';
+    const LEVEL_PROFILE        = 'profile';
+    const MAX_LOGS             = 10000;
+
+    public $rotateByCopy       = true;
+    public $maxLogFiles        = 5;
+    public $maxFileSize        = 100; // in MB
+
+    //系统日志标识
+    private $logSystem       = 'swoole-jobs';
+
+    private $logPath      = '';
+    //单个类型log
+    private $logs                 = [];
+    private $logCount             = 0;
+    //默认log文件存储名
+    private $logSaveFileApp       = 'application.log';
+
+    private static $instance=null;
+
+    public function __construct($logPath, $logSaveFileApp='', $logSystem = '')
+    {
+        if (empty($logPath)) {
+            die('config logPath must be set!' . PHP_EOL);
+        }
+        if (!is_dir($logPath)) {
+            mkdir($logPath, '0777', true);
+        }
+        $this->logPath = $logPath;
+        if ($logSaveFileApp) {
+            $this->logSaveFileApp = $logSaveFileApp;
+        }
+
+        $logSystem && $this->logSystem = $logSystem;
+    }
+
+
+    static public function getInstance($logPath='', $logSaveFileApp='', $logSystem = '')
+    {
+        $key = md5('logs');
+
+        if (isset(self::$instance[$key]) && self::$instance[$key] !== null) {
+            return self::$instance[$key];
+        }
+        self::$instance[$key] = new self($logPath, $logSaveFileApp, $logSystem);
+
+        return self::$instance[$key];
+    }
+
+    /**
+     * 格式化日志信息.
+     *
+     * @param mixed $message
+     * @param mixed $level
+     * @param mixed $category
+     * @param mixed $time
+     */
+    public function formatLogMessage($message, $level, $category, $time)
+    {
+        return @date('Y/m/d H:i:s', $time) . "$this->logSystem [$level] [$category] \n $message \n";
+    }
+
+    /**
+     * 日志分类处理.
+     *
+     * @param mixed $message
+     * @param mixed $level
+     * @param mixed $category
+     * @param mixed $flush
+     */
+    public function log($message, $level = 'info', $category = '', $flush = true)
+    {
+        if( defined('QUEUE_RUNLOG') && !LOG_ERROR ){
+            return;
+        }
+
+        if (empty($category)) {
+            $category=$this->logSaveFileApp;
+        }
+        $this->logs[$category][]      = [$message, $level, $category, microtime(true)];
+        $this->logCount++;
+        if ($this->logCount >= self::MAX_LOGS || true == $flush) {
+            $this->flush($category);
+        }
+    }
+
+    /**
+     * 日志分类处理.
+     */
+    public function processLogs()
+    {
+        $logsAll=[];
+        foreach ((array) $this->logs as $key => $logs) {
+            $logsAll[$key] = '';
+            foreach ((array) $logs as $log) {
+                $logsAll[$key] .= $this->formatLogMessage($log[0], $log[1], $log[2], $log[3]);
+            }
+        }
+
+        return $logsAll;
+    }
+
+    /**
+     * 写日志到文件.
+     */
+    public function flush()
+    {
+        if ($this->logCount <= 0) {
+            return false;
+        }
+        $logsAll = $this->processLogs();
+        $this->write($logsAll);
+        $this->logs     = [];
+        $this->logCount = 0;
+    }
+
+    /**
+     * [write 根据日志类型写到不同的日志文件].
+     *
+     * @param $logsAll
+     *
+     * @throws \Exception
+     */
+    public function write($logsAll)
+    {
+        if (empty($logsAll)) {
+            return;
+        }
+        //$this->logPath = ROOT_PATH . 'src/runtime/';
+        if (!is_dir($this->logPath)) {
+            self::mkdir($this->logPath, [], true);
+        }
+        foreach ($logsAll as $key => $value) {
+            if (empty($key)) {
+                continue;
+            }
+            $fileName = $this->logPath . '/' . $key;
+
+            if (($fp = @fopen($fileName, 'a')) === false) {
+                throw new \Exception("Unable to append to log file: {$fileName}");
+            }
+            @flock($fp, LOCK_EX);
+
+            if (@filesize($fileName) > $this->maxFileSize * 1024 * 1024) {
+                $this->rotateFiles($fileName);
+            }
+            @fwrite($fp, $value);
+            @flock($fp, LOCK_UN);
+            @fclose($fp);
+        }
+    }
+
+    /**
+     * Rotates log files.
+     *
+     * @param mixed $file
+     */
+    protected function rotateFiles($file)
+    {
+        for ($i = $this->maxLogFiles; $i >= 0; --$i) {
+            // $i == 0 is the original log file
+            $rotateFile = $file . ($i === 0 ? '' : '.' . $i);
+            //var_dump($rotateFile);
+            if (is_file($rotateFile)) {
+                // suppress errors because it's possible multiple processes enter into this section
+                if ($i === $this->maxLogFiles) {
+                    @unlink($rotateFile);
+                } else {
+                    if ($this->rotateByCopy) {
+                        @copy($rotateFile, $file . '.' . ($i + 1));
+                        if ($fp = @fopen($rotateFile, 'a')) {
+                            @ftruncate($fp, 0);
+                            @fclose($fp);
+                        }
+                    } else {
+                        @rename($rotateFile, $file . '.' . ($i + 1));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Shared environment safe version of mkdir. Supports recursive creation.
+     * For avoidance of umask side-effects chmod is used.
+     *
+     * @param string $dst       path to be created
+     * @param array  $options   newDirMode element used, must contain access bitmask
+     * @param bool   $recursive whether to create directory structure recursive if parent dirs do not exist
+     *
+     * @return bool result of mkdir
+     *
+     * @see mkdir
+     */
+    private static function mkdir($dst, array $options, $recursive)
+    {
+        $prevDir = dirname($dst);
+        if ($recursive && !is_dir($dst) && !is_dir($prevDir)) {
+            self::mkdir(dirname($dst), $options, true);
+        }
+        $mode = isset($options['newDirMode']) ? $options['newDirMode'] : 0777;
+        $res  = mkdir($dst, $mode, $recursive);
+        @chmod($dst, $mode);
+
+        return $res;
+    }
+}

+ 105 - 0
Mall/Framework/Core/RedisQueue.Class.php

@@ -0,0 +1,105 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Cache\Redis;
+
+class RedisQueue
+{
+    public static $prefix = 'queue::';
+    public static $queueName = 'public_queue';
+
+    protected static $_instance;
+
+    /** @var Redis $redis */
+    protected static $redis;
+
+    /**
+     * @param Redis $redis      redis实例
+     * @param string $queueName 队列名称
+     *
+     * @return mixed
+     *
+     * @throws \Exception
+     */
+    public static function getInstance(Redis $redis ,$queueName = 'public_queue')
+    {
+        if ($redis instanceof \Redis) {
+            throw new \Exception('Redis driver not null');
+        }
+
+        self::$redis = $redis;
+        self::setQueueName($queueName);
+
+        $key = md5(self::$queueName);
+        if (!isset(self::$_instance[$key])) {
+            self::$_instance[$key] = new self();
+        }
+
+        return self::$_instance[$key];
+    }
+
+    public static function setQueueName($queueName)
+    {
+        if ($queueName) {
+            self::$queueName = self::$prefix . $queueName;
+        }
+    }
+
+    /**
+     * 添加队列
+     *
+     * @param string $value 存储的队列
+     *
+     * @return bool
+     */
+    public static function push($value)
+    {
+        return self::$redis->push(self::$queueName, $value, 'start');
+    }
+
+    /**
+     * 从队列取出数据
+     *
+     * @param int $number 数量
+     *
+     * @return array
+     */
+    public static function pop($number = 1)
+    {
+        $result = [];
+
+        for ($i = 0; $i < $number; $i++) {
+            if ($value = self::$redis->pop(self::$queueName, 'end')) {
+                $result[] = $value;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 删除队列
+     */
+    public static function flushQueue()
+    {
+        return self::$redis->delete(self::$queueName);
+    }
+
+
+    /**
+     * 返回队列的长度
+     */
+    public function llen()
+    {
+        return self::$redis->llen(self::$queueName);
+    }
+
+    /**
+     * 查看队列的 key 是否存在
+     */
+    public function has()
+    {
+        return self::$redis->has(self::$queueName);
+    }
+}

+ 407 - 0
Mall/Framework/Core/Request.Class.php

@@ -0,0 +1,407 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Request
+{
+    /**
+     *
+     * 存放当前请求的参数
+     * @var array
+     */
+    protected $params;
+
+    static protected $instance;
+
+    private function __construct()
+    {
+    }
+
+    static public function getInstance()
+    {
+        if (is_null(self::$instance)) {
+            self::$instance = new self();
+        }
+
+        return self::$instance;
+    }
+
+    /**
+     * 获取请求的参数集。
+     * 支持http访问的参数 以及 命令行下访问的参数
+     * demo1:php test.php -p3 -t=abc --opt=valopt --opt2 valopt2
+     * demo2 http://test.kimiss.com/index.php?c=xxx&a=ddd
+     *
+     * @param boolean $returnRawJson 是否将json参数作为独立key(rawjson)返回
+     * @throws
+     * @return array
+     */
+    public static function params($returnRawJson = false)
+    {
+        if (self::isCLI()) {
+            return self::cliParams();
+        } else if( strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== FALSE || strpos($_SERVER['CONTENT_TYPE'], 'text/plain') !== FALSE || strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') !== FALSE){
+            $request_body = file_get_contents("php://input");
+            if(!empty($request_body)){
+                $request_body = json_decode($request_body, true);
+                if(!$request_body){
+                    throw new \Exception("参数格式异常");
+                }
+                if($returnRawJson){
+                    return array_merge($_REQUEST, ['rawjson'=> $request_body]);
+                }
+
+                return array_merge($_REQUEST, $request_body);
+            }
+            return $_REQUEST;
+        } else{
+            return $_REQUEST;
+        }
+    }
+
+    /**
+     *
+     * 获取命令行下传递进来的参数
+     * 只支持以 - 或 -- 开头的参数
+     * demo:php test.php -p3 -t=abc --opt=valopt --opt2 valopt2
+     * @return array
+     */
+    private static function cliParams()
+    {
+        $result = array();
+        $params = $GLOBALS['argv'];
+
+        array_shift($params);
+        do {
+            $tmpEachResult = array_shift($params);
+            if (!$tmpEachResult) {
+                break;
+            }
+            $p = $tmpEachResult;
+            if ($p[0] == '-') {
+                $pname = substr($p, 1);
+                $value = false;
+                if ($pname[0] == '-') {// 长选项 (--<param>)
+                    $pname = substr($pname, 1);
+                    if (strpos($p, '=') !== false) {
+                        // value specified inline (--<param>=<value>)
+                        list($pname, $value) = explode('=', substr($p, 2), 2);
+                    }
+                } else {// 短选项
+                    if (strpos($p, '=') !== false) {
+                        // value specified inline (-<param>=<value>)
+                        list($pname, $value) = explode('=', substr($p, 1), 2);
+                    } else if (strlen($p) > 1) {
+                        $pname = substr($p, 1, 1);
+                        $value = substr($p, 2);
+                    }
+                }
+                # 如果上面没有取到值,并且下一个不是以-开头的,则下一个值为当前参数的值
+                $nextparm = current($params);
+                if ($value === false
+                    && $nextparm !== false
+                    && $nextparm[0] != '-'
+                ) {
+                    $value = array_shift($params);
+                }
+                $result[$pname] = (string)$value;// 将 false转为空串,以便与http访问时对参数的处理一致
+            } else {
+                # 不是以-指定开始的参数,一律丢弃
+                //$result[] = $p;
+            }
+        } while (true);
+
+        return $result;
+    }
+
+
+    /**
+     * 分发请求,调用Controller以及对应的action
+     *
+     * @param boolean $enableNginxRwite  是否启用nginx rewrite功能支持pathinfo访问方式
+     * @param boolean $enableModule 是否启用控制器分模块方式
+     * @throws
+     * @return object
+     */
+    public function dispatch($enableNginxRwite = true, $enableModule = false)
+    {
+
+        // 如果未启用nginx rewrite,通过此处简单路由功能匹配
+        if( !$enableNginxRwite && !self::isCLI()){
+            $requestUrl = $_SERVER['REQUEST_URI'];
+            if(strpos($requestUrl, '?') !== false){
+                $requestUrl = substr($requestUrl, 0, strpos($requestUrl, '?'));
+            }
+            if(!empty($requestUrl)){
+                $requestUrl = explode('/', $requestUrl);
+
+                if(!empty($requestUrl)){
+                    if($enableModule){
+                        $_REQUEST['m'] = $requestUrl[1];
+                        $_REQUEST['c'] = $requestUrl[2];
+                        $_REQUEST['a'] = $requestUrl[3];
+                        if(isset($requestUrl[4])){
+                            $_REQUEST['request_id'] = intval($requestUrl[4]);
+                        }
+                    }else{
+                        $_REQUEST['c'] = $requestUrl[1];
+                        $_REQUEST['a'] = $requestUrl[2];
+                        if(isset($requestUrl[3])){
+                            $_REQUEST['request_id'] = intval($requestUrl[3]);
+                        }
+                    }
+                }
+            }
+        }
+
+        self::$instance->params = self::params();
+
+        $controllerName = self::getController($enableModule);
+        if (!class_exists($controllerName)) {
+            throw new \Exception("controller: {$controllerName} not exists!");
+        }
+
+        $action = self::getAction();
+        $controller = new $controllerName();
+        $controller->$action();
+//
+//        if (method_exists($controller, 'display')) {
+//            $controller->display();
+//        }
+    }
+
+    public function getModule()
+    {
+        $m = self::param('m') ?: DEFAULT_CLASS_MODULE_NAME;
+        return ucfirst(trim($m, '_'));
+    }
+
+    public function getModuleType()
+    {
+        $t = self::param('t') ?: '';
+        if ($t) {
+            return ucfirst(trim($t, '_'));
+        }
+    }
+
+    public function getController($enableModule)
+    {
+        $c = self::param('c');
+        $c = trim($c, '_');
+        //默认给一个控制器名
+        if (!$c) {
+            $c = DEFAULT_CLASS_CONTROLLER_NAME;
+        }
+
+        $arr_class_path = array_map(function ($tmpV) {
+            return ucfirst($tmpV);
+        }, explode('_', $c));
+        $c = join('\\', $arr_class_path);
+        $NS = Config::getInstance()->get('app_name');
+        $controller = "{$NS}\\Controller\\";
+        if ($t = self::getModuleType()) {
+            $controller .= $t . "\\";
+        }
+
+        if (($m = self::getModule()) && $enableModule) {
+            $controller .= $m . "\\";
+        }
+        $controller .= "{$c}";
+        return $controller;
+    }
+
+    public function getAction()
+    {
+        $action = self::param('a');
+        //默认给一个方法名
+        if (!$action) {
+            $action = DEFAULT_CLASS_ACTION_NAME;
+        }
+        return $action;
+    }
+
+    /**
+     * 获取指定的参数值
+     * @param $name
+     * @return array|bool|string
+     */
+    public function param($name)
+    {
+        if(empty($this->params)){
+            $this->params = self::params();
+        }
+
+        if (isset($this->params[$name])) {
+            return self::paramFilter($this->params[$name]);
+        }
+
+        return false;
+    }
+
+    /**
+     * 获取raw中json格式参数值
+     */
+    public function getRawJson()
+    {
+        if(empty($this->params['rawjson'])){
+            $this->params = self::params(true);
+        }
+
+        if (isset($this->params['rawjson'])) {
+            return self::paramFilter($this->params['rawjson']);
+        }
+
+        return false;
+    }
+
+    /**
+     * 是否POST请求
+     *
+     * @return boolean
+     */
+    static public function isPost()
+    {
+        return $_SERVER['REQUEST_METHOD'] == 'POST' ? true : false;
+    }
+
+    /**
+     * 是否GET请求
+     *
+     * @return boolean
+     */
+    static public function isGet()
+    {
+        return $_SERVER['REQUEST_METHOD'] == 'GET' ? true : false;
+    }
+
+    /**
+     * 是否处于命令行下
+     *
+     * @return Boolean
+     */
+    static public function isCLI()
+    {
+        if (php_sapi_name() == "cli" || empty($_SERVER['PHP_SELF'])) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 获取客户端真实Ip
+     * @return string
+     */
+    static public function get_onlineip()
+    {
+        $onlineip = '';
+
+        if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
+            $onlineip = getenv('HTTP_CLIENT_IP');
+        } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
+            $onlineip = getenv('HTTP_X_FORWARDED_FOR');
+        } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
+            $onlineip = getenv('REMOTE_ADDR');
+        } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
+            $onlineip = $_SERVER['REMOTE_ADDR'];
+        }
+
+        return $onlineip;
+    }
+
+
+    /**
+     * 获取$_SERVER下面的方法
+     */
+    function getServerParam($param)
+    {
+        if (isset($_SERVER[$param])) {
+            return self::paramFilter($_SERVER[$param]);
+        }
+
+        return '';
+    }
+
+    /**
+     * 全局的接收参数过滤
+     */
+    private function paramFilter($param)
+    {
+        if (is_array($param)) {
+            foreach ($param as $key => $value) {
+                if(is_array($value)){
+                    self::paramFilter($value);
+                    continue;
+                }
+                if(!is_numeric($value)){
+                    $param[$key] = htmlspecialchars(trim($value), ENT_QUOTES, 'UTF-8');
+                }
+            }
+        } else if (!empty($param) && !is_numeric($param)) {
+            $param = htmlspecialchars(trim($param), ENT_QUOTES, 'UTF-8');
+        }
+
+        return $param;
+    }
+
+    /**
+     * 获取上传的文件信息
+     *
+     * @access public
+     * @param string|array $name 名称
+     *
+     * @return null|array| \Mall\Framework\Core\File
+     */
+    public function file($name = '')
+    {
+        $files = isset($_FILES) ? $_FILES : [];
+        if (is_array($name)) {
+            return $files = array_merge($files, $name);
+        }
+        if (!empty($files)) {
+            // 处理上传文件
+            $array = [];
+            foreach ($files as $key => $file) {
+                if (is_array($file['name'])) {
+                    $item = [];
+                    $keys = array_keys($file);
+                    $count = count($file['name']);
+                    for ($i = 0; $i < $count; $i++) {
+                        if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) {
+                            continue;
+                        }
+                        $temp['key'] = $key;
+                        foreach ($keys as $_key) {
+                            $temp[$_key] = $file[$_key][$i];
+                        }
+                        $item[] = (new File($temp['tmp_name']))->setUploadInfo($temp);
+                    }
+                    $array[$key] = $item;
+                } else {
+                    if ($file instanceof File) {
+                        $array[$key] = $file;
+                    } else {
+                        if (empty($file['tmp_name']) || !is_file($file['tmp_name'])) {
+                            continue;
+                        }
+                        $array[$key] = (new File($file['tmp_name']))->setUploadInfo($file);
+                    }
+                }
+            }
+            if (strpos($name, '.')) {
+                list($name, $sub) = explode('.', $name);
+            }
+            if ('' === $name) {
+                // 获取全部文件
+                return $array;
+            } elseif (isset($sub) && isset($array[$name][$sub])) {
+                return $array[$name][$sub];
+            } elseif (isset($array[$name])) {
+                return $array[$name];
+            }
+        }
+
+        return;
+    }
+
+}

+ 93 - 0
Mall/Framework/Core/ResultWrapper.Class.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+/**
+ * 内部结果传递类
+ * 目的:标准化方法执行后返回值的表述。
+ *
+ */
+class ResultWrapper
+{
+    /** @var Boolean $status 存放处理状态 */
+    private $status;
+
+    /** @var mixed $data 存放处理状态 */
+    private $data;
+
+    /** @var int $errorCode 错误编码 */
+    private $errorCode;
+
+    /**
+     * 构造函数私有,这个类不允许从外部实例化
+     */
+    private function __construct()
+    {
+    }
+
+
+    /**
+     * 表示处理成功
+     *
+     * @param mixed $data
+     *
+     * @return ResultWrapper
+     */
+    public static function success($data = null)
+    {
+        $objResultWrapper = new self();
+
+        $objResultWrapper->status = true;
+        $objResultWrapper->data = $data;
+
+        return $objResultWrapper;
+    }
+
+    /**
+     * 表示处理失败
+     *
+     * @param mixed $data
+     *
+     * @return ResultWrapper
+     */
+    public static function fail($data = null, $errorCode)
+    {
+        $objResultWrapper = new self();
+
+        $objResultWrapper->status = false;
+        $objResultWrapper->data = $data;
+        $objResultWrapper->errorCode = $errorCode;
+
+        return $objResultWrapper;
+    }
+
+    /**
+     * 判断处理是否成功
+     *
+     * @return Boolean
+     */
+    public function isSuccess()
+    {
+        return $this->status === true;
+    }
+
+    /**
+     * 获取数据
+     * 
+     * @return mixed
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * 获取错误编码
+     *
+     * @return int
+     */
+    public function getErrorCode()
+    {
+        return $this->errorCode;
+    }
+}

+ 300 - 0
Mall/Framework/Core/Search.Class.php

@@ -0,0 +1,300 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\SearchClient\Client;
+use Mall\Framework\SearchClient\Transport;
+use Exception;
+
+class Search
+{
+    /**
+     * @var array
+     */
+    private $config;
+
+    /**
+     * SearchClient 客户端
+     *
+     * @var Client
+     */
+    private $connection = null;
+
+    /**
+     * @var int
+     */
+    private $serverId;
+
+    /**
+     * @var array
+     */
+    private static $badServerIds = array();
+
+    /**
+     * @var object
+     */
+    private static $_instance;
+
+    /**
+     * 构造函数
+     *
+     * @param $config
+     */
+    public function __construct(array $config)
+    {
+        $this->config = $config;
+    }
+
+    /**
+     * 从服务器组中获取一个可用的ES服务器信息
+     *
+     * @return array
+     * @throws Exception
+     */
+    private function getServer()
+    {
+        $config = $this->config;
+
+        $serverId = null;
+        if (empty(self::$badServerIds)) {
+            $serverId = array_rand($config['servers']);
+        } else {
+            foreach ($config['servers'] as $id => $val) {
+                if (in_array($id, self::$badServerIds)) {
+                    continue;
+                }
+                $serverId = $id;
+                break;
+            }
+        }
+
+        if ($serverId === null) {
+            throw new Exception('服务暂时不可用,请稍后再试!');
+        }
+
+        $this->serverId = $serverId;
+
+        return array(
+            'protocol' => $config['protocol'],
+            'timeout' => $config['timeout'],
+            'index' => $config['index'],
+            'type' => $config['type'],
+            'servers' => $config['servers'][$serverId]
+        );
+    }
+
+    /**
+     * 设置Index索引
+     *
+     * @param $index
+     * @return Search|null
+     */
+    public function setIndex($index)
+    {
+        $this->config['index'] = $index;
+        return is_object($this->getConnection()->setIndex($index)) ? $this : null;
+    }
+
+    /**
+     * 设置Type
+     *
+     * @param $type
+     * @return Search|null
+     */
+    public function setType($type)
+    {
+        $this->config['type'] = $type;
+        return is_object($this->getConnection()->setType($type)) ? $this : null;
+    }
+
+    /**
+     * 创建Index索引
+     *
+     * @param string $index 索引名称
+     * @param array $mappings options
+     * @return mixed
+     */
+    public function createBase($index, $mappings)
+    {
+        return $this->getConnection()->createBase($index, $mappings);
+    }
+
+    /**
+     * 删除Index索引
+     *
+     * @param string $index 索引名称
+     * @return mixed
+     */
+    public function deleteBase($index)
+    {
+        return $this->getConnection()->deleteBase($index);
+    }
+
+    /**
+     * 根据 ID 获取文档
+     *
+     * @param $id
+     * @param bool|false $verbose
+     * @return array
+     */
+    public function get($id, $verbose = false)
+    {
+        $this->checkIndexType();
+
+        return $this->getConnection()->get($id, $verbose);
+    }
+
+    /**
+     * 创建索引
+     *
+     * @param string $document 文档格式
+     * @param string $id 文档ID
+     * @param array $options 自定义选项
+     * @return mixed
+     */
+    public function index($document, $id, $options = array())
+    {
+        $this->checkIndexType();
+
+        $this->log('index', sprintf(
+            'domain:[%s] uri:[%s] contentid:[%s] %s %s',
+            empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'],
+            empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'],
+            $id,
+            var_export($document, true),
+            var_export($options, true)
+        ));
+
+        return $this->getConnection()->index($document, $id, $options);
+    }
+
+    /**
+     * 更新索引
+     *
+     * @param string $document 文档
+     * @param string $id 文档ID
+     * @param array $options 自定义选项
+     * @return mixed
+     */
+    public function update($document, $id, $options = array())
+    {
+        $this->checkIndexType();
+
+        $this->log('update', sprintf(
+            'domain:[%s] uri:[%s] contentid:[%s] %s %s',
+            empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'],
+            empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'],
+            $id,
+            var_export($document, true),
+            var_export($options, true)
+        ));
+
+        return $this->getConnection()->index($document, $id, $options);
+    }
+
+    /**
+     * 删除索引
+     *
+     * @param string $id 索引ID
+     * @return null
+     */
+    public function delete($id)
+    {
+        if (!$id) return null;
+
+        $this->checkIndexType();
+
+        $this->log('delete', sprintf(
+            'domain:[%s] uri:[%s] contentid:[%s]',
+            empty($_SERVER['HTTP_HOST']) ? '' : $_SERVER['HTTP_HOST'],
+            empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI'],
+            $id
+        ));
+
+        return $this->getConnection()->delete($id);
+    }
+
+    /**
+     * 搜索接口
+     *
+     * @param array $query DSL查询格式
+     * @param array $options 选项
+     * @return array
+     * @throws Exception
+     */
+    public function search($query = array(), array $options = array())
+    {
+        try {
+            $client = $this->getConnection()->search($query, $options);
+           
+            return empty($client) ? array() : $client;
+        } catch (Transport\HTTPException $e) {
+            self::$badServerIds[] = $this->serverId;
+            $this->connection = Client::connection($this->getServer());
+            return $this->search($query, $options);
+        }
+    }
+
+    /**
+     * 搜索服务链接
+     *
+     * @return Client
+     * @throws Exception
+     */
+    public function getConnection()
+    {
+        $key = md5(json_encode($this->config));
+
+        if (!isset(self::$_instance[$key]) || !self::$_instance[$key] instanceof Client) {
+            if ($this->connection) {
+                return $this->connection;
+            }
+            $connection = Client::connection($this->getServer());
+            $connection->setIndex($this->config['index']);
+            $connection->setType($this->config['type']);
+
+            self::$_instance[$key] = $this->connection = $connection;
+        }
+
+        return self::$_instance[$key];
+    }
+
+    private function checkIndexType()
+    {
+        $connection = $this->getConnection();
+
+        $index = $connection->getIndex();
+        if (empty($index)) {
+            throw new \RuntimeException('Invalid search index');
+        }
+
+        $type = $connection->getType();
+        if (empty($type)) {
+            throw new \RuntimeException('Invalid search index type');
+        }
+
+        return true;
+    }
+
+    protected function log($type, $message)
+    {
+        if (!defined('DEBUG_MODE') || DEBUG_MODE != 1) {
+            return;
+        }
+
+        $temp_dir = sys_get_temp_dir();
+        if (!is_writable($temp_dir)) {
+            return;
+        }
+
+        $log_file = sprintf('%s/search_%s.log', $temp_dir, $type);
+        $log = sprintf(
+            '%s index[%s] type[%s] %s',
+            date('r'),
+            $this->config['index'],
+            $this->config['type'],
+            $message
+        );
+        file_put_contents($log_file, $log . PHP_EOL, FILE_APPEND);
+    }
+}

+ 148 - 0
Mall/Framework/Core/SendMail.Class.php

@@ -0,0 +1,148 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Mail\Mail;
+
+class SendMail {
+
+    private static $_instance;
+
+    protected $error;
+
+    static public function getInstance()
+    {
+        $key = md5('sendmail');
+
+        if (!self::$_instance[$key] instanceof self) {
+            self::$_instance[$key] = new self;
+        }
+
+        return self::$_instance[$key];
+    }
+
+    public function mail($title, $email, $html, $sign=null)
+    {
+        $key = md5('mail');
+
+        if (!self::$_instance[$key] instanceof self) {
+            $options = Config::getInstance()->get('mail');
+            self::$_instance[$key] = new Mail($options['mailer'], $options['delimiter'], $options['charset'], $options['from'], $options['sign'], $options['smtp_host'], $options['smtp_port'], $options['smtp_auth'], $options['smtp_username'], $options['smtp_password']);
+        }
+
+        self::$_instance[$key]->execute($email, $title, $html);
+        if ($this->error = self::$_instance[$key]->error()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * 邮箱验证码
+     * @param $nickname
+     * @param $code
+     * @return string
+     */
+    public function emailCode($nickname, $code)
+    {
+        return "
+             <div style='width:700px;height:240px;padding:0 30px;color:#444;font-size:15px;padding-top:20px;'>
+                <p style='font-size: 16px;'>
+                    亲爱的{$nickname}:
+                </p>
+                <div style='width:630px;margin-top:20px;color:#444;word-wrap:break-word;'>
+                    <p style='margin-top:20px;'>您的验证码为:{$code}</p>
+                </div>
+            </div>
+        ";
+    }
+
+    /**
+     * 重置密码
+     * @param $nickname
+     * @param $url
+     * @return string
+     */
+    public function userResetPw($nickname, $url)
+    {
+        return "
+             <div style='width:700px;height:240px;padding:0 30px;color:#444;font-size:15px;padding-top:10px;'>
+                <h4 style='font-size: 15px; padding:0; margin:0;'>亲爱的 {$nickname},您好:</h4>
+                <p style='font-size: 14px; margin: 3px 0; padding-left:30px;'>您的账号正在进行找回密码操作!</p>
+                <div style='width:630px;margin-top:20px; padding-left:30px;color:#444;word-wrap:break-word;'>
+                    <p style='text-align: center;width:630px;'>
+                        <a href='" . $url . "' style='margin-left:220px;padding: 6px; color:white;text-decoration: none;width:130px;height:30px;display:block;background: #65A8FF;border-radius:10px;'>
+                            <span style='vertical-align: middle;'>点击重置密码</span>
+                        </a>
+                    </p>
+                    <div style='border-left: 5px solid #007AFF;padding-left:10px; font-size: 14px;'>
+                        <p style='margin:0; padding:0;'>如果您点击上述链接无效,请将下面的链接复制到浏览器地址栏中访问:</p>
+                        <p>{$url}</p>
+                    </div>
+                    <p style='color:grey;margin:0;'>
+                        <span>为保障您的账号安全性,以上链接有效期为 3 天。</span><br/>
+                    </p>
+                </div>
+            </div>
+        ";
+    }
+
+    public function html($content, $sitename, $info)
+    {
+        $logo = $logo = PROJECT_IMG_DOMAIN . "assets/mail/img/logo.png";
+        $copyright = $copyright = "Copyright © 2017 北京猫逛科技有限公司";
+        $sitename = $sitename ?: '北京猫逛科技有限公司';
+
+
+        return
+            "<html xmlns='http://www.w3.org/1999/xhtml'>
+            <head>
+                <meta http-equiv='Content-Type' content='text/html; charset=3DUTF-8'/>
+                <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/>
+                <title>{$sitename}</title>
+            </head>
+            <body style='margin:0;padding:0;'>
+            <table width='900' cellpadding='0' cellspacing='0' border='0'>
+                <tr>
+                    <td style='width:900px;background-color:#f5f3f0;'>
+                        <div style='padding:50px 100px;'>
+                            <div style='width:700px;min-height:600px; _height:600px;background-color:#fff;font-family:微软雅黑;overflow:hidden;'>
+                                <div style='width:600px;height:80px;'>
+                                    <div style='float:left;'>
+                                        <img src='{$logo}' width='245' height='30' style='padding:25px 0 0 30px;' />
+                                    </div>
+                                    <div style='padding:32px 0 0 280px;'>
+                                        <span>这里是占位符</span>
+                                    </div>
+                                </div>
+                                <div style='width:700px;height:200px;padding:0 30px;'>
+                                    <div style='width:640px;height:200px;background-image:url(http://www{$domainSuffix}/assets/mail/img/mail.png);'>
+                                       <div style='width:545px;height:200px;padding-top:70px;margin-left:95px;overflow:hidden;'>
+                                            <span style='padding-left:130px;font-size:32px;color:#fff;font-weight:bold;white-space:nowrap;'>{$sitename}</span>
+            <br />
+            <span style='padding-left:132px;font-size:18px;color:#feebeb;'>{$info}</span>
+            </div>
+            </div>
+            </div>
+            {$content}
+            <div style='width:700px;height:30px;padding-left:500px;margin-top:20px;;color:#444;'>
+                <span>{$sitename}团队</span>
+            </div>
+            </div>
+            <div style='text-align:center;color:#444;font-size:12px;font-family:微软雅黑;margin-top:20px;padding-top:20px;'>
+                {$copyright}
+            </div>
+            </div>
+            </td>
+            </tr>
+            </table>
+            </body>
+            </html>";
+    }
+}

+ 73 - 0
Mall/Framework/Core/SendSms.Class.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Core\ResultWrapper;
+use Mall\Framework\SearchClient\Exception;
+
+class SendSms {
+
+    private static $_instance;
+
+    protected $error;
+
+    static public function getInstance()
+    {
+        $key = md5('sendSms');
+
+        if (!self::$_instance[$key] instanceof self) {
+            self::$_instance[$key] = new self;
+        }
+
+        return self::$_instance[$key];
+    }
+
+
+    /**
+     * 发送短信消息方法
+     * 官方文档地址: https://help.aliyun.com/document_detail/55451.html?spm=5176.doc55288.6.556.rZK9dj
+     * 业务限流       https://help.aliyun.com/knowledge_detail/57710.html?spm=5176.doc55451.6.583.sHFzrz
+     *
+     * @param string $mobile 接收短信的手机号
+     * @param string $signname  短信签名
+     * @param string $templatecode  短信模板编号
+     * @param string $templateparam 短信模板替换变量
+     * @param string $source        发送短信业务来源
+     */
+    public function send($mobile, $signname, $templatecode, $templateparam, $source = '')
+    {
+        $options = Config::getInstance()->get('sms');
+
+        if(empty($options)){
+            throw  new \ErrorException('短信配置错误');
+        }
+
+        $post = [
+            'phonenumbers' => $mobile,
+            'signname' => $signname,
+            'templatecode' => $templatecode,
+            'templateparam' => $templateparam,
+        ];
+        $result = request($options['api_url'], $post);
+        if( $result['httpcode'] == 200 && !empty($result['content'])){
+            $resultContent = json_decode($result['content'],true);
+            if($resultContent['state']){
+                return ResultWrapper::success('发送成功');
+            }else{
+                return ResultWrapper::fail($resultContent['data'], ErrorCode::$apiNotResult);
+            }
+        }else{
+            if(!empty($result['content'])){
+                file_put_contents('/tmp/sendSms.log',date('Y-m-d H:i:s').var_export($result['content'],true).PHP_EOL,FILE_APPEND);
+                return ResultWrapper::fail('请求接口失败,错误原因请留意日志', ErrorCode::$apiNotResult);
+            }else{
+                return ResultWrapper::fail('发送失败,接口未返回消息', ErrorCode::$apiNotResult);
+            }
+        }
+    }
+
+    public function getError()
+    {
+        return $this->error;
+    }
+}

+ 180 - 0
Mall/Framework/Core/Session.Class.php

@@ -0,0 +1,180 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Session\SaveHandler\SaveHandlerInterface;
+use Mall\Framework\Session\SaveHandler\RedisSession;
+
+class Session
+{
+    private static $_instance;
+
+    protected static $defaultOptions = array(
+        'save_path'               => null,
+        'name'                    => null, /* this should be set to a unique value for each application */
+        'save_handler'            => null,
+        //'auto_start'            => null, /* intentionally excluded (see manual) */
+        'gc_probability'          => null,
+        'gc_divisor'              => null,
+        'gc_maxlifetime'          => null,
+        'serialize_handler'       => null,
+        'cookie_lifetime'         => null,
+        'cookie_path'             => null,
+        'cookie_domain'           => null,
+        'cookie_secure'           => null,
+        'cookie_httponly'         => null,
+        'use_cookies'             => null,
+        'use_only_cookies'        => 'on',
+        'referer_check'           => null,
+        'entropy_file'            => null,
+        'entropy_length'          => null,
+        'cache_limiter'           => null,
+        'cache_expire'            => null,
+        'use_trans_sid'           => null,
+        'bug_compat_42'           => null,
+        'bug_compat_warn'         => null,
+        'hash_function'           => null,
+        'hash_bits_per_character' => null
+    );
+
+    static public function getInstance()
+    {
+        $key = 'SessionRedis';
+
+        if (!isset(self::$_instance[$key])) {
+            $obj = new self();
+            $obj->connect();
+
+            self::$_instance[$key] = $obj;
+        }
+
+        return self::$_instance[$key];
+    }
+
+    public function connect()
+    {
+        $saveHandler = new RedisSession();
+
+        $this->registerSaveHandler($saveHandler);
+
+        session_start();
+    }
+
+    public static function setOptions($options)
+    {
+        if (!is_array($options)) {
+            throw new \Exception(sprintf(
+                'Parameter provided to %s must be an array or Traversable',
+                __METHOD__
+            ));
+        }
+
+        // set the options the user has requested to set
+        foreach ($options as $name => $value) {
+
+            $name = strtolower($name);
+
+            // set the ini based values
+            if (array_key_exists($name, self::$defaultOptions)) {
+                ini_set("session.$name", $value);
+            }
+        }
+    }
+
+    public function set($name, $value)
+    {
+        if (!self::sessionExists()) {
+            return FALSE;
+        }
+
+        $_SESSION[$name] = $value;
+    }
+
+    public function get($name)
+    {
+        if (!self::sessionExists()) {
+            return FALSE;
+        }
+
+        if (isset($_SESSION[$name])) {
+
+            return $_SESSION[$name];
+
+        }
+
+        return FALSE;
+    }
+
+    /**
+     * Does a session exist and is it currently active?
+     *
+     * @return bool
+     */
+    public static function sessionExists()
+    {
+        $sid = defined('SID') ? constant('SID') : false;
+        if ($sid !== false && self::getId()) {
+            return true;
+        }
+        if (headers_sent()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Set session ID
+     *
+     * @param  string $id
+     */
+    public static function setId($id)
+    {
+        if (self::sessionExists()) {
+            throw new \Exception (
+                'Session has already been started, to change the session ID call regenerateId()'
+            );
+        }
+
+        session_id($id);
+    }
+
+    /**
+     * Get session ID
+     *
+     * @return string
+     */
+    public static function getId()
+    {
+        return session_id();
+    }
+
+    /**
+     * session destory
+     */
+    public function destory()
+    {
+        if (!self::sessionExists()) {
+            return FALSE;
+        }
+
+        session_destroy();
+    }
+
+    /**
+	 * Register Save Handler with ext/session
+	 *
+	 * @param Mall\Framework\SaveHandler\SaveHandlerInterface $saveHandler
+	 * @return bool
+	 */
+	protected static function registerSaveHandler(SaveHandlerInterface $saveHandler)
+	{
+		return session_set_save_handler(
+			array($saveHandler, 'open'),
+			array($saveHandler, 'close'),
+			array($saveHandler, 'read'),
+			array($saveHandler, 'write'),
+			array($saveHandler, 'destroy'),
+			array($saveHandler, 'gc')
+		);
+	}
+}

+ 1070 - 0
Mall/Framework/Core/SqlHelper.Class.php

@@ -0,0 +1,1070 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+use Mall\Framework\Factory;
+
+abstract class SqlHelper
+{
+    const FETCH_ASSOC = \PDO::FETCH_ASSOC;
+    const FETCH_OBJ = \PDO::FETCH_OBJ;
+
+    /**
+     * 数据库连接
+     *
+     * @var db
+     */
+    protected $db;
+
+    /**
+     * 数据库名称
+     *
+     * @var string
+     */
+    protected $_table = null;
+
+    /**
+     * 数据库查询主键
+     *
+     * @var string
+     */
+    protected $_primary = null;
+
+    /**
+     * 数据表字段
+     *
+     * @var array
+     */
+    protected $_fields = array();
+
+    /**
+     * 只读字段
+     *
+     * @var array
+     */
+    protected $_readonly = array();
+
+    /**
+     * 自动填充字段定义
+     *
+     * @var array
+     */
+    protected $_create_autofill = array();
+
+    /**
+     * 自动更新字段定义
+     *
+     * @var array
+     */
+    protected $_update_autofill = array();
+
+    /**
+     * 自动过滤字段定义
+     *
+     * @var array
+     */
+    protected $_filters_input = array();
+
+    /**
+     * 自动过滤字段定义
+     *
+     * @var array
+     */
+    protected $_filters_output = array();
+
+    /**
+     * 插入时,字段验证定义
+     *
+     * @var array
+     */
+    protected $_validators = array();
+
+    /**
+     * 插入时,准备好的数据
+     *
+     * @var array
+     */
+    protected $_data = array();
+
+    /**
+     * select 查询join where limit 等定义
+     *
+     * @var array
+     */
+    protected $_options = array();
+
+    /**
+     * 数据获取方式
+     *
+     * @var int
+     */
+    protected $_fetch_style = self::FETCH_ASSOC;
+
+    /**
+     * 用户ID
+     *
+     * @var int
+     */
+    public $_userid = null;
+
+    /**
+     * 用户名称
+     *
+     * @var string
+     */
+    public $_username = null;
+
+    /**
+     * 用户组ID
+     *
+     * @var int
+     */
+    public $_groupid = null;
+
+    /**
+     * 用户角色ID
+     *
+     * @var int
+     */
+    public $_roleid = null;
+
+    /**
+     * 错误信息
+     *
+     * @var mixed
+     */
+    public $error = null;
+
+    /**
+     * mysql 操作符
+     * @var array
+     */
+    public static $operator = ['=','!=','>=','<=','like','in','>','<'];
+
+    /**
+     * 初始化数据库,用户定义
+     *
+     */
+    function __construct($serviceDB)
+    {
+        $this->db = Factory::db($serviceDB);
+
+        // 表前缀自动拼接
+        $tablePrefix = $this->db->prefix();
+        if( !empty($tablePrefix) ){
+            $this->_table = $tablePrefix .'_'.$this->_table;
+        }
+    }
+
+    public function __set($name, $value)
+    {
+        $this->_data[$name] = $value;
+    }
+
+    public function __get($name)
+    {
+        return isset($this->_data[$name]) ? $this->_data[$name] : null;
+    }
+
+    public function __isset($name)
+    {
+        return isset($this->_data[$name]);
+    }
+
+    public function __unset($name)
+    {
+        unset($this->_data[$name]);
+    }
+
+    public function __call($method, $args)
+    {
+        if (in_array($method, array('field', 'where', 'order', 'limit', 'offset', 'having', 'group', 'distinct', 'data'), true)) {
+            $this->_options[$method] = $args[0];
+            return $this;
+        } elseif (in_array($method, array('sum', 'min', 'max', 'avg'), true)) {
+            $field = isset($args[0]) ? $args[0] : '*';
+            return $this->get_field($method . '(' . $field . ') AS `count`');
+        } elseif (preg_match("/^(get|gets|delete)_by_(.*)$/", $method, $matches)) {
+            $field = $matches[2];
+            if (in_array($field, $this->_fields, true)) {
+                array_unshift($args, $field);
+                return call_user_func_array(array($this, $matches[1] . '_by'), $args);
+            }
+        }
+        return;
+    }
+
+    /**
+     * 设置查询读取模式
+     *
+     * @param int $style
+     */
+    public function set_fetch_style($style)
+    {
+        $this->_fetch_style = $style;
+    }
+
+    /**
+     * 查询数据
+     *
+     * @param string|array $where
+     * @param string|array $fields
+     * @param string|array $order
+     * @param int $limit
+     * @param int $offset
+     * @param array $data
+     * @param boolean $multiple
+     * @return array|boolean
+     */
+    function select($where = null, $fields = '*', $order = null, $limit = null, $offset = null, $data = array(), $multiple = true, $isExport = false , $join = null)
+    {
+        $having = $group = NULL;
+        if (!empty($this->_options)) {
+            $fields = isset($this->_options['field']) ? $this->_options['field'] : $fields;
+            $fields = isset($this->_options['distinct']) ? "distinct " . $this->_options['distinct'] : $fields;
+            $where = isset($this->_options['where']) ? $this->_options['where'] : $where;
+            $having = isset($this->_options['having']) ? $this->_options['having'] : null;
+            $order = isset($this->_options['order']) ? $this->_options['order'] : $order;
+            $group = isset($this->_options['group']) ? $this->_options['group'] : null;
+            $limit = isset($this->_options['limit']) ? $this->_options['limit'] : $limit;
+            $offset = isset($this->_options['offset']) ? $this->_options['offset'] : $offset;
+            $this->_options = array();
+        }
+        if (is_array($fields)) $fields = '`' . implode('`,`', $fields) . '`';
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+        if (!$this->_before_select($where)) return false;
+        if ($join){
+            $sql = "SELECT $fields FROM `$this->_table` as a ".$join;
+        }else{
+            $sql = "SELECT $fields FROM `$this->_table` ";
+        }
+
+        if ($where && is_string($where)) $sql .= " WHERE $where ";
+        if ($order) $sql .= " ORDER BY $order ";
+        if ($group) $sql .= " GROUP BY $group ";
+        if ($having) $sql .= " HAVING $having ";
+        if (is_null($limit) && !$multiple) $sql .= " LIMIT 1 ";
+        $method = $multiple ? 'select' : 'get';
+        if($isExport){
+            $method = 'exportSelect';
+        }
+//         var_dump($sql);
+        $result = is_null($limit)
+            ? $this->db->$method($sql, $data, $this->_fetch_style)
+            : $this->db->limit($sql, $limit, $offset, $data, $this->_fetch_style);
+        if ($result === false || empty($result)) {
+            if ($this->db->error() && $this->db->errno() != '00000') {
+                $this->error = $this->db->error();
+                return false;
+            }
+
+            return [];
+        } else {
+            $this->_data = $result;
+            $this->_after_select($result, $multiple);
+            return $result;
+        }
+    }
+
+    /**
+     * 查询前执行
+     *
+     * @param array|string $where
+     * @return boolean
+     */
+    protected function _before_select(&$where)
+    {
+        return true;
+    }
+
+    /**
+     * 查询后执行
+     *
+     * @param array $result 查询后的结果
+     * @param boolean $multiple 是否多条数据
+     */
+    protected function _after_select(&$result, $multiple = true)
+    {
+    }
+
+    /**
+     * 分页查询
+     *
+     * @param array|string $where
+     * @param array|string $fields
+     * @param string $order
+     * @param int $page
+     * @param int $size
+     * @param array $data
+     * @return array
+     */
+    public function page($where = null, $fields = '*', $order = null, $page = 1, $size = 20, $data = array())
+    {
+        $offset = ($page - 1) * $size;
+        return $this->select($where, $fields, $order, $size, $offset, $data, true);
+    }
+
+    /**
+     * 获得单条数据
+     *
+     * @param array|string $where
+     * @param array|string $fields
+     * @param string $order
+     * @return array
+     */
+    public function get($where = null, $fields = '*', $order = null)
+    {
+        return $this->select($where, $fields, $order, null, null, array(), false);
+    }
+
+    /**
+     * 根据字段值获得单条数据
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param array|string $fields
+     * @param string $order
+     * @return array
+     */
+    public function get_by($field, $value, $fields = '*', $order = null)
+    {
+        return $this->select("`$field`=?", $fields, $order, null, null, array($value), false);
+    }
+
+    /**
+     * 根据字段值查询多条数据
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param array|string $fields
+     * @param string $order
+     * @param int $limit
+     * @param int $offset
+     * @return array
+     */
+    public function gets_by($field, $value, $fields = '*', $order = null, $limit = null, $offset = 0)
+    {
+        return $this->select("`$field`=?", $fields, $order, $limit, $offset, array($value), true);
+    }
+
+    /**
+     * 根据字段值查询多条数据
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param array|string $fields
+     * @param string $order
+     * @param int $limit
+     * @param int $offset
+     * @return object
+     */
+    public function exportSelect($where = null, $fields = '*', $order = null, $limit = null, $value = [], $offset = 0)
+    {
+        return $this->select($where, $fields, $order, $limit, $offset, $value, true, true);
+    }
+
+
+    /**
+     * 获取单条字段值
+     *
+     * @param string $field
+     * @param array|string $where
+     * @param array $data
+     * @return mixed
+     */
+    public function get_field($field, $where = null, $data = array())
+    {
+        $r = $this->select($where, $field, null, null, null, $data, false);
+        if(empty($r)){
+            return $r;
+        }
+        return array_shift($r);
+    }
+
+    /**
+     * 获取字段集合
+     *
+     * @param string $field
+     * @param array|string $where
+     * @param array $data
+     * @return array
+     */
+    public function gets_field($field, $where = null, $data = array())
+    {
+        $result = array();
+        $data = $this->select($where, $field, null, null, null, $data, true);
+        foreach ($data as $r) {
+            $result[] = array_shift($r);
+        }
+        return $result;
+    }
+
+    /**
+     * 获取总数
+     *
+     * @param array|string $where
+     * @param array $data
+     * @return int
+     */
+    public function count($where = null, $data = array(), $join = null)
+    {
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+        if (!empty($where)) $where = " WHERE $where";
+        if ($join){
+            $sql = "SELECT count(*) as `count` FROM `$this->_table` as a $join $where";
+        }else{
+            $sql = "SELECT count(*) as `count` FROM `$this->_table` $where";
+        }
+//        var_dump($sql);
+        $r = $this->db->get($sql, $data);
+        return $r ? $r['count'] : false;
+    }
+
+    /**
+     * 获取主健定义
+     *
+     * @return string
+     */
+    public function primary()
+    {
+        return isset($this->_primary) ? $this->_primary : $this->db->get_primary($this->_table);
+    }
+
+    /**
+     * 是否存在符合条件的记录
+     *
+     * @param array|string $field
+     * @param mixed $value
+     * @return boolean
+     */
+    public function exists($field, $value)
+    {
+        return $this->db->get("SELECT `$field` FROM `$this->_table` WHERE `$field`=?", array($value)) ? true : false;
+    }
+
+    /**
+     * 插入数据
+     *
+     * @param array $data
+     * @param boolean $multiple 是否批量插入
+     * @return int|boolean
+     */
+    protected function insert($data = array(), $multiple = false)
+    {
+        $this->_data($data);
+        if (!$this->_before_insert($data)) return false;
+
+        $this->_create_autofill($data, $multiple);
+
+        if($multiple){
+            foreach ($data as $key => $value){
+                $insertData[] = array_values($value);
+            }
+            $id = $this->db->insert("INSERT INTO `$this->_table` (`" . implode('`,`', array_keys($data[0])) . "`) VALUES(" . implode(',', array_fill(0, count($data[0]), '?')) . ")", $insertData, $multiple);
+        }else{
+            $id = $this->db->insert("INSERT INTO `$this->_table` (`" . implode('`,`', array_keys($data)) . "`) VALUES(" . implode(',', array_fill(0, count($data), '?')) . ")", array_values($data), $multiple);
+        }
+        if ($id === false) {
+            $this->error = $this->db->error();
+            return $this->error;
+        } else {
+            $this->_after_insert($data);
+            return $id;
+        }
+    }
+
+
+    /**
+     * 替换方式插入数据
+     *
+     * @param array $data
+     * @return int|boolean
+     */
+    protected function replace($data = array(), $multiple = false)
+    {
+        $this->_data($data);
+
+        if (!$this->_before_insert($data)) return false;
+
+        $this->_create_autofill($data, $multiple);
+
+        if($multiple){
+            foreach ($data as $key => $value){
+                $insertData[] = array_values($value);
+            }
+
+            $id = $this->db->insert("REPLACE INTO `$this->_table` (`" . implode('`,`', array_keys($data[0])) . "`) VALUES(" . implode(',', array_fill(0, count($data[0]), '?')) . ")", $insertData, $multiple);
+        }else{
+            $id = $this->db->insert("REPLACE INTO `$this->_table` (`" . implode('`,`', array_keys($data)) . "`) VALUES(" . implode(',', array_fill(0, count($data), '?')) . ")", array_values($data));
+        }
+
+        if ($id === false) {
+            $this->error = $this->db->error();
+            return false;
+        } else {
+            $this->_after_insert($data);
+            return $id;
+        }
+    }
+
+    /**
+     * 插入之前执行
+     *
+     * @param array $data
+     * @return boolean
+     */
+    protected function _before_insert(&$data)
+    {
+        return true;
+    }
+
+    /**
+     * 插入之后执行
+     *
+     * @param array $data
+     */
+    protected function _after_insert(&$data)
+    {
+    }
+
+    /**
+     * 根据ID拷贝一份
+     *
+     * @param int $id
+     * @param array $data
+     * @return int/boolean
+     */
+    public function copy_by_id($id, $data = array())
+    {
+        $r = $this->db->get("SELECT * FROM `$this->_table` WHERE `$this->_primary`=?", array($id));
+        if (!$r) return false;
+        unset($r[$this->_primary]);
+        if ($data) $r = array_merge($r, $data);
+        return $this->insert($r);
+    }
+
+    /**
+     * 更新
+     *
+     * @param array $data
+     * @param array /string $where
+     * @param int $limit
+     * @param array /string $order
+     * @return int/boolean
+     */
+    protected function update($data = array(), $where = null, $limit = null, $order = null)
+    {
+
+        if (!empty($this->_options)) {
+            $where = isset($this->_options['where']) ? $this->_options['where'] : $where;
+            $order = isset($this->_options['order']) ? $this->_options['order'] : $order;
+            $limit = isset($this->_options['limit']) ? $this->_options['limit'] : $limit;
+            $offset = isset($this->_options['offset']) ? $this->_options['offset'] : $offset;
+            $this->_options = array();
+        }
+
+        $this->_data($data);
+
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+
+        if (!$this->_before_update($data, $where)) return false;
+
+        $this->_update_autofill($data);
+
+        $this->_readonly($data);
+
+
+        $sql = "UPDATE `$this->_table` SET `" . implode('`=?,`', array_keys($data)) . "`=?";
+        if ($where) $sql .= " WHERE $where ";
+        if ($order) $sql .= " ORDER BY $order ";
+        if ($limit) $sql .= " LIMIT $limit ";
+        
+        $result = $this->db->update($sql, array_values($data));
+
+        if ($result === FALSE) {
+            $this->error = $this->db->error();
+            return false;
+        } else {
+            $this->_after_update($data, $where);
+            return $result;
+        }
+    }
+
+    /**
+ * query方法
+ */
+    protected function query($sql)
+    {
+        if(!empty($sql)){
+            $result = $this->db->query($sql);
+            if($result === false){
+                $this->error = $this->db->error();
+                return false;
+            }else{
+                return $result;
+            }
+        }else{
+            $this->error = 'sql is null';
+            return false;
+        }
+    }
+
+    /**
+     * query方法
+     */
+    protected function exportQuery($sql)
+    {
+        if(!empty($sql)){
+            $result = $this->db->exportQuery($sql);
+            if($result === false){
+                $this->error = $this->db->error();
+                return false;
+            }else{
+                return $result;
+            }
+        }else{
+            $this->error = 'sql is null';
+            return false;
+        }
+    }
+
+    /**
+     * 更新记录之前执行
+     *
+     * @param array $data
+     * @param array|string $where
+     * @return boolean
+     */
+    protected function _before_update(&$data, $where)
+    {
+        return true;
+    }
+
+    /**
+     * 更新记录之后执行
+     *
+     * @param array $data
+     * @param array|string $where
+     */
+    protected function _after_update(&$data, $where)
+    {
+    }
+
+    /**
+     * 更新一个字段值
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param string /array $where
+     * @return int/boolean
+     */
+    public function set_field($field, $value, $where = null)
+    {
+        return $this->update(array($field => $value), $where);
+    }
+
+    /**
+     * 递增一个int字段值
+     *
+     * @param string $field
+     * @param string /array $where
+     * @param int $step
+     * @param array $data
+     * @return int/boolean
+     */
+    public function set_inc($field, $where = null, $step = 1, $data = array())
+    {
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+        if(empty($step)){
+            $step = 0;
+        }
+        $sql = "UPDATE " .$this->_table." SET `$field`=IFNULL(`$field`,0)+".$step." WHERE $where";
+        return $this->db->update($sql, $data);
+    }
+
+    /**
+     * 递减一个int字段值
+     *
+     * @param string $field
+     * @param array /string $where
+     * @param int $step
+     * @param array $data
+     * @return int/boolean
+     */
+    public function set_dec($field, $where = null, $step = 1, $data = array())
+    {
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+        if(empty($step)){
+            $step = 0;
+        }
+        $sql = "UPDATE ".$this->_table." SET `$field`=IFNULL(`$field`,0)-".$step." WHERE $where";
+        return $this->db->update($sql, $data);
+    }
+
+    /**
+     * 删除
+     *
+     * @param array /string $where
+     * @param int $limit
+     * @param array /string $order
+     * @param array $data
+     * @return int/boolean
+     */
+    protected function delete($where = null, $limit = null, $order = null, $data = array())
+    {
+        if (!empty($this->_options)) {
+            $where = isset($this->_options['where']) ? $this->_options['where'] : $where;
+            $order = isset($this->_options['order']) ? $this->_options['order'] : $order;
+            $limit = isset($this->_options['limit']) ? $this->_options['limit'] : $limit;
+            $offset = isset($this->_options['offset']) ? $this->_options['offset'] : $offset;
+            $this->_options = array();
+        }
+
+        $where = $this->_where($where);
+        if($where === false){
+            return false;
+        }
+
+        if (!$this->_before_delete($where)) return false;
+
+        $sql = "DELETE FROM `$this->_table`";
+        $asql = $sql;
+        if ($where) $sql .= " WHERE $where ";
+        if ($limit) {
+            if ($order) $sql .= " ORDER BY $order ";
+            $sql .= " LIMIT $limit ";
+        }
+        if ($sql == $asql) {
+            return false;
+        }
+
+        $result = $this->db->delete($sql, $data);
+        if ($result === FALSE) {
+            $this->error = $this->db->error();
+            return false;
+        } else {
+            $this->_after_delete($where);
+            return $result;
+        }
+    }
+
+    /**
+     * 删除前执行
+     *
+     * @param array|string $where
+     * @return boolean
+     */
+    protected function _before_delete(&$where)
+    {
+        return true;
+    }
+
+    /**
+     * 删除后执行
+     *
+     * @param array|string $where
+     */
+    protected function _after_delete(&$where)
+    {
+    }
+
+    /**
+     * 根据字段值查询
+     *
+     * @param string $field
+     * @param mixed $value
+     * @param int $limit
+     * @param array|string $order
+     * @return int|boolean
+     */
+    public function delete_by($field, $value, $limit = null, $order = null)
+    {
+        return $this->delete("`$field`=?", $limit, $order, array($value));
+    }
+
+
+    /**
+     * 获得一个字段的类型
+     *
+     * @param string $field
+     * @return string
+     */
+    protected function _fieldtype($field)
+    {
+
+        static $fields;
+        if (is_null($fields) || !isset($fields[$this->_table])) {
+            if ($data = $this->db->list_fields($this->_table)) {
+                foreach ($data as $k => $v) {
+                    $fields[$v['Field']] = $v;
+                }
+            }
+        }
+        return isset($fields[$field]) && isset($fields[$field]['Type']) ? $fields[$field]['Type'] : '';
+
+    }
+
+
+    private function _create_autofill(& $data, $multiple = false)
+    {
+        if (empty($this->_create_autofill)) return true;
+        if($multiple){
+            foreach ($this->_create_autofill as $field => $val) {
+                foreach ($data as $key => $value){
+                    if (!isset($data[$key][$field])) $data[$key][$field] = $val;
+                }
+            }
+        }else{
+            foreach ($this->_create_autofill as $field => $val) {
+                if (!isset($data[$field])) $data[$field] = $val;
+            }
+        }
+    }
+
+    private function _update_autofill(& $data)
+    {
+        if (empty($this->_update_autofill)) return true;
+        foreach ($this->_update_autofill as $field => $val) {
+            if (!isset($data[$field])) $data[$field] = $val;
+        }
+    }
+
+    private function _readonly(& $data)
+    {
+        if (empty($this->_readonly)) return true;
+        foreach ($this->_readonly as $field => $val) {
+            if (isset($data[$field])) unset($data[$field]);
+        }
+    }
+
+    private function checkVaule($where){
+        if(is_string($where)){
+            return addslashes($where);
+        }
+        return $where;
+    }
+
+
+    private function _where(& $where)
+    {
+        if (empty($where) && isset($this->_data[$this->_primary])) $where = $this->_data[$this->_primary];
+        if (empty($where)) return null;
+
+        if (is_numeric($where)) {
+            $where = "`$this->_primary`=$where";
+        } elseif (is_array($where)) {
+            $condition = array();
+            foreach ($where as $key => $value){
+                if($key === 0 && is_numeric($value)){
+                    $where = array_map(array(__CLASS__,'checkVaule'), $where);
+                    $ids = is_numeric($where[0]) ? implode(',', $where) : "'" . implode("','", $where) . "'";
+                    $where = "`$this->_primary` IN($ids)";
+                    break;
+                }else{
+                    if(is_numeric($key) && count($value) == 3){
+//                        if(!in_array($value[0],$this->_fields)){
+//                            $this->error = $value[0].'字段未被映射';
+//                            return false;
+//                        }
+
+                        if(!in_array($value[1], self::$operator)){
+                            $this->error = $value[1].'查询不支持';
+                            return false;
+                        }
+
+                        if(is_array($value[2])){
+                            $sqlIn = '';
+                            $len = count($value[2]);
+                            foreach ($value[2] as $k => $v){
+                                if(!is_numeric($v)){
+                                    $sqlIn .="'".$v."'";
+                                }else{
+                                    $sqlIn .= $v;
+                                }
+
+                                if($len >1 && $k+1 != $len){
+                                    $sqlIn .= ',';
+                                }
+                            }
+                            $condition[] = "`$value[0]` $value[1] (".$sqlIn.")";
+                        }else{
+                            if (strstr($value[0], '|')){
+                                $str = explode('|', $value[0]);
+                                $condition[] = "$str[0] $value[1] '".$this->checkVaule($value[2])."' OR "."$str[1] $value[1] '".$this->checkVaule($value[2])."'";
+                            }else{
+                                if ($value[1] == 'in'){
+                                    $condition[] = "$value[0] $value[1] (".$this->checkVaule($value[2]).")";
+                                }else{
+                                    $condition[] = "$value[0] $value[1] '".$this->checkVaule($value[2])."'";
+                                }
+                            }
+                        }
+                        $condition;
+                    }else{
+                        $where = array_map(array(__CLASS__,'checkVaule'), $where);
+//                        if (in_array($key, $this->_fields)) {
+                            if(is_array($value)){
+                                $sqlIn = '';
+                                $len = count($value);
+                                foreach ($value as $k => $v){
+                                    if(!is_numeric($v)){
+                                        $sqlIn .="'".$v."'";
+                                    }else{
+                                        $sqlIn .= $v;
+                                    }
+
+                                    if($len >1 && $k+1 != $len){
+                                        $sqlIn .= ',';
+                                    }
+                                }
+                                $condition[] = "$key in(".$sqlIn.")";
+                            }else{
+                                if (strstr($key, '|')){
+                                    $str = explode('|', $key);
+                                    $condition[] =  "$str[0] ='$value'"."' OR "."$str[0] ='$value'";
+                                }else{
+                                    $condition[] = "$key ='$value'";
+                                }
+                            }
+//                        }else{
+//                            $this->error = $key.'字段未被映射';
+//                            return false;
+//                        }
+                    }
+                }
+            }
+            if(!empty($condition)){
+                $where = implode(' AND ', $condition);
+            }
+        } elseif (preg_match("/^[0-9a-z\'\"\,\s]+$/i", $where)) {
+            $where = strpos($where, ',') === false ? "`$this->_primary`='$where'" : "`$this->_primary` IN($where)";
+        }
+
+        $notice_flags = array('select', 'delete', 'insert', 'drop', 'update', 'benchmark', 'database');
+        $quote = strpos($where, '"') === true ? '"' : "'";
+        foreach ($notice_flags as $w) {
+            $where = preg_replace('#([\s\(\)\!])' . $w . '([\s\(\)\*])#i', "$1" . substr($w, 0, 1) . $quote . '/**/' . $quote . substr($w, 1) . "$2", $where);
+        }
+        return $where;
+    }
+
+    private function _data(& $data)
+    {
+        if (empty($data)) {
+            if (!empty($this->_options['data'])) {
+                $data = $this->_options['data'];
+            } elseif (!empty($this->_data)) {
+                $data = $this->_data;
+            } elseif (!empty($_POST)) {
+                $data = $_POST;
+            }
+        }
+    }
+
+    /**
+     * 过滤不需要的字段
+     *
+     * @param array $data 输入数据
+     * @param array $keys 需要的字段定义
+     * @return array
+     */
+    protected function filter_array($data, $keys)
+    {
+        // 合并附加字段
+        $fields = $this->_get_extra_fileds();
+        if ($fields) {
+            $fields = array_filter(array_keys($fields));
+            if ($fields) {
+                $keys = array_unique(array_merge($keys, $fields));
+            }
+        }
+
+        foreach ($data as $field => $v) {
+            if (!in_array($field, $keys)) unset($data[$field]);
+        }
+        return $data;
+    }
+
+    /**
+     * 获取模型操作中的错误,并返回字符串类型的错误
+     *
+     * @return string
+     */
+    public function error()
+    {
+        $error = $this->error;
+        if (empty($error)) {
+            $error = $this->db->error();
+        }
+        if (empty($error)) {
+            return '';
+        } else {
+            if (is_array($error)) {
+                $error = implode("*****", $error);
+            }
+            return $error;
+        }
+    }
+
+    /**
+     * 切换分表
+     * @param $tablename
+     */
+    public function get_Table()
+    {
+       return $this->_table;
+    }
+
+
+    /**
+     * 切换分表
+     * @param $tablename
+     */
+    public function set_Table($tablename)
+    {
+        $this->_table = $tablename;
+    }
+
+    /**
+     * 开启事务
+     * @param bool $foreign_key_checks
+     * @return mixed
+     */
+    public function beginTransaction($foreign_key_checks = false)
+    {
+        return $this->db->beginTransaction($foreign_key_checks)?true:false;
+    }
+
+    /**
+     * 提交事务
+     * @return mixed
+     */
+    public function commit()
+    {
+        return $this->db->commit();
+    }
+
+    /**
+     * 回滚事务
+     * @return mixed
+     */
+    public function rollBack()
+    {
+        return $this->db->rollBack();
+    }
+}

+ 710 - 0
Mall/Framework/Core/StatusCode.Class.php

@@ -0,0 +1,710 @@
+<?php
+/**
+ * 统一的内容状态码
+ * 原则上按照数字顺序申请,可以提前占位,体现分组
+ * !!!不允许出现重复状态码!!!
+ */
+
+namespace Mall\Framework\Core;
+
+class StatusCode
+{
+    /**
+     * 公用模块状态码
+     *
+     * 数值范围 1-100
+     */
+
+    /** @var int $partion 部分 */
+    public static $partion = 3;
+
+    /** @var int $delete 删除 */
+    public static $delete = 4;
+
+    /** @var int $standard 正常 */
+    public static $standard = 5;
+
+    /** @var int $offline 下线 */
+    public static $offline = 6;
+
+    /**
+     * XX模块状态码
+     *
+     * 数值范围 101-200
+     */
+
+
+    /**
+     * XX模块状态码
+     *
+     * 数值范围 201-300
+     */
+
+    /**
+     * 系统设置类型type
+     */
+    public static $settingType = [
+        'applets' => 1,  // 小程序设置
+        'message' => 2,  // 模板消息设置
+        'class'   => 3,  // 分类设置
+    ];
+
+    public static $adminSettingType = [
+        'pay'         => 1,  // 支付
+        'delivery'    => 2,  // 配送
+        'financeType' => 3,  // 财务类型
+        'unit'        => 4,  //属性
+        'printTemplate'    => 5,//打印模版,
+        'role'        => 6,//角色
+        'process'    => 7,//流程,
+        'notice'    => 8,//语音提示
+    ];
+
+    public static $systemFinanceType = [
+        'saleReturnReceipt'     => 1,//销售退货退款
+        'saleReceipt'           => 2,//销售收款
+        'purchaseReturnReceipt' => 3,//采购退货收款
+        'purchasePrepaid'       => 4,//采购预付
+        'purchaseReceipt'       => 5,//采购付款
+        'depositsReceived'      => 6,//预存收款
+        'expenseSingleIn'       => 7,//费用单收款
+        'expenseSingleOut'      => 8,//费用单支出
+        1      => '销售退货退款',//费用单支出
+        2      => '销售收款',//费用单支出
+        3      => '采购退货收款',//费用单支出
+        4      => '采购预付',//费用单支出
+        5      => '采购付款',//费用单支出
+        6      => '预存收款',//费用单支出
+        7      => '费用单收款',//费用单支出
+        8      => '费用单支出',//费用单支出
+    ];
+
+    public static $messageType = [
+        'inventoryNull' => 1,//库存不足消息
+    ];
+
+
+    /**
+     * 通用审核状态
+     */
+    public static $auditStatus = [
+        'completion'   => 0, //待补全
+        'auditing'     => 1,  // 待审
+        'auditPass'    => 2,  // 审核通过
+        'auditNotPass' => 3, //审核未通过
+        'auditIng'     => 4, // 审核中
+        0              => '待补全',
+        1              => '待审核',
+        2              => '审核通过',
+        3              => '审核未通过',
+        4              => '审核中',
+    ];
+
+    /**
+     * 分销层级
+     */
+    public static $level = [
+        'off' => 0, //分销开关
+        'one' => 1, //第一级
+        'two' => 2, //第二级
+        'three' => 3, //第三级
+    ];
+
+    public static $subConditions = [
+        'oneCheck' => 1, //首次点击链接
+        'oneOrder' => 2, //首次下单
+        'onePay' => 3, //首次付款
+];
+
+
+    /** @var array $userActionID 用户操作行为 */
+    public static $userActionID = [
+        'favour'      => 1,// 点赞
+        'tread'       => 2, // 踩--不喜欢
+        'report'      => 3, // 举报
+        'fllow'       => 4, // 关注
+        'avorites'    => 5, //收藏
+        'notAvorites' => 6, // 取消收藏
+        'notFollow'   => 7,  //取消关注
+        'notTread'    => 8,//取消踩--不喜欢
+        'notFavour'   => 9, //取消点赞
+    ];
+
+    /** @var array $type 价格类型 */
+    public static $priceType = [
+        'salePrice' => 1,//销售价
+        'costPrice' => 2,// 成本价
+        'qtPrice'   => 3,// 其他价
+    ];
+    /** @var array $type 运费类型 */
+    public static $freightType = [
+        'enterCharge' => 1,//销售价
+        'pourCharge'  => 2,// 成本价
+    ];
+
+    /** @var array $userActionObj 用户操作对象  此处标识修改需谨慎多处关联 */
+    public static $userActionObj = [
+        /** @var int $actionContent 用户操作对象-文章 */
+        'actionContent'        => 1,
+        /** @var int $actionUser 用户操作对象-用户 */
+        'actionUser'           => 2,
+        /** @var int $actionComment 用户操作对象-评论 */
+        'actionComment'        => 3,
+        /** @var int $actionMall 用户操作对象-商场 -店铺 */
+        'actionMallStore'      => 4,
+        /** @var int $actionMall 用户操作对象-商场 -商品 */
+        'actionMallShop'       => 5,
+        /** @var int $actionSea 用户操作对象-海淘--店铺 */
+        'actionSeaStore'       => 6,
+        /** @var int $actionSea 用户操作对象-海淘--商品 */
+        'actionSeaShop'        => 7,
+        /** @var int $actionMall 用户操作对象-商场 */
+        'actionMall'           => 8,
+        /** @var int $actionMall 用户操作对象-旅游景点 */
+        'actionScenic'         => 9,
+        /** @var int $actionPromotion 用户操作对象-消费打折信息 */
+        'actionPromotion'      => 10,
+
+
+        /** @var int $actionArVocalConcert 用户操作对象-休闲--演唱会a */
+        'actionArVocalConcert' => 24,
+        /** @var int $actionArTheatre 用户操作对象-休闲--剧场a */
+        'actionArTheatre'      => 25,
+        /** @var int $actionArTusicale 用户操作对象-休闲--音乐会a */
+        'actionArMusicale'     => 26,
+        /** @var int $actionArCompete 用户操作对象-休闲--比赛a */
+        'actionArCompete'      => 27,
+        /** @var int $actionArVariety 用户操作对象-休闲--综艺a */
+        'actionArVariety'      => 28,
+        /** @var int $actionArder 用户操作对象-休闲--博物馆p */
+        'actionArMuseum'       => 29,
+        /** @var int $actionArSport 用户操作对象-休闲--健身p */
+        'actionArSport'        => 30,
+        /** @var int $actionArCoffee 用户操作对象-休闲--咖啡p */
+        'actionArCoffee'       => 31,
+        /** @var int $actionMall 用户操作对象-其他 */
+        'actionArOther'        => 32,
+        /** @var int $actionArCoffee 用户操作对象-休闲--下午茶p */
+        'actionArTea'          => 33,
+        /** @var int $actionArCoffee 用户操作对象-休闲--酒吧p */
+        'actionArWine'         => 34,
+        /** @var int $actionArCoffee 用户操作对象-休闲--足疗p */
+        'actionArFoot'         => 35,
+        /** @var int $actionArCoffee 用户操作对象-休闲--轰趴p */
+        'actionArParty'        => 36,
+        /** @var int $actionArCoffee 用户操作对象-休闲--瑜伽p */
+        'actionArYoga'         => 37,
+        /** @var int $actionArCoffee 用户操作对象-休闲--舞蹈p */
+        'actionArDance'        => 38,
+    ];
+
+    /**
+     * 成本计算标识
+     */
+    public static $costType = [
+        'mwa' => 1,//移动加权平均法
+        'sp'  => 2,//个别计价法
+    ];
+
+    /**
+     * 成本标识名称
+     */
+    public static $costTypeTitle = [
+        1 => '移动加权平均法',
+        2 => '个别计价法',
+    ];
+    /**
+     * 采购订单 状态码
+     * @var array
+     *  1:创建2:审核中3:已审核4:重新审核
+     */
+    public static $purchaseOrder = [
+        'create'   => 1,
+        'auditing' => 2,
+        'audited'  => 3,
+        'reAudite' => 4
+    ];
+
+    /**
+     * 销售出库是否已出库状态
+     * @var array
+     */
+    public static $outWarehouseStatus = [
+        'shopPut'    => 1, //未出库商品推送出库单,状态未出库
+        'erpOut'     => 2, //已出库 erp自己创建的销售出库
+        'shopPutOut' => 2, //已出库 商城推的订单
+    ];
+
+    /**
+     * 销售订单来源
+     * @var array
+     */
+    public static $orderFrom = [
+        'shopOrder'  => 1, //商城订单
+        'stockOrder' => 2, //库存服务自建订单
+    ];
+    /**
+     *锁库存与解库存标识
+     * @var array
+     */
+    public static $lockType = [
+        'lock'   => 1, //未出库商品推送出库单,状态未出库
+        'unlock' => 2, //已出库 erp自己创建的销售出库
+    ];
+    /**
+     * 销售退货方式
+     * @var array
+     */
+    public static $sellReturnMoney = [
+        'returnMoney'    => 1, //销售退货退资金
+        'notReturnMoney' => 2, //销售退货不退资金
+    ];
+    /*
+     * 订单状态
+     */
+    public static $orderShippingStatus = [
+        'out'        => 1, //已经提交出库
+        'get'        => 2, //已收货
+        'returnAll'  => 3, //全部退货
+        'returnPart' => 4, //部分退货
+    ];
+    /*
+     * 订单操作来源
+     */
+    public static $actionType = [
+        'shop'  => 0, //商城
+        'stock' => 1, //库存服务
+        'tms'   => 2, //已收货
+    ];
+
+    /**
+     * 定价系统 - 销售价格生效区域类型
+     */
+    public static $salePriceAreaType = [
+        'nationalUnifiedPrice' => 1, // 全国统一价格
+        'areaPrice'            => 2, // 大区和片区价格
+    ];
+
+    /**
+     * 定价系统 - 销售价格类型
+     */
+    public static $salePriceType = [
+        'salePrice'    => 1, // 普通市场价
+        'seckillPrice' => 2, // 活动秒杀价
+    ];
+
+    /**
+     * 定价系统 - 权限标识
+     */
+    public static $aclType = [
+        'edit'   => 1, // 编辑
+        'cat'    => 2, // 查看
+        'noCast' => 3, // 去除成本
+    ];
+
+    /**
+     * 支付方式
+     */
+    public static $payType = [
+        'wxPay'     => 1, //微信
+        'aliPay'    => 2, //支付宝
+        'cashPay'   => 3, //货到付款
+        'bankLoans' => 4, //银行打款
+        'cash'      => 5,//现金支付
+        'other'     => 6,//其他
+        'balance'   => 7, // 余额支付
+        'byte'      => 8, // 字节跳动
+        'cashBefore'=> 9, // 先款后货
+        1 => '微信支付',
+        2 => '支付宝',
+        3 => '货到付款',
+        4 => '银行打款',
+        5 => '现金支付',
+        6 => '其它',
+        7 => '余额支付',
+        8 => '字节跳动',
+        9 => '先款后货'
+    ];
+
+    /**
+     * 配送方式
+     */
+    public static $deliveryType = [
+        'goodsDelivery' => 1, //商品配送
+        'selfMention'   => 2, //上门自提
+        'logistics'     => 3, //物流
+        'logisticsLines'   => 4, // 物流专线
+        'logisticsVehicle' => 5, // 物流专车
+        1               => '快递',
+        2               => '上门自提',
+        3               => '物流',
+        4               => '物流专线',
+        5               => '物流专车',
+    ];
+
+    /**
+     * 业务来源
+     */
+    public static $source = [
+        'ios'               => 1, //ios
+        'android'           => 2,//安卓
+        'miniProgram'       => 3,//小程序
+        'manage'            => 4, //后台创建
+        'H5'                => 5, //H5页面
+        'Pc'                => 6, //Pc页面
+        'manageMiniProgram' => 7,//微信小程序后台管理端
+        'ByteProgram'       => 8,//字节小程序
+        'weiXinBrowser'     => 9, // 微信浏览器
+        1                   => '苹果',
+        2                   => '安卓',
+        3                   => '小程序',
+        4                   => '后台创建',
+        5                   => 'H5页面',
+        6                   => 'PC页面',
+        7                   => '微信小程序后台管理',
+        8                   => '字节小程序',
+        9                   => '微信浏览器'
+    ];
+
+    /**
+     * 订单状态
+     */
+    public static $orderStatus = [
+        //'create'       => 1, //创建
+        'waitPay'      => 2, //等待支付
+        'waitDelivery' => 3, //待发货
+        'waitReceive'  => 4, //待收货
+        'finish'       => 5, //已完成
+        'close'        => 6, //已关闭
+        'cancelIng'    => 7, // 待取消
+        2              => '等待支付',
+        3              => '待发货',
+        4              => '待收货',
+        5              => '已完成',
+        6              => '已关闭',
+        7              => '待取消', // 申请取消订单,等待审核
+    ];
+
+    /**
+     * 订单退货状态
+     */
+    public static $orderReturn = [
+        'notReturn'  => 0, //未退货
+        'partReturn' => 1, //部分退货 (详情里用是已退货)
+        'allReturn'  => 2, //全部退货 (详情里用是已退货)
+    ];
+
+    /**
+     * 单据状态
+     */
+    public static $documentStatus = [
+        'create'         => 1, //已创建
+        'audit'          => 2, //已审核
+        'inventoryAudit' => 3, //库存已审核
+        'return'         => 9, //已退货 (单据详情)
+        'returnAll'      => 7, //全部退货 (主单据)
+        'returnPart'     => 8, //部分退货 (主单据)
+    ];
+
+    /**
+     * 订单退货状态
+     */
+    public static $orderPickStatus = [
+        'notPicking'        =>  1,
+        'picking'           =>  2,
+        'finishPicking'     =>  3,
+        1                   =>  '未拣货',
+        2                   =>  '拣货中',
+        3                   =>  '拣货完成',
+    ];
+
+    /**
+     * 客户账号状态
+     */
+    public static $customerStatus = [
+        'enable' => 1, //已审核
+        'delete' => 2, //待审核
+    ];
+
+    /**
+     * 客户类型
+     */
+    public static $customerType = [
+        'customer' => 5, //企业客户
+        'user'     => 4, //企业用户
+    ];
+
+    /**
+     * 单据类型
+     * @var array
+     */
+    public static $orderType = [
+        'saleOrder'                   => 1,//销售订单
+        'purchaseOrder'               => 2,//采购订单
+        'purchaseIn'                  => 3,//采购入库
+        'purchaseReturn'              => 4,//采购退货
+        'saleOut'                     => 5,//销售出库
+        'saleReturn'                  => 6,//销售退货
+        'allocate'                    => 7,//调拨库存
+        'allocateOut'                 => 8,//调拨出库
+        'batch'                       => 9,//批次编号
+        'priceAdjustment'             => 10,//商品调价单
+        'customerPriceAdjustment'     => 11,//客户调价单
+        'customerTypePriceAdjustment' => 11,//客户类型调价单
+        'allocateIn'                  => 12,//调拨入库
+        'stocktaking'                 => 13,//盘点库存
+        'purchaseReturnOut'           => 14,//采购退货出库
+        'saleReturnIn'                => 15,//销售退货入库
+        'reportLoss'                  => 27,//库存报损
+        'merchantPurchase'            => 29,//商户采购单
+        'merchantPurchaseIn'          => 30,//商户入库单
+
+
+        /*财务*/
+        'shouldReceive'               => 16,//应收单
+        'received'                    => 17,//收款单
+        'shouldPay'                   => 18,//应付单
+        'payed'                       => 19,//付款单
+        'accountTransfer'             => 20,//资金转账
+        'ExpenseSingle'               => 28, //费用单
+
+        //单据
+        'warehouseBeginning'          => 21,//仓库期初
+
+        //会员卡
+        'vipCard'                     => 22,
+
+        //销售单(销售单管理)
+        'salesSlip'                   => 23,//销售单
+        'cashierOrder'                => 24,//收银台订单
+        'storeWithdraw'               => 25,//商户提现
+        'supplierOfferPrice'          => 26,//供应商报价单
+    ];
+
+    /**
+     * 单据编码前缀
+     * @var array
+     */
+    public static $noPrefix = [
+        1  => 'XSDD',// 1 销售订单
+        2  => 'CGDD',// 2 采购订单
+        3  => 'CGRK',// 3 采购入库
+        4  => 'CGTH',// 4 采购退货
+        5  => 'XSCK',// 5 销售出库
+        6  => 'TH',// 6 销售退货
+        7  => 'DBKC',// 7 调拨库存
+        8  => 'DBCK',// 8 调拨出库
+        12 => 'DBRK',//12 调拨入库
+        13 => 'PDKC',//13 盘点库存
+        14 => 'CGTHCK',//14 采购退货出库
+        15 => 'XSTHRK',//15 销售退货入库
+        16 => 'YS', // 16 应收单
+        17 => 'SK', // 17 收款单
+        18 => 'YF', // 18 应付单
+        19 => 'FK', // 19 付款单
+        20 => 'TK', // 20 退款单
+        26 => 'RFQS',// 26 供应商报价单
+        27 => 'BSD',//27 报损单
+        28 => 'FY', // 28 费用单
+        29 =>'SHCG',//商户采购单
+        30 => 'SHRK',//商户入库单
+        31 => 'JHD',//拣货单
+        32 => 'DBD',//调拨单
+        33 => 'BCD',//报损单
+    ];
+
+
+    /**
+     * 编码前缀
+     * @var array
+     */
+    public static $code = [
+        'brand'      => ['prefix' => 'BRAND', 'length' => 6],//品牌
+        'customer'   => ['prefix' => 'CUSTOMER', 'length' => 6],//客户
+        'category'   => ['prefix' => 'CATEGORY', 'length' => 6],//分类
+        'goodsBasic' => ['prefix' => 'MATERIEL', 'length' => 6],//基础物料code
+        'shop'       => ['prefix' => 'SHOP', 'length' => 6],//商铺
+        'supplier'   => ['prefix' => 'SUPPLIER', 'length' => 6],//供应商
+        'staff'      => ['prefix' => 'STAFF', 'length' => 6],//员工
+        'warehouse'  => ['prefix' => 'WAREHOUSE', 'length' => 6],//仓库
+        'account'    => ['prefix' => 'ACCOUNT', 'length' => 6],//账户编码
+    ];
+
+    /**
+     * 角色类型
+     * @var array
+     */
+    public static $roleType = [
+        'admin'    => 1,//后台管理员
+        'staff'    => 2,//员工
+        'customer' => 3,//客户
+        'merchant' => 4,//商户
+        'supplier' => 5,//供应商
+    ];
+
+    /**
+     * 数据域类型
+     * @var array
+     */
+    public static $dataFieldType = [
+        'person' => 1,//个人
+        'staff'  => 2,//员工
+        'all'    => 3,//所有
+    ];
+
+    /**
+     * 小程序模板审核状态
+     */
+    public static $weixinTemplateStatus = [
+        'commit'   => 1, // 提交代码
+        'auditing' => 2, // 提交审核
+    ];
+
+    /*** @var array 模版状态********* */
+    public static $wxAuditStatus = [
+        'pass'        => 0,//审核成功
+        'refuse'      => 1,//审核拒绝
+        'inAudit'     => 2,//审核中
+        'withdrawn'   => 3,//已撤回
+        'postponed'   => 4,//延后
+        'noSubmit'    => 5,//提交审核
+        'beenRelease' => 6,//提交代码
+        'beingUsed'   => 7,//正在使用
+    ];
+
+    /*** @var array 优惠券类型 */
+    public static $couponType = [
+        'commonly'  => 10,// 普通优惠券
+        'vipCoupon' => 20,// 会员卡优惠券
+    ];
+
+    /**@var array 发放方式****** */
+    public static $grantType = [
+        'receive'   => 10,//主动领取
+        'grant'     => 20,//定向发放
+        'register'  => 30,//注册领取
+        'onlinePay' => 40,//在线支付领取
+    ];
+    /***@var array 商品适用范围* */
+    public static $applyRange = [
+        'allGoods'        => 10,//全部商品
+        'appointCategory' => 20,//指定分类
+        'appointBrand'    => 30,//指定品牌
+        'goodsCollect'    => 40,//指定商品
+    ];
+
+    /***************活动类型*********/
+    public static $activityType = [
+        'specialOffer' => 10,//特价
+        'secKill'      => 20,//秒杀
+    ];
+
+    /************************/
+    public static $delivery = [
+        '包邮',
+        '自提'
+    ];
+
+    /******@var array $templateType 模版类型 * */
+    public static $templateType = [
+        'ordinary'  => 1,//普通模版
+        'universal' => 2,//万能模版
+    ];
+
+    /***@var array $pageType 页面类型** */
+    public static $pageType = [
+        'home'    => 1,//首页,
+        'special' => 2,//专题
+    ];
+
+    /*** @var array $specType 规格类型 */
+    public static $specType = [
+        'single'   => 1,//单规格
+        'multiple' => 2,//多规格
+    ];
+
+    /*** @var array 系统默认角色id */
+    public static $roleId = [
+        'cashier' => 1,//收银员工,
+        'guide'   => 2,//导购员
+    ];
+
+    public static $expressType = [
+        'free' => 1,
+        'rule' => 2,
+        'unify' => 3
+    ];
+
+    //匿名用户UID
+    public static $noneUserCenter = 99999;
+
+    //匿名客户id
+    public static $noneCustomer = 99999;
+
+    /**
+     * @var array 流程类型
+     */
+    public static $processType = [
+        'sales' => 1,//订货流程
+        'retSales' => 2,//退货流程
+    ];
+
+    /**
+     * @var array 小程序主体类型
+     */
+    public static $principalType = [
+        'personal' => 0,
+        'enterprise' => 1,
+        'media' => 2,
+        'gov' => 3,
+        'other' => 4,
+    ];
+
+    /**
+     * @var array 业务模式
+     */
+    public static $modelType = [
+        'B2C' => 1,
+        'B2B' => 2,
+        'multi' => 3,//多商户
+    ];
+    
+    /**
+     * @var array 标签颜色
+     */
+    public static $colourType = [
+        1                   => '蓝色',
+        2                   => '绿色',
+        3                   => '粉色',
+        4                   => '橙色',
+    ];
+
+    /**
+     * 财务账户类型
+     */
+    public static $financeAccountType = [
+        'ali' => 1, // 支付宝账户
+        'wx' => 2, // 微信账户
+        'bank' => 3, //银行卡账户
+        'ordinary' => 4, // 普通账户
+    ];
+
+    /**
+     * 出库状态
+     */
+    public static $outStatus = [
+        'notOut' => 4, // 未出库
+        'allOut' => 5, // 全部出库
+        'notAllOut' => 6, // 部分出库
+    ];
+
+    /**
+     * 支付状态
+     */
+    public static $payStatus = [
+        'pay' => 4,
+        'notPay' => 5,
+        'refund' => 6,
+        'partRefund'=>7,
+    ];
+}

+ 33 - 0
Mall/Framework/Core/Swoole.Class.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+class Swoole
+{
+
+    static protected $instance;
+
+    static public function getInstance($options = [], $driver = 'Client')
+    {
+        $driver = $driver ?: 'Client';
+
+        if (!is_array($options) && !$options) {
+            throw new \Exception('Need default Swoole ' . $driver . ' config');
+        }
+
+        /*$key = $driver . md5(implode(',', $options)) . $driver;
+
+        if (!isset(self::$instance[$key])) {
+            $class = 'Mall\\Framework\\Swoole\\' . ucwords(strtolower($driver));
+            if(class_exists($class)) {
+                self::$instance[$key] = new $class($options);
+            }
+        }
+        return self::$instance[$key];
+        */
+
+
+        $class = 'Mall\\Framework\\Swoole\\' . ucwords($driver);
+        return new $class($options);
+    }
+}

+ 17 - 0
Mall/Framework/Core/Url.Class.php

@@ -0,0 +1,17 @@
+<?php
+/**
+ * 统一的内容状态码
+ * 原则上按照数字顺序申请,可以提前占位,体现分组
+ * !!!不允许出现重复状态码!!!
+ */
+namespace Mall\Framework\Core;
+
+class Url
+{
+    /**
+     * 接口地址
+     */
+    public static $offLine = 'v2/shopmanager/goods/auto/obtained';
+    public static $salt = 'autooffline';
+
+}

+ 110 - 0
Mall/Framework/Core/VerificationCode.Class.php

@@ -0,0 +1,110 @@
+<?php
+namespace Mall\Framework\Core;
+
+class VerificationCode{
+    private $width;
+    private $height;
+    private $codeNum;
+    private $code;
+    private $im;
+    protected static $_instance;
+
+    function __construct($width=80, $height=20, $codeNum=4)
+    {
+        $this->width = $width;
+        $this->height = $height;
+        $this->codeNum = $codeNum;
+    }
+
+    public static function getInstance()
+    {
+        $key = md5('VerificationCode');
+
+        if (!isset(self::$_instance[$key])) {
+            self::$_instance[$key] = new self();
+        }
+
+        return self::$_instance[$key];
+    }
+
+    //输出图像
+    public function showImg()
+    {
+        //创建图片
+        $this->createImg();
+        //设置干扰元素
+        $this->setDisturb();
+        //设置验证码
+        $this->setCaptcha();
+        //输出图片
+        $this->outputImg();
+    }
+
+    //获取验证码内容
+    public function getCaptcha()
+    {
+        return $this->code;
+    }
+
+    private function createImg()
+    {
+        $this->im = imagecreatetruecolor($this->width, $this->height);
+        $bgColor = imagecolorallocate($this->im, 0, 0, 0);
+        imagefill($this->im, 0, 0, $bgColor);
+    }
+
+    private function setDisturb()
+    {
+        $area = ($this->width * $this->height) / 20;
+        $disturbNum = ($area > 250) ? 250 : $area;
+        //加入点干扰
+        for ($i = 0; $i < $disturbNum; $i++) {
+            $color = imagecolorallocate($this->im, rand(0, 255), rand(0, 255), rand(0, 255));
+            imagesetpixel($this->im, rand(1, $this->width - 2), rand(1, $this->height - 2), $color);
+        }
+        //加入弧线
+        for ($i = 0; $i <= 5; $i++) {
+            $color = imagecolorallocate($this->im, rand(128, 255), rand(125, 255), rand(100, 255));
+            imagearc($this->im, rand(0, $this->width), rand(0, $this->height), rand(30, 300), rand(20, 200), 50, 30, $color);
+        }
+    }
+
+    private function createCode()
+    {
+        $str = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKMNPQRSTUVWXYZ";
+
+        for ($i = 0; $i < $this->codeNum; $i++) {
+            $this->code .= $str{rand(0, strlen($str) - 1)};
+        }
+    }
+
+    private function setCaptcha()
+    {
+        $this->createCode();
+
+        for ($i = 0; $i < $this->codeNum; $i++) {
+            $color = imagecolorallocate($this->im, rand(50, 250), rand(100, 250), rand(128, 250));
+            $size = rand(floor($this->height / 5), floor($this->height / 3));
+            $x = floor($this->width / $this->codeNum) * $i + 5;
+            $y = rand(0, $this->height - 20);
+            imagechar($this->im, $size, $x, $y, $this->code{$i}, $color);
+        }
+    }
+
+    private function outputImg()
+    {
+        if (imagetypes() & IMG_JPG) {
+            header('Content-type:image/jpeg');
+            imagejpeg($this->im);
+        } elseif (imagetypes() & IMG_GIF) {
+            header('Content-type: image/gif');
+            imagegif($this->im);
+        } elseif (imagetypes() & IMG_PNG) {
+            header('Content-type: image/png');
+            imagepng($this->im);
+        } else {
+            die("Don't support image type!");
+        }
+    }
+
+}

+ 60 - 0
Mall/Framework/Core/View.Class.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace Mall\Framework\Core;
+
+require_once ROOT_PATH . DS . 'Framework/Smarty/libs/Smarty.class.php';
+
+use Mall\Framework\Core\Config;
+
+class View {
+
+    protected $template;
+    protected $smartyConfig;
+
+    private static $_instance;
+
+    private function __construct($options = [])
+    {
+        $this->template = new \Smarty();
+        $this->smartyConfig = $options ?: Config::getInstance()->get('smarty');
+
+        $this->template
+            ->setTemplateDir($this->smartyConfig['template_dir'])
+            ->setCompileDir($this->smartyConfig['compile_dir'])
+            ->setCacheDir($this->smartyConfig['cache_dir']);
+
+        $this->template->caching = $this->smartyConfig['is_caching'];
+
+        $this->template->left_delimiter="<{";
+        $this->template->right_delimiter="}>";
+    }
+
+    public static function getInstance($options = [])
+    {
+        if (!self::$_instance instanceof self) {
+            self::$_instance = new self($options);
+        }
+
+        return self::$_instance;
+    }
+
+    public function assign($tpl_var, $value = null, $nocache = false)
+    {
+        $this->template->assign($tpl_var, $value, $nocache);
+    }
+
+    public function display($template = null, $cache_id = null, $compile_id = null, $parent = null)
+    {
+        $template = explode('.', $template)[0] . $this->smartyConfig['tpl_type'];
+
+        $this->template->display($template, $cache_id, $compile_id, $parent);
+    }
+
+    public function fetch($template = null, $cache_id = null, $compile_id = null, $parent = null)
+    {
+        $template = explode('.', $template)[0] . $this->smartyConfig['tpl_type'];
+
+        return $this->template->fetch($template, $cache_id, $compile_id, $parent);
+    }
+
+}

+ 650 - 0
Mall/Framework/Db/Db.Class.php

@@ -0,0 +1,650 @@
+<?php
+
+namespace Mall\Framework\Db;
+
+class Db
+{
+    static $queries = 0;
+    public $master = array(), $slaves = array(), $slave = array(), $slave_key, $sql,  $error, $errno;
+    private $dbh, $dbh_master, $dbh_slave, $foreign_key_checks = 0;
+
+    public function __construct($master = array(), $slaves = array())
+    {
+        $this->master = $master;
+        $this->dbh_master = $this->connect($master);
+        $this->dbh = $this->dbh_master;
+
+        if(!empty($slaves)){
+            $this->slaves = $slaves;
+            $this->dbh_slave = $this->connect_slave();
+            $this->dbh = $this->dbh_slave;
+        }
+    }
+
+    public function __call($method, $args)
+    {
+        if (in_array($method, array('errorCode', 'errorInfo', 'getAttribute', 'lastInsertId', 'quote', 'setAttribute'), true)) {
+            if (in_array($method, array('lastInsertId'))) {
+                return $this->dbh_master()->$method();
+            }
+            if (isset($args[0])) {
+                return isset($args[1]) ? $this->dbh()->$method($args[0], $args[1]) : $this->dbh()->$method($args[0]);
+            } else {
+                return $this->dbh()->$method();
+            }
+        }
+    }
+
+    public function beginTransaction($foreign_key_checks = false)
+    {
+        try{
+            $result = $this->dbh_master()->beginTransaction();
+
+            if ($result && !$foreign_key_checks) {
+                $this->foreign_key_checks = intval($this->dbh_master()->query("SELECT @@FOREIGN_KEY_CHECKS")->fetchColumn(0));
+                $this->foreign_key_checks && $this->dbh_master()->query("SET @@FOREIGN_KEY_CHECKS=0");
+            }
+            return $result;
+        }catch (\PDOException $e){
+            if ( isset($e->errorInfo) && ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) ) {
+                $this->closeConnection();
+                return $this->beginTransaction($foreign_key_checks);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+    }
+
+    public function commit()
+    {
+        try {
+            $result = $this->dbh_master()->commit();
+            if ($this->foreign_key_checks) {
+                $this->dbh_master()->query("SET @@FOREIGN_KEY_CHECKS=1");
+                $this->foreign_key_checks = 0;
+            }
+            return $result;
+        }catch (\PDOException $e){
+            if ( isset($e->errorInfo) && ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) ) {
+                $this->closeConnection();
+                return $this->commit();
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+
+    }
+
+    public function rollBack()
+    {
+        try{
+            $result = $this->dbh_master()->rollBack();
+            if ($this->foreign_key_checks) {
+                $this->dbh_master()->query("SET @@FOREIGN_KEY_CHECKS=1");
+                $this->foreign_key_checks = 0;
+            }
+            return $result;
+        }catch (\PDOException $e){
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->rollBack();
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+
+    }
+
+    public function connect($options = array())
+    {
+        try {
+            $dbh = new \PDO($options['driver'] . ':host=' . $options['host'] . ';port=' . $options['port'] . ';dbname=' . $options['dbname'] . ';charset=' . $options['charset'], $options['username'], $options['password'], array(
+                \PDO::ATTR_PERSISTENT => ($options['pconnect'] ? true : false),)
+            );
+            $dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+            $dbh->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, false);
+            $dbh->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
+
+        } catch (\PDOException $e) {
+            $this->errno = $e->getCode();
+            $this->error = $e->getMessage();
+            return false;
+        }
+        if ($options['driver'] == 'mysql') {
+            $dbh->exec("SET character_set_connection='" . $options['charset'] . "',character_set_results='" . $options['charset'] . "',character_set_client='{$options['charset']}'" . ($dbh->query("SELECT version()")->fetchColumn(0) > '5.0.1' ? ",sql_mode=''" : ''));
+        }
+        return $dbh;
+    }
+
+    private function connect_slave()
+    {
+        $this->slave_key = array_rand($this->slaves);
+        $this->slave = $this->slaves[$this->slave_key];
+        $this->dbh_slave = $this->connect($this->slave);
+        if (!$this->dbh_slave && count($this->slaves) > 1) {
+            unset($this->slaves[$this->slave_key]);
+            return $this->connect_slave();
+        }
+        return $this->dbh_slave;
+    }
+
+    // https://github.com/walkor/mysql/blob/master/src/Connection.php
+    // https://my.oschina.net/u/222608/blog/1621402
+    public function closeConnection(){
+        $this->dbh = null;
+        $this->dbh_master = null;
+        $this->dbh_slave  = null;
+    }
+
+    public function exec($statement)
+    {
+        try{
+            return $this->dbh($statement) ? $this->dbh->exec($this->sql) : false;
+        }catch (\PDOException $e){
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->exec($statement);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$this->sql." ".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+    }
+
+    public function prepare($statement, $driver_options = array())
+    {
+        try{
+            return $this->dbh($statement) ? $this->dbh->prepare($this->sql, $driver_options) : false;
+        }catch (\PDOException $e){
+            file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'prepare'.var_export('prepare超时错误了',true).PHP_EOL,FILE_APPEND);
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->prepare($statement, $driver_options);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$this->sql." ".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+
+                file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'prepare'.var_export($e->getMessage(),true).PHP_EOL,FILE_APPEND);
+                return false;
+            }
+        }
+    }
+
+    public function exportQuery($statement)
+    {
+        try {
+            if($this->dbh($statement)){
+                $this->dbh->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
+                return $this->dbh->query($this->sql,\PDO::FETCH_ASSOC)->fetchAll();
+            }else{
+                return false;
+            }
+        }catch (\PDOException $e){
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->query($statement);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$this->sql." ".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+    }
+
+    public function query($statement)
+    {
+        try {
+            return $this->dbh($statement) ? $this->dbh->query($this->sql,\PDO::FETCH_ASSOC)->fetchAll() : false;
+        }catch (\PDOException $e){
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->query($statement);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$this->sql." ".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+    }
+
+    private function dbh($sql = null)
+    {
+        if($sql === null){
+            $this->sql = null;
+        }else{
+            $this->sql = $sql;
+        }
+
+        if (is_null($this->dbh)) {
+            if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
+            $this->dbh = $this->dbh_master;
+        }
+        return $this->dbh;
+
+
+        self::$queries++;
+        $this->sql = str_replace('#table_', $this->master['prefix'], trim($sql));
+        if ($this->slaves && is_null($this->dbh_master) && stripos($this->sql, 'select') === 0) {
+            if (is_null($this->dbh_slave)) $this->dbh_slave = $this->connect_slave();
+            $this->dbh = $this->dbh_slave;
+        } else {
+            if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
+            $this->dbh = $this->dbh_master;
+        }
+        return $this->dbh;
+    }
+
+    public function get($sql, $data = array(), $fetch_style = \PDO::FETCH_ASSOC)
+    {
+        $this->sql = $sql;
+        try{
+            $db = $this->prepare($sql);
+            if (!$db) return false;
+            if ($db->execute($data)) {
+                return $db->fetch($fetch_style);
+            } else {
+                $this->errno = $db->errorCode();
+                $this->error = $db->errorInfo();
+                return false;
+            }
+        }catch (\PDOException $e){
+            file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'get'.var_export('get超时错误了',true).PHP_EOL,FILE_APPEND);
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->get($sql, $data, $fetch_style);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$sql." ".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+    }
+
+    public function cursor($sth)
+    {
+        while($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
+            yield $row;
+        }
+    }
+
+    public function exportSelect($sql, $data = array())
+    {
+        $this->sql = $sql;
+
+        try{
+            $db = $this->prepare($sql);
+            if (!$db) return false;
+            $this->dbh->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
+            if ($db->execute($data)) {
+                return $this->cursor($db);
+            } else {
+                $this->errno = $db->errorCode();
+                $this->error = $db->errorInfo();
+                return false;
+            }
+        }catch (\PDOException $e){
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->exportSelect($sql, $data);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$sql." ".$msg;
+                echo $err_msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+    }
+
+    public function select($sql, $data = array(), $fetch_style = \PDO::FETCH_ASSOC)
+    {
+        $this->sql = $sql;
+
+        try{
+            //V($sql);
+            $db = $this->prepare($sql);
+            if (!$db) return false;
+            if ($db->execute($data)) {
+                return $db->fetchAll($fetch_style);
+            } else {
+                $this->errno = $db->errorCode();
+                $this->error = $db->errorInfo();
+                return false;
+            }
+        }catch (\PDOException $e){
+            file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'select'.var_export('select超时错误了',true).PHP_EOL,FILE_APPEND);
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->select($sql, $data, $fetch_style);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$sql." ".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+    }
+
+    public function insert($sql, $data = array(), $multiple = false)
+    {
+        $this->sql = $sql;
+        try{
+            $db = $this->prepare($sql);
+            if (!$db) return false;
+            if (empty($data)) {
+                if ($db->execute()) {
+                    $insertid = $this->dbh_master->lastInsertId();
+                    return $insertid ? $insertid : true;
+                } else {
+                    $this->errno = $db->errorCode();
+                    $this->error = $db->errorInfo();
+                    return false;
+                }
+            }
+            if ($multiple) {
+                $insertids = [];
+                foreach ($data as $r) {
+                    $this->_bindValue($db, $r);
+                    if ($db->execute()) {
+                        $insertids[] = $this->dbh_master->lastInsertId();
+                    }else{
+                        $this->errno = $db->errorCode();
+                        $this->error = $db->errorInfo();
+                        return false;
+                    }
+                }
+                return !empty($insertids) ? $insertids : true;
+            } else {
+                $this->_bindValue($db, $data);
+                if ($db->execute()) {
+                    $insertid = $this->dbh_master->lastInsertId();
+                    return $insertid > 0 ? $insertid : true;
+                } else {
+                    $this->errno = $db->errorCode();
+                    $this->error = $db->errorInfo();
+                    return false;
+                }
+            }
+        }catch (\PDOException $e){
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->insert($sql, $data, $multiple);
+            }else{
+                file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'insert'.var_export($data,true).PHP_EOL,FILE_APPEND);
+
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$sql." ".$msg;
+                $this->sql   = $sql;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'insert'.var_export($e->getMessage(),true).PHP_EOL,FILE_APPEND);
+                return false;
+            }
+        }
+    }
+
+    public function update($sql, $data = array(), $multiple = false)
+    {
+        $this->sql = $sql;
+        try{
+            $db = $this->prepare($sql);
+            if (!$db) return false;
+            if (empty($data)) {
+                if ($db->execute()) {
+                    $rowcount = $db->rowCount();
+                    return $rowcount ? $rowcount : true;
+                } else {
+                    $this->errno = $db->errorCode();
+                    $this->error = $db->errorInfo();
+                    return false;
+                }
+            }
+            if ($multiple) {
+                foreach ($data as $r) {
+                    $this->_bindValue($db, $r);
+                    if (!$db->execute()) {
+                        $this->errno = $db->errorCode();
+                        $this->error = $db->errorInfo();
+                        return false;
+                    }
+                }
+                return true;
+            } else {
+                $this->_bindValue($db, $data);
+                if ($db->execute()) {
+                    $rowcount = $db->rowCount();
+                    return $rowcount ? $rowcount : true;
+                } else {
+                    $this->errno = $db->errorCode();
+                    $this->error = $db->errorInfo();
+                    return false;
+                }
+            }
+        }catch (\PDOException $e){
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->update($sql, $data, $multiple);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$sql." ".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+
+    }
+
+    public function replace($sql, $data = array(), $multiple = false)
+    {
+        return $this->update($sql, $data, $multiple);
+    }
+
+    public function delete($sql, $data = array())
+    {
+        $this->sql = $sql;
+
+        try{
+            $db = $this->prepare($sql);
+            if (!$db) return false;
+            if ($db->execute($data)) {
+                $rowcount = $db->rowCount();
+                return $rowcount ? $rowcount : true;
+            } else {
+                $this->errno = $db->errorCode();
+                $this->error = $db->errorInfo();
+                return false;
+            }
+        }catch (\PDOException $e){
+            if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
+                $this->closeConnection();
+                return $this->delete($sql, $data);
+            }else{
+                $msg = $e->getMessage();
+                $err_msg = "SQL:".$sql." ".$msg;
+                $this->errno = (int)$e->getCode();
+                $this->error = $err_msg;
+                return false;
+            }
+        }
+    }
+
+    public function limit($sql, $limit = 0, $offset = 0, $data = array(), $fetch_style = \PDO::FETCH_ASSOC)
+    {
+        if ($limit > 0) $sql .= $offset > 0 ? " LIMIT $offset, $limit" : " LIMIT $limit";
+        return $this->select($sql, $data, $fetch_style);
+    }
+
+    public function page($sql, $page = 1, $size = 20, $data = array(), $fetch_style = \PDO::FETCH_ASSOC)
+    {
+        $page = isset($page) ? max(intval($page), 1) : 1;
+        $size = max(intval($size), 1);
+        $offset = ($page - 1) * $size;
+        return $this->limit($sql, $size, $offset, $data, $fetch_style);
+    }
+
+    public function select_db($dbname)
+    {
+        return $this->exec("USE $dbname");
+    }
+
+    public function list_fields($table, $field = null)
+    {
+        $sql = "SHOW COLUMNS FROM `$table`";
+        if ($field) $sql .= " LIKE '$field'";
+        return $this->query($sql);
+    }
+
+    public function list_tables($dbname = null)
+    {
+        $tables = array();
+        $sql = $dbname ? "SHOW TABLES FROM `$dbname`" : "SHOW TABLES";
+        $result = $this->query($sql);
+        foreach ($result as $r) {
+            $tables[] = array_pop($r);
+        }
+        return $tables;
+    }
+
+    public function list_dbs()
+    {
+        $dbs = array();
+        $result = $this->query("SHOW DATABASES");
+        foreach ($result as $r) {
+            foreach ($r as $db) $dbs[] = $db;
+        }
+        return $dbs;
+    }
+
+    public function get_primary($table)
+    {
+        $primary = array();
+        $result = $this->query("SHOW COLUMNS FROM `$table`");
+        foreach ($result as $r) {
+            if ($r['Key'] == 'PRI') $primary[] = $r['Field'];
+        }
+        return count($primary) == 1 ? $primary[0] : (empty($primary) ? null : $primary);
+    }
+
+    public function get_var($var = null)
+    {
+        $variables = array();
+        $sql = is_null($var) ? '' : " LIKE '$var'";
+        $result = $this->query("SHOW VARIABLES $sql");
+        foreach ($result as $r) {
+            if (!is_null($var) && isset($r['Value'])) return $r['Value'];
+            $variables[$r['Variable_name']] = $r['Value'];
+        }
+        return $variables;
+    }
+
+    public function version()
+    {
+        $db = $this->query("SELECT version()");
+        return $db ? $db->fetchColumn(0) : false;
+    }
+
+    public function prefix()
+    {
+        return $this->master['prefix'];
+    }
+
+    public function errno()
+    {
+        return is_null($this->errno) ? $this->errorCode() : $this->errno;
+    }
+
+    public function error()
+    {
+        if (is_null($this->error)) {
+            return $this->errorInfo();
+        } else {
+            /*if($this->sql && $this->error){
+                $this->error .= 'sql:'.$this->sql;
+            }*/
+            return $this->error;
+        }
+    }
+
+    /**
+     * 检查数据库连接,是否有效,无效则重新建立
+     */
+    protected function checkConnection()
+    {
+        if (!$this->pdo_ping())
+        {
+            $this->dbh_master = NULL;
+        }
+    }
+
+    /**
+     * 检查连接是否可用
+     * @return Boolean
+     */
+    function pdo_ping(){
+        file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'ping'.var_export(is_null($this->dbh),true).PHP_EOL,FILE_APPEND);
+        if( is_null($this->dbh) ){
+            return false;
+        }
+
+        if (!$this->dbh->getAttribute(\PDO::ATTR_SERVER_INFO)) {
+            $this->errno = $this->dbh->errorCode();
+            $this->error = $this->dbh->errorInfo();
+
+            file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'error'.var_export($this->errno.':'.$this->error,true).PHP_EOL,FILE_APPEND);
+            if($this->errno == 'HY000'){
+                $this->dbh_master = null;
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    private function dbh_master()
+    {
+        //$this->checkConnection();
+
+        if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
+        $this->dbh = $this->dbh_master;
+        return $this->dbh;
+    }
+
+
+
+    private function _bindValue(& $db, $data)
+    {
+        if (!is_array($data)) return false;
+        foreach ($data as $k => $v) {
+            $k = is_numeric($k) ? $k + 1 : ':' . $k;
+            $db->bindValue($k, $v);
+        }
+        return true;
+    }
+}

+ 385 - 0
Mall/Framework/Db/DbMssql.Class.php

@@ -0,0 +1,385 @@
+<?php
+
+namespace Mall\Framework\Db;
+
+class DbMssql
+{
+    static $queries = 0;
+
+    public $options = array();
+    public $master = array();
+    public $slaves = array();
+    public $slave = array();
+    public $slave_key;
+    public $sql;
+
+    public $dbname;
+    public $prefix;
+    public $charset;
+
+    protected $_error;
+    protected $_cmstop_charset;
+    protected $_transaction_started;
+
+    private $dbh;
+    private $dbh_master;
+    private $dbh_slave;
+
+    function __construct($master = array(), $slaves = array())
+    {
+        $this->master = $master;
+        $this->options = &$this->master;
+        if ($slaves) $this->slaves = $slaves;
+
+        // 禁止 SQL Server 进行日期格式转换
+        ini_set('mssql.datetimeconvert', 0);
+
+        // 统一编码格式
+        $this->_cmstop_charset = $this->_normorlize_charset(config('config', 'charset'));
+    }
+
+    function __destruct()
+    {
+        if ($this->dbh) {
+            mssql_close($this->dbh);
+        }
+    }
+
+    function beginTransaction()
+    {
+        if (!$this->_transaction_started) {
+            $result = $this->exec('BEGIN TRAN');
+            $this->_transaction_started = true;
+            return $result;
+        }
+        $this->_error = 'Transaction had already begin';
+        return false;
+    }
+
+    function commit()
+    {
+        if ($this->_transaction_started) {
+            $result = $this->exec('COMMIT TRAN');
+            $this->_transaction_started = false;
+            return $result;
+        }
+        $this->_error = 'No activied transaction';
+        return false;
+    }
+
+    function rollBack()
+    {
+        if ($this->_transaction_started) {
+            $result = $this->exec('ROLLBACK TRAN');
+            $this->_transaction_started = false;
+            return $result;
+        }
+        $this->_error = 'No activied transaction';
+        return false;
+    }
+
+    function lastInsertId()
+    {
+        return $this->exec('SELECT SCOPE_IDENTITY()');
+    }
+
+    static function get_instance($master = array(), $slaves = array())
+    {
+        static $instance;
+        $key = implode('', $master);
+        if (!isset($instance[$key])) {
+            $instance[$key] = new DbMssql($master, $slaves);
+
+        }
+        return $instance[$key];
+    }
+
+    function connect($options = array())
+    {
+        $host = value($options, 'host');
+        $port = value($options, 'port');
+        $dbname = value($options, 'dbname');
+        $prefix = value($options, 'prefix', '');
+        $charset = value($options, 'charset');
+        $username = value($options, 'username');
+        $password = value($options, 'password');
+        $pconnect = value($options, 'pconnect');
+
+        $handler = $pconnect ? 'mssql_pconnect' : 'mssql_connect';
+        $server = $port ? ($host . (stripos(PHP_OS, 'win') === 0 ? ',' : ':') . $port) : $host;
+
+        if (!function_exists($handler)) {
+            throw new \Exception("SQL Server extension not enabled");
+        }
+
+        if (!($dbh = $handler($server, $username, $password))) {
+            return false;
+        }
+
+        if (!$dbname) {
+            $this->_error = 'No database name specialed';
+            return false;
+        }
+
+        if (!mssql_select_db($dbname, $dbh)) {
+            return false;
+        }
+
+        // 处理 SQL Server nchar,nvarchar ... 字段的问题
+        mssql_query('SET TEXTSIZE 1024000', $dbh);
+
+        $this->dbname = $dbname;
+        $this->prefix = $prefix;
+
+        // 统一编码格式
+        $this->charset = $this->_normorlize_charset($charset);
+
+        return $dbh;
+    }
+
+    private function connect_slave()
+    {
+        $this->slave_key = array_rand($this->slaves);
+        $this->slave = $this->slaves[$this->slave_key];
+        $this->dbh_slave = $this->connect($this->slave);
+        if (!$this->dbh_slave && count($this->slaves) > 1) {
+            unset($this->slaves[$this->slave_key]);
+            return $this->connect_slave();
+        }
+        return $this->dbh_slave;
+    }
+
+    function exec($statement, $multiple = false)
+    {
+        if (!$this->dbh($statement)) return false;
+
+        $query = mssql_query($this->_prepare_sql($this->sql), $this->dbh);
+        if (!$query) {
+            return false;
+        }
+
+        if (!is_resource($query)) {
+            return $query;
+        }
+
+        if ($multiple) {
+            $result = array();
+            while ($row = mssql_fetch_assoc($query)) {
+                $result[] = $row;
+            }
+        } else {
+            $result = mssql_fetch_assoc($query);
+            if (is_array($result)) {
+                $result = array_shift(array_values($result));
+            }
+        }
+        mssql_free_result($query);
+
+        /*if ($this->charset != $this->_cmstop_charset)
+        {
+            $result = str_charset($this->charset, $this->_cmstop_charset, $result);
+        }*/
+
+        return $result;
+    }
+
+    function query($statement)
+    {
+        return $this->exec($statement);
+    }
+
+    function get($sql)
+    {
+        return $this->exec($sql);
+    }
+
+    function select($sql)
+    {
+        return $this->exec($sql, true);
+    }
+
+    function insert($sql)
+    {
+        return $this->exec($sql);
+    }
+
+    function update($sql)
+    {
+        return $this->exec($sql);
+    }
+
+    function replace($sql)
+    {
+        return $this->update($sql);
+    }
+
+    function delete($sql)
+    {
+        return $this->exec($sql);
+    }
+
+    function limit($sql, $limit = 0)
+    {
+        if ($limit
+            && preg_match('/^SELECT\s+(.+)\s+FROM\s+(.+?)$/Usim', $sql, $matches)
+            && !preg_match('/\bTOP\b/sim', $matches[1])
+        ) {
+            $fields = $matches[1];
+            $from = $matches[2];
+            $sql = "SELECT TOP $limit $fields FROM $from";
+        }
+        return $this->select($sql);
+    }
+
+    function select_db($dbname)
+    {
+        return $this->exec("USE $dbname");
+    }
+
+    function list_fields($table)
+    {
+        static $result = array();
+
+        if (!is_array($result[$table])) {
+            if ($fields = $this->select("
+                SELECT [column_name], [data_type], [column_default], [is_nullable]
+                FROM [information_schema].[tables] AS t
+                JOIN [information_schema].[columns] AS c
+                ON t.table_catalog = c.table_catalog
+                AND t.table_schema = c.table_schema
+                AND t.table_name = c.table_name
+                WHERE t.table_name = '$table'")
+            ) {
+                foreach ($fields as $field) {
+                    $result[$table][$field['column_name']] = array(
+                        'Field' => $field['column_name'],
+                        'Type' => $field['data_type'],
+                        'Null' => $field['is_nullable'] === '' ? 'NO' : 'YES',
+                        'Key' => '',
+                        'Default' => $field['column_default'],
+                        'Extra' => ''
+                    );
+                }
+            }
+        }
+
+        return $result[$table];
+    }
+
+    function list_tables()
+    {
+        static $result;
+
+        if (!is_array($result)) {
+            if ($fields = $this->select("SELECT [TABLE_NAME] FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_TYPE] = 'BASE TABLE'")) {
+                foreach ($fields as $field) {
+                    $result[] = $field['TABLE_NAME'];
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    function list_dbs()
+    {
+        static $result;
+
+        if (!is_array($result)) {
+            if ($fields = $this->select("SELECT [name] FROM [master]..[sysdatabases] WHERE [name] NOT IN ('master', 'model', 'msdb', 'tempdb') ORDER BY [name]")) {
+                foreach ($fields as $field) {
+                    $result[] = $field['name'];
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    function get_primary($table)
+    {
+        static $result = array();
+
+        if (!in_array($result, $table)) {
+            $primarys = array();
+            foreach ($this->exec("EXEC sp_pkeys '$table'", true) as $primary) {
+                $primarys[] = $primary['COLUMN_NAME'];
+            }
+            $result[$table] = implode(',', $primarys);
+        }
+
+        return $result[$table];
+    }
+
+    function field_type($table, $field)
+    {
+        static $result = array();
+
+        $key = md5($table . $field);
+        if (!is_array($result[$key])) {
+            if ($fields = $this->select("SELECT [DATA_TYPE] FROM [INFORMATION_SCHEMA].[COLUMNS] WHERE [TABLE_NAME] = '$table' AND [COLUMN_NAME] = '$field'")) {
+                foreach ($fields as $field) {
+                    $result[$key] = $field['DATA_TYPE'];
+                }
+            }
+        }
+
+        return $result[$key];
+    }
+
+    function version()
+    {
+        return $this->exec('SELECT @@VERSION');
+    }
+
+    function prefix()
+    {
+        return $this->master['prefix'];
+    }
+
+    function error()
+    {
+        if (is_null($this->_error)) {
+            $this->_error = mssql_get_last_message();
+            if ($this->_error && $this->charset != $this->_cmstop_charset) {
+                $this->_error = str_charset($this->charset, $this->_cmstop_charset, $this->_error);
+            }
+        }
+        return $this->_error;
+    }
+
+    protected function _prepare_sql($sql)
+    {
+        $this->sql = str_replace('#table_', $this->master['prefix'], trim($sql));
+        $this->sql = preg_replace('/(`(\w*)`)/Usim', '[$2]', $this->sql);
+        return $this->sql;
+    }
+
+    protected function _normorlize_charset($charset)
+    {
+        return strtolower(str_replace('-', '', $charset));
+    }
+
+    private function dbh($sql = null)
+    {
+        if (is_null($sql)) {
+            $this->sql = null;
+            if (is_null($this->dbh)) {
+                if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
+                $this->dbh = $this->dbh_master;
+            }
+            return $this->dbh;
+        }
+
+        self::$queries++;
+        $this->sql = str_replace('#table_', $this->master['prefix'], trim($sql));
+        if ($this->slaves && is_null($this->dbh_master) && stripos($this->sql, 'select') === 0) {
+            if (is_null($this->dbh_slave)) $this->dbh_slave = $this->connect_slave();
+            $this->dbh = $this->dbh_slave;
+        } else {
+            if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
+            $this->dbh = $this->dbh_master;
+        }
+        return $this->dbh;
+    }
+}

+ 407 - 0
Mall/Framework/Db/DbOci.php

@@ -0,0 +1,407 @@
+<?php
+
+namespace Mall\Framework\Db;
+
+class DbOci
+{
+    static $queries = 0;
+
+    public $options = array();
+    public $master = array();
+    public $slaves = array();
+    public $slave = array();
+    public $slave_key;
+    public $sql;
+
+    public $dbname;
+    public $prefix;
+    public $charset;
+
+    protected $_error;
+    protected $_cmstop_charset;
+    protected $_transaction_started;
+    protected $_table;
+    protected $_mode;
+
+    private $dbh;
+    private $dbh_master;
+    private $dbh_slave;
+
+    function __construct($master = array(), $slaves = array())
+    {
+        $this->master = $master;
+        $this->options = &$this->master;
+        if ($slaves) $this->slaves = $slaves;
+
+        // 设置全局字符集
+        putenv('NLS_LANG=AMERICAN_AMERICA.UTF8');
+
+        // 设置会话期间的日期格式
+        $this->exec("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
+
+        // 统一编码格式
+        $this->_cmstop_charset = $this->_normorlize_charset(config('config', 'charset'));
+    }
+
+    function __destruct()
+    {
+        if ($this->dbh) {
+            oci_close($this->dbh);
+        }
+    }
+
+    function beginTransaction()
+    {
+        if (!$this->_transaction_started) {
+            $this->_mode = OCI_DEFAULT;
+            $this->_transaction_started = true;
+            return true;
+        }
+        $this->_error = 'Transaction had already begin';
+        return false;
+    }
+
+    function commit()
+    {
+        if (!$this->dbh()) return false;
+
+        if ($this->_transaction_started) {
+            $result = oci_commit($this->dbh);
+            $this->_transaction_started = false;
+            return $result;
+        }
+        $this->_error = 'No activied transaction';
+        return false;
+    }
+
+    function rollBack()
+    {
+        if (!$this->dbh()) return false;
+
+        if ($this->_transaction_started) {
+            $result = oci_rollback($this->dbh);
+            $this->_transaction_started = false;
+            return $result;
+        }
+        $this->_error = 'No activied transaction';
+        return false;
+    }
+
+    function lastInsertId($table = null, $primary_key = null)
+    {
+        if ($table !== null) {
+            $this->_table = $table;
+        }
+        if (!$this->_table) {
+            return 0;
+        }
+
+        $sequence_name = $this->_table;
+        if ($primary_key) {
+            $sequence_name .= "_{$primary_key}";
+        }
+        $sequence_name .= '_seq';
+        return $this->lastSequenceId($sequence_name);
+    }
+
+    function lastSequenceId($sequence_name)
+    {
+        if (!$this->dbh()) return false;
+        $sql = 'SELECT ' . $sequence_name . '.CURRVAL FROM dual';
+        return $this->get($sql);
+    }
+
+    function nextSequenceId($sequence_name)
+    {
+        if (!$this->dbh()) return false;
+        $sql = 'SELECT ' . $sequence_name . '.NEXTVAL FROM dual';
+        return $this->get($sql);
+    }
+
+    static function get_instance($master = array(), $slaves = array())
+    {
+        static $instance;
+        $key = implode('', $master);
+        if (!isset($instance[$key])) {
+            $instance[$key] = new DbOci($master, $slaves);
+
+        }
+        return $instance[$key];
+    }
+
+    function connect($options = array())
+    {
+        $host = value($options, 'host');
+        $port = value($options, 'port');
+        $dbname = value($options, 'dbname');
+        $prefix = value($options, 'prefix', '');
+        $charset = value($options, 'charset');
+        $username = value($options, 'username');
+        $password = value($options, 'password');
+        $pconnect = value($options, 'pconnect');
+
+        $handler = $pconnect ? 'oci_pconnect' : 'oci_connect';
+        $server = '//' . $host . ($port ? (':' . $port) : '') . '/' . $dbname;
+
+        if (!function_exists($handler)) {
+            throw new \Exception("OCI extension not enabled");
+        }
+
+        if (!($dbh = $handler($username, $password, $server))) {
+            return false;
+        }
+
+        $this->dbname = $dbname;
+        $this->prefix = $prefix;
+
+        // 统一编码格式
+        $this->charset = $this->_normorlize_charset($charset);
+
+        return $dbh;
+    }
+
+    private function connect_slave()
+    {
+        $this->slave_key = array_rand($this->slaves);
+        $this->slave = $this->slaves[$this->slave_key];
+        $this->dbh_slave = $this->connect($this->slave);
+        if (!$this->dbh_slave && count($this->slaves) > 1) {
+            unset($this->slaves[$this->slave_key]);
+            return $this->connect_slave();
+        }
+        return $this->dbh_slave;
+    }
+
+    function exec($statement, $multiple = false)
+    {
+        if (!$this->dbh($statement)) return false;
+
+        if (preg_match('/^\s*(INSERT\s+INTO)\s+(\w+)\s+/i', $this->sql, $matches)) {
+            $this->_table = $matches[2];
+        }
+
+        $this->_mode = OCI_COMMIT_ON_SUCCESS;
+        $stmt = oci_parse($this->dbh, $this->_prepare_sql($this->sql));
+        if (false === oci_execute($stmt, $this->_mode)) {
+            return false;
+        }
+
+        if ($multiple) {
+            $result = array();
+            oci_fetch_all($stmt, $result, 0, -1, OCI_FETCHSTATEMENT_BY_ROW + OCI_ASSOC);
+        } else {
+            $result = oci_fetch_assoc($stmt);
+            if (is_array($result)) {
+                $result = array_shift(array_values($result));
+            }
+        }
+        oci_free_statement($stmt);
+
+        /*if ($this->charset != $this->_cmstop_charset)
+        {
+            $result = str_charset($this->charset, $this->_cmstop_charset, $result);
+        }*/
+
+        return $result;
+    }
+
+    function query($statement)
+    {
+        return $this->exec($statement);
+    }
+
+    function get($sql)
+    {
+        return $this->exec($sql);
+    }
+
+    function select($sql)
+    {
+        return $this->exec($sql, true);
+    }
+
+    function insert($sql)
+    {
+        return $this->exec($sql);
+    }
+
+    function update($sql)
+    {
+        return $this->exec($sql);
+    }
+
+    function replace($sql)
+    {
+        return $this->update($sql);
+    }
+
+    function delete($sql)
+    {
+        return $this->exec($sql);
+    }
+
+    public function limit($sql, $limit = 0, $offset = 0)
+    {
+        $limit_sql = "SELECT c2.*
+            FROM (
+                SELECT c1.*, ROWNUM AS \"cmstop_db_rownum\"
+                FROM (
+                    {$sql}
+                ) c1
+            ) c2
+            WHERE c2.\"cmstop_db_rownum\" BETWEEN " . ($offset + 1) . " AND " . ($offset + $limit);
+        return $this->select($limit_sql);
+    }
+
+    public function page($sql, $page = 1, $size = 20)
+    {
+        $page = isset($page) ? max(intval($page), 1) : 1;
+        $size = max(intval($size), 1);
+        $offset = ($page - 1) * $size;
+        return $this->limit($sql, $size, $offset);
+    }
+
+    function select_db($dbname)
+    {
+        return true;
+    }
+
+    function list_fields($table)
+    {
+        static $result = array();
+
+        if (!is_array($result[$table])) {
+            if ($fields = $this->select("SELECT a.column_name,
+                data_type, decode(nullable, 'Y', 0,1) notnull,
+                data_default, decode(a.column_name, b.column_name, 1, 0) pk
+                FROM user_tab_columns a, (
+                    SELECT column_name
+                    FROM user_constraints c,user_cons_columns col
+                    WHERE c.constraint_name = col.constraint_name
+                    AND c.constraint_type='P'
+                    AND c.table_name = '" . strtoupper($table) . "'
+                ) b
+                WHERE table_name = '" . strtoupper($table) . "'
+                AND a.column_name = b.column_name(+)")
+            ) {
+                foreach ($fields as $field) {
+                    $result[$table][$field['COLUMN_NAME']] = array(
+                        'Field' => $field['COLUMN_NAME'],
+                        'Type' => $field['DATA_TYPE'],
+                        'Null' => $field['NOTNULL'] === 0 ? 'NO' : 'YES',
+                        'Key' => '',
+                        'Default' => $field['DATA_DEFAULT'],
+                        'Extra' => ''
+                    );
+                }
+            }
+        }
+
+        return $result[$table];
+    }
+
+    function list_tables()
+    {
+        static $result;
+
+        if (!is_array($result)) {
+            if ($tables = $this->select("SELECT TABLE_NAME FROM USER_TABLES ORDER BY TABLE_NAME ASC")) {
+                foreach ($tables as $table) {
+                    $result[] = $table['TABLE_NAME'];
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    function list_dbs()
+    {
+        return array(
+            $this->master['dbname']
+        );
+    }
+
+    function get_primary($table)
+    {
+        static $result = array();
+
+        if (!array_key_exists($table, $result)) {
+            $primarys = array();
+            foreach ($this->exec("SELECT *
+                FROM ALL_CONS_COLUMNS A
+                JOIN ALL_CONSTRAINTS C
+                    ON A.CONSTRAINT_NAME = C.CONSTRAINT_NAME
+                WHERE C.TABLE_NAME = '$table'
+                AND C.CONSTRAINT_TYPE = 'P'", true) as $primary) {
+                $primarys[] = $primary['COLUMN_NAME'];
+            }
+            $result[$table] = implode(',', $primarys);
+        }
+
+        return $result[$table];
+    }
+
+    function field_type($table, $field)
+    {
+        return false;
+    }
+
+    function version()
+    {
+        if (!$this->dbh()) return false;
+
+        return oci_server_version($this->dbh);
+    }
+
+    function prefix()
+    {
+        return $this->master['prefix'];
+    }
+
+    function error()
+    {
+        if (is_null($this->_error)) {
+            $this->_error = oci_error();
+            if ($this->_error && $this->charset != $this->_cmstop_charset) {
+                $this->_error = str_charset($this->charset, $this->_cmstop_charset, $this->_error);
+            }
+        }
+        return $this->_error;
+    }
+
+    protected function _prepare_sql($sql)
+    {
+        $this->sql = str_replace('#table_', $this->master['prefix'], trim($sql));
+        $this->sql = preg_replace('/(`(\w*)`)/Usim', '"$2"', $this->sql);
+        return $this->sql;
+    }
+
+    protected function _normorlize_charset($charset)
+    {
+        return strtolower(str_replace('-', '', $charset));
+    }
+
+    private function dbh($sql = null)
+    {
+        if (is_null($sql)) {
+            $this->sql = null;
+            if (is_null($this->dbh)) {
+                if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
+                $this->dbh = $this->dbh_master;
+            }
+            return $this->dbh;
+        }
+
+        self::$queries++;
+        $this->sql = str_replace('#table_', $this->master['prefix'], trim($sql));
+        if ($this->slaves && is_null($this->dbh_master) && stripos($this->sql, 'select') === 0) {
+            if (is_null($this->dbh_slave)) $this->dbh_slave = $this->connect_slave();
+            $this->dbh = $this->dbh_slave;
+        } else {
+            if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
+            $this->dbh = $this->dbh_master;
+        }
+        return $this->dbh;
+    }
+}

+ 209 - 0
Mall/Framework/Factory.Class.php

@@ -0,0 +1,209 @@
+<?php
+
+namespace Mall\Framework;
+
+use Mall\Framework\Cache\Redis;
+use Mall\Framework\Core\Cache;
+use Mall\Framework\Core\Config;
+use Mall\Framework\Core\Cookie;
+use Mall\Framework\Core\Db;
+use Mall\Framework\Core\File;
+use Mall\Framework\Core\RedisQueue;
+use Mall\Framework\Core\Request;
+use Mall\Framework\Core\View;
+use Mall\Framework\Core\Session;
+use Mall\Framework\Core\SendMail;
+use Mall\Framework\Core\SendSms;
+use Mall\Framework\Core\Swoole;
+use Mall\Framework\Core\Aes;
+use Mall\Framework\Core\Search;
+use Mall\Framework\Core\BaseImg;
+use Mall\Framework\Core\VerificationCode;
+use Mall\Framework\Core\Logs;
+
+
+abstract class Factory
+{
+    /**
+     * @param array $options
+     * @return Redis
+     * @throws \Exception
+     */
+    public static function cache($options = [])
+    {
+        $cacheConfig = self::config()->get('cache');
+        $options = $options ?: $cacheConfig['default'];
+
+        if (!$options) {
+            throw new \Exception('The Factory::cache need default cache config');
+        } else if (is_string($options)) {
+            $optionsData = $cacheConfig[$options];
+        }
+
+        if (!is_array($optionsData)) {
+            throw new \Exception('config file cache node '.$options.' is error');
+        }
+
+        return Cache::getInstance($optionsData);
+    }
+
+    public static function config()
+    {
+        return Config::getInstance();
+    }
+
+    public static function logs($logPath='', $logSaveFileApp='', $logSystem = '')
+    {
+        return Logs::getInstance($logPath, $logSaveFileApp, $logSystem);
+    }
+
+    public static function cookie($options = [])
+    {
+        $options = $options ?: self::config()->get('cookie');
+        if (!$options || !is_array($options)) {
+            throw new \Exception('The Factory::cookie need default cookie config');
+        }
+
+        return Cookie::getInstance($options);
+    }
+
+    public static function request()
+    {
+        return Request::getInstance();
+    }
+
+    public static function view($options = [])
+    {
+        $options = $options ?: self::config()->get('smarty');
+        if (!$options || !is_array($options)) {
+            throw new \Exception('The Factory::view need default smarty config');
+        }
+
+        return View::getInstance($options);
+    }
+
+    /**
+     * 另外一种直接修改php.ini 配置方式,配置项如下:
+     * session.save_handler = Redis
+     * session.save_path = “tcp://192.168.5.114:6379?auth=password&database=3”
+     */
+    public static function session($options = [])
+    {
+        $options = $options ?: self::config()->get('session');
+
+        if (!$options || !is_array($options)) {
+            throw new \Exception('The Factory::session need default session config');
+        }
+
+        foreach ($options as $k => &$v) {
+            if (empty($v)) {
+                unset($options[$k]);
+            }
+            if ($k == 'save_path') {
+                $options[$k] = urldecode($options[$k]);
+            }
+        }
+        Session::setOptions($options);
+
+        return Session::getInstance();
+    }
+
+    public static function sendmail()
+    {
+        return SendMail::getInstance();
+    }
+
+    /**
+     * 发送短信消息
+     *
+     * @return mixed
+     */
+    public static function sendSms()
+    {
+        return SendSms::getInstance();
+    }
+
+    public static function db($options = 'default')
+    {
+
+        $options = $options ?: self::config()->get('db');
+        if (!$options) {
+            throw new \Exception('The Factory::db need default db config');
+        } else if (is_string($options)) {
+            $dbconfig = self::config()->get('db');
+            $options = $dbconfig[$options];
+        }
+
+        if (!is_array($options)) {
+            throw new \Exception('The Factory::db config is error');
+        }
+
+        return Db::getInstance($options);
+    }
+
+    public static function swoole($options = [], $driver = 'Client')
+    {
+
+        if (!is_array($options) || empty($options)) {
+            throw new \Exception('Need default Swoole ' . $driver . ' config');
+        }
+
+        return Swoole::getInstance($options, $driver);
+    }
+
+    public static function aes($encryptKey = '')
+    {
+        return aes::getInstance($encryptKey);
+    }
+
+    public static function search($options = 'default')
+    {
+        $options = $options ?: self::config()->get('search');
+        if (!$options) {
+            throw new \Exception('The Factory::search need default search config');
+        } else if (is_string($options)) {
+            $dbconfig = self::config()->get('search');
+            $options = isset($dbconfig[$options]) ? $dbconfig[$options]: $dbconfig['default'];
+        }
+
+        if (!is_array($options)) {
+            throw new \Exception('The Factory::search config is error');
+        }
+
+        $search = new Search($options);
+
+        return $search->getConnection();
+    }
+
+    /**
+     * @return File
+     */
+    public static function baseImg()
+    {
+        return BaseImg::getInstance();
+    }
+
+    /**
+     * @param string $filename
+     * @param string $mode
+     * @return File
+     */
+    public static function file($filename = '', $mode = 'r')
+    {
+        return File::getInstance($filename, $mode);
+    }
+
+    public static function redisQueue(Redis $redis, $queueName = '')
+    {
+        return RedisQueue::getInstance($redis, $queueName);
+    }
+
+    public static function verificationCode()
+    {
+        return VerificationCode::getInstance();
+    }
+
+
+
+   
+}

+ 272 - 0
Mall/Framework/Mail/Mail.Class.php

@@ -0,0 +1,272 @@
+<?php
+
+namespace Mall\Framework\Mail;
+
+class Mail
+{
+    const PHP_QPRINT_MAXL=75;
+
+    public $mailer,
+        $delimiter,
+        $charset,
+        $from,
+        $sign,
+        $smtp_host,
+        $smtp_port,
+        $smtp_auth,
+        $smtp_username,
+        $smtp_password;
+
+    protected $errno;
+
+    protected $error;
+
+    function __construct($mailer = 1, $delimiter = 1, $charset = 'utf-8', $from = null, $sign = null, $smtp_host = null, $smtp_port = 25, $smtp_auth = true, $smtp_username = null, $smtp_password = null)
+    {
+        $this->mailer = $mailer;
+        $this->delimiter = $delimiter == 1 ? "\r\n" : ($delimiter == 2 ? "\r" : "\n");
+        $this->charset = $charset;
+        $this->from = $from;
+        $this->sign = $sign;
+        $this->smtp_host = $smtp_host;
+        $this->smtp_port = $smtp_port ? $smtp_port : 25;
+        $this->smtp_auth = $smtp_auth;
+        $this->smtp_username = $smtp_username;
+        $this->smtp_password = $smtp_password;
+    }
+
+    function execute($to, $subject, $message, $from = null)
+    {
+        $subject = '=?'.$this->charset.'?B?'.base64_encode(str_replace("\r", '', str_replace("\n", '', $subject))).'?=';
+        $message .= $this->sign;
+        $message = str_replace("\r\n.", " \r\n..", str_replace("\n", "\r\n", str_replace("\r", "\n", str_replace("\r\n", "\n", str_replace("\n\r", "\r", $message)))));
+        $message = $this->leading_dot_fixed_php_quot_print_encode($message);
+        $from = is_null($from) ? '=?'.$this->charset.'?B?'.base64_encode('MailGuang')."?= <$this->from>" : (preg_match('/^(.+?) \<(.+?)\>$/', $from, $m) ? '=?'.$this->charset.'?B?'.base64_encode($m[1])."?= <$m[2]>" : $from);
+        if (strpos($to, ','))
+        {
+            foreach(explode(',', $to) as $touser)
+            {
+                $tousers[] = preg_match('/^(.+?) \<(.+?)\>$/', $touser, $m) ? '=?'.$this->charset.'?B?'.base64_encode($m[1])."?= <$m[2]>" : $touser;
+            }
+            $to = implode(',', $tousers);
+        }
+        $headers = "From: $from{$this->delimiter}X-Priority: 3{$this->delimiter}X-Mailer: MailGuang mcloud{$this->delimiter}MIME-Version: 1.0{$this->delimiter}Content-type: text/html; charset=$this->charset{$this->delimiter}";
+        $headers .= "Content-Transfer-Encoding: quoted-printable{$this->delimiter}";
+
+        if ($this->mailer == 1)
+        {
+            return mail($to, $subject, $message, $headers);
+        }
+        elseif ($this->mailer == 2)
+        {
+            return $this->smtp($to, $subject, $message, $headers, $from);
+        }
+        else
+        {
+            return $this->mail($to, $subject, $message, $headers, $from);
+        }
+    }
+
+    function leading_dot_fixed_php_quot_print_encode($str)
+    {
+        $lp = 0;
+        $ret = '';
+        $hex = "0123456789ABCDEF";
+        $length = strlen($str);
+        $str_index = 0;
+
+        while ($length--) {
+            if ((($c = $str[$str_index++]) == "\015") && ($str[$str_index] == "\012") && $length > 0) {
+                $ret .= "\015";
+                $ret .= $str[$str_index++];
+                $length--;
+                $lp = 0;
+            } else {
+                if (ctype_cntrl($c)
+                    || (ord($c) == 0x7f)
+                    || (ord($c) & 0x80)
+                    || ($c == '=')
+                    || (($c == ' ') && ($str[$str_index] == "\015")))
+                {
+                    if (($lp += 3) > self::PHP_QPRINT_MAXL)
+                    {
+                        $ret .= '=';
+                        $ret .= "\015";
+                        $ret .= "\012";
+                        $lp = 3;
+                    }
+                    $ret .= '=';
+                    $ret .= $hex[ord($c) >> 4];
+                    $ret .= $hex[ord($c) & 0xf];
+                }
+                else
+                {
+                    if ((++$lp) > self::PHP_QPRINT_MAXL)
+                    {
+                        $ret .= '=';
+                        $ret .= "\015";
+                        $ret .= "\012";
+                        $lp = 1;
+                    }
+                    $ret .= $c;
+                    if($lp == 1 && $c == '.') {
+                        $ret .= '.';
+                        $lp++;
+                    }
+                }
+            }
+        }
+
+        return $ret;
+    }
+
+    function mail($to, $subject, $message, $headers, $from)
+    {
+        ini_set('SMTP', $this->smtp_host);
+        ini_set('smtp_port', $this->smtp_port);
+        ini_set('sendmail_from', $from);
+        return mail($to, $subject, $message, $headers);
+    }
+
+    function smtp($to, $subject, $message, $headers, $from)
+    {
+        if(!$fp = fsockopen($this->smtp_host, $this->smtp_port, $errno, $errstr, 30))
+        {
+            $this->errno = $errno;
+            $this->error = $errstr;
+            return false;
+        }
+
+        stream_set_blocking($fp, true);
+        $lastmessage = fgets($fp, 512);
+        if(substr($lastmessage, 0, 3) != '220')
+        {
+            $this->errno = substr($lastmessage, 0, 3);
+            $this->error = $lastmessage;
+            return false;
+        }
+
+        fputs($fp, ($this->smtp_auth ? 'EHLO' : 'HELO')." MailGuang\r\n");
+        $lastmessage = fgets($fp, 512);
+        if(substr($lastmessage, 0, 3) != 220 && substr($lastmessage, 0, 3) != 250)
+        {
+            $this->errno = substr($lastmessage, 0, 3);
+            $this->error = $lastmessage;
+            return false;
+        }
+
+        while(1)
+        {
+            if((substr($lastmessage,0, 3) != 220 && substr($lastmessage, 3, 1) != '-') || empty($lastmessage)) break;
+            $lastmessage = fgets($fp, 512);
+        }
+
+        if($this->smtp_auth)
+        {
+            fputs($fp, "AUTH LOGIN\r\n");
+            $lastmessage = fgets($fp, 512);
+            if(substr($lastmessage, 0, 3) != 334)
+            {
+                $this->errno = substr($lastmessage, 0, 3);
+                $this->error = $lastmessage;
+                return false;
+            }
+
+            fputs($fp, base64_encode($this->smtp_username)."\r\n");
+            $lastmessage = fgets($fp, 512);
+            if(substr($lastmessage, 0, 3) != 334)
+            {
+                $this->errno = substr($lastmessage, 0, 3);
+                $this->error = $lastmessage;
+                return false;
+            }
+
+            fputs($fp, base64_encode($this->smtp_password)."\r\n");
+            $lastmessage = fgets($fp, 512);
+            if(substr($lastmessage, 0, 3) != 235)
+            {
+                $this->errno = substr($lastmessage, 0, 3);
+                $this->error = $lastmessage;
+                return false;
+            }
+        }
+
+        fputs($fp, "MAIL FROM: <".preg_replace("/.*\<(.+?)\>.*/", "\\1", $from).">\r\n");
+        $lastmessage = fgets($fp, 512);
+        if(substr($lastmessage, 0, 3) != 250)
+        {
+            fputs($fp, "MAIL FROM: <".preg_replace("/.*\<(.+?)\>.*/", "\\1", $from).">\r\n");
+            $lastmessage = fgets($fp, 512);
+            if(substr($lastmessage, 0, 3) != 250)
+            {
+                $this->errno = substr($lastmessage, 0, 3);
+                $this->error = $lastmessage;
+                return false;
+            }
+        }
+
+        $email_tos = array();
+        foreach(explode(',', $to) as $touser)
+        {
+            $touser = trim($touser);
+            if($touser)
+            {
+                fputs($fp, "RCPT TO: <".preg_replace("/.*\<(.+?)\>.*/", "\\1", $touser).">\r\n");
+                $lastmessage = fgets($fp, 512);
+                if(substr($lastmessage, 0, 3) != 250)
+                {
+                    fputs($fp, "RCPT TO: <".preg_replace("/.*\<(.+?)\>.*/", "\\1", $touser).">\r\n");
+                    $lastmessage = fgets($fp, 512);
+                    $this->errno = substr($lastmessage, 0, 3);
+                    $this->error = $lastmessage;
+                    return false;
+                }
+            }
+        }
+
+        fputs($fp, "DATA\r\n");
+        $lastmessage = fgets($fp, 512);
+        if(substr($lastmessage, 0, 3) != 354)
+        {
+            $this->errno = substr($lastmessage, 0, 3);
+            $this->error = $lastmessage;
+            return false;
+        }
+
+        if (isset($_SERVER['HTTP_HOST'])) {
+            $host = $_SERVER['HTTP_HOST'];
+        } else if (isset($_SERVER['HOSTNAME'])) {
+            $host = $_SERVER['HOSTNAME'];
+        } else {
+            $host = '';
+        }
+
+        $headers .= 'Message-ID: <'.gmdate('YmdHs').'.'.substr(md5($message.microtime()), 0, 6).rand(100000, 999999).'@'.$host.">{$this->delimiter}";
+
+        fputs($fp, "Date: ".gmdate('r')."\r\n");
+        fputs($fp, "To: ".$to."\r\n");
+        fputs($fp, "Subject: ".$subject."\r\n");
+        fputs($fp, $headers."\r\n");
+        fputs($fp, "\r\n\r\n");
+        fputs($fp, "$message\r\n.\r\n");
+        $lastmessage = fgets($fp, 512);
+        if(substr($lastmessage, 0, 3) != 250)
+        {
+            $this->errno = substr($lastmessage, 0, 3);
+            $this->error = $lastmessage;
+            return false;
+        }
+        fputs($fp, "QUIT\r\n");
+        return true;
+    }
+
+    function error()
+    {
+        return $this->error;
+    }
+
+    function errno()
+    {
+        return $this->errno;
+    }
+}

+ 118 - 0
Mall/Framework/SearchClient/Bulk.Class.php

@@ -0,0 +1,118 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient;
+
+class Bulk {
+
+	private $client;
+	private $operations = array();
+
+    /**
+     * Construct a bulk operation
+     *
+     * @param \ElasticSearch\Client
+     */
+
+	public function __construct(Client $client) {
+		$this->client = $client;
+	}
+
+    /**
+     * commit this operation
+     */
+	public function commit() {
+		return $this->client->request('/_bulk', 'POST', $this->createPayload());
+	}
+	
+    /**
+     * reset this operation
+     */
+	public function reset() {
+		$this->operations = array();
+	}
+    
+    /**
+     * Index a new document or update it if existing
+     *
+     * @param array $document
+     * @param mixed $id Optional
+     * @param string $index Index
+     * @param string $type Type
+     * @param array $options Allow sending query parameters to control indexing further
+     *        _refresh_ *bool* If set to true, immediately refresh the shard after indexing
+     * @return \Mall\Framework\SearchClient
+     */
+	public function index($document, $id=false, $index, $type, array $options = array()) {
+		$params = array( '_id' => $id, 
+   					     '_index' => $index, 
+					     '_type' => $type);
+
+		foreach ($options as $key => $value) {
+			$params['_' . $key] = $value;
+		}
+
+		$operation = array(
+			array('index' => $params),
+			$document 
+		);
+		$this->operations[] = $operation;
+		return $this;
+	}
+
+	 /**
+     * delete a document
+     *
+     * @param mixed $id
+     * @param string $index Index
+     * @param string $type Type
+     * @param array $options Parameters to pass to delete action
+     * @return \Mall\Framework\SearchClient
+     */
+	public function delete($id=false, $index, $type, array $options = array()) {
+		$params = array( '_id' => $id, 
+   					     '_index' => $index, 
+					     '_type' => $type);
+
+		foreach ($options as $key => $value) {
+			$params['_' . $key] = $value;
+		}
+
+		$operation = array(
+			array('delete' => $params)
+		);
+		$this->operations[] = $operation;
+		return $this;
+
+	}
+
+	 /**
+	 * get all pending operations
+	 * @return array
+     */
+	public function getOperations() {
+		return $this->operations;		
+	}
+
+	 /**
+	 * count all pending operations
+	 * @return int
+     */
+	public function count() {
+		return count($this->operations);
+	}
+
+	 /**
+	 * create a request payload with all pending operations
+	 * @return string
+     */
+	public function createPayload() 
+	{
+		$payloads = array();
+		foreach ($this->operations as $operation) {
+			foreach ($operation as $partial) {
+				$payloads[] = json_encode($partial);
+			}
+		}
+		return join("\n", $payloads)."\n";
+	}
+}

+ 468 - 0
Mall/Framework/SearchClient/Client.Class.php

@@ -0,0 +1,468 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient;
+
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Client
+{
+    const DEFAULT_PROTOCOL = 'http';
+    const DEFAULT_SERVER = '127.0.0.1:9200';
+    const DEFAULT_INDEX = null;
+    const DEFAULT_TYPE = null;
+
+    protected $_config = array();
+
+    protected static $_defaults = array(
+        'protocol' => Client::DEFAULT_PROTOCOL,
+        'servers' => Client::DEFAULT_SERVER,
+        'index' => Client::DEFAULT_INDEX,
+        'type' => Client::DEFAULT_TYPE,
+        'timeout' => null,
+    );
+
+    protected static $_protocols = array(
+        'http' => 'Mall\\Framework\\SearchClient\\Transport\\HTTP'
+    );
+
+    /**
+     * @var Transport\Base
+     */
+    private $transport;
+
+    /**
+     * @var Bulk
+     */
+    private $bulk;
+
+    /**
+     * @var string
+     */
+    private $index;
+
+    /**
+     * @var string
+     */
+    private $type;
+
+    /**
+     * Construct search client
+     *
+     * @return \Mall\Framework\SearchClient\Client
+     * @param \Mall\Framework\SearchClient\Transport\Base $transport
+     * @param string $index
+     * @param string $type
+     */
+    public function __construct($transport, $index = null, $type = null)
+    {
+        $this->transport = $transport;
+        $this->setIndex($index);
+        $this->setType($type);
+    }
+
+    /**
+     * Get a client instance
+     * Defaults to opening a http transport connection to 127.0.0.1:9200
+     *
+     * @param string|array $config Allow overriding only the configuration bits you desire
+     *   - _transport_
+     *   - _host_
+     *   - _port_
+     *   - _index_
+     *   - _type_
+     * @throws \Exception
+     * @return \Mall\Framework\SearchClient\Client
+     */
+    public static function connection($config = array())
+    {
+        if (!$config && ($url = getenv('ELASTICSEARCH_URL'))) {
+            $config = $url;
+        }
+        if (is_string($config)) {
+            $config = self::parseDsn($config);
+        }
+
+        $config += self::$_defaults;
+
+        $protocol = $config['protocol'];
+        if (!isset(self::$_protocols[$protocol])) {
+            throw new \Exception("Tried to use unknown protocol: $protocol");
+        }
+        $class = self::$_protocols[$protocol];
+
+        if (null !== $config['timeout'] && !is_numeric($config['timeout'])) {
+            throw new \Exception("HTTP timeout should have a numeric value when specified.");
+        }
+
+        $server = is_array($config['servers']) ? $config['servers'][0] : $config['servers'];
+        list($host, $port) = explode(':', $server);
+
+        $transport = new $class($host, $port, $config['timeout']);
+
+        $client = new self($transport, $config['index'], $config['type']);
+        $client->config($config);
+        return $client;
+    }
+
+    /**
+     * @param array|null $config
+     * @return array|void
+     */
+    public function config($config = null)
+    {
+        if (!$config)
+            return $this->_config;
+        if (is_array($config))
+            $this->_config = $config + $this->_config;
+    }
+
+    /**
+     * Change what index to go against
+     * @return \Mall\Framework\SearchClient\Client
+     * @param mixed $index
+     */
+    public function setIndex($index)
+    {
+        if (is_array($index)) {
+            $index = implode(",", array_filter($index));
+        }
+        $this->index = $index;
+        $this->transport->setIndex($index);
+        return $this;
+    }
+
+    /**
+     * Get current index
+     *
+     * @return string
+     */
+    public function getIndex()
+    {
+        return $this->index;
+    }
+
+    /**
+     * Change what types to act against
+     * @return \Mall\Framework\SearchClient\Client
+     * @param mixed $type
+     */
+    public function setType($type)
+    {
+        if (is_array($type))
+            $type = implode(",", array_filter($type));
+        $this->type = $type;
+        $this->transport->setType($type);
+        return $this;
+    }
+
+    /**
+     * Get current type
+     *
+     * @return string
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * Fetch a document by its id
+     *
+     * @return array
+     * @param mixed $id Optional
+     * @param bool $verbose
+     */
+    public function get($id, $verbose = false)
+    {
+        return $this->request($id, "GET", false, $verbose);
+    }
+
+    /**
+     * Puts a mapping on index
+     *
+     * @param array|object $mapping
+     * @param array $config
+     * @throws Exception
+     * @return array
+     */
+    public function map($mapping, array $config = array())
+    {
+        if (is_array($mapping)) $mapping = new Mapping($mapping);
+        $mapping->config($config);
+
+        try {
+            $type = $mapping->config('type');
+        } catch (\Exception $e) {
+        } // No type is cool
+        if (isset($type) && !$this->passesTypeConstraint($type)) {
+            throw new Exception("Cant create mapping due to type constraint mismatch");
+        }
+
+        return $this->request('_mapping', 'PUT', $mapping->export(), true);
+    }
+
+    protected function passesTypeConstraint($constraint)
+    {
+        if (is_string($constraint)) $constraint = array($constraint);
+        $currentType = explode(',', $this->type);
+        $includeTypes = array_intersect($constraint, $currentType);
+        return ($constraint && count($includeTypes) === count($constraint));
+    }
+
+    /**
+     * 更新文档中的字段值
+     * @param array $data 要更新的字段内容
+     * @param int $id 要更新的文档id
+     * @return array
+     */
+    public function updateFieldVaule($data, $id)
+    {
+        $updateData['doc'] = $data;
+        $searchUpdateData = json_encode($updateData);
+        return $this->request($id.'/_update', 'POST', $searchUpdateData);
+    }
+
+    /**
+     * 批量更新文档中的字段值
+     * @param array $data 要更新的字段内容
+     * @param int $id 要更新的文档id
+     * @return array
+     */
+    public function batchUpdateFieldVaule($data, $query)
+    {
+        $start = microtime(true);
+        $result = $this->transport->batchUpdateFieldVaule($data, $query);
+        $result['time'] = microtime(true) - $start;
+        return $result;
+    }
+
+    /**
+     * Perform a raw request
+     *
+     * Usage example
+     *
+     *     $response = $client->request('_status', 'GET');
+     *
+     * @return array
+     * @param mixed $path Request path to use.
+     *     `type` is prepended to this path inside request
+     * @param string $method HTTP verb to use
+     * @param mixed $payload Array of data to be json-encoded
+     * @param bool $verbose Controls response data, if `false`
+     *     only `_source` of response is returned
+     */
+    public function request($path, $method = 'GET', $payload = false, $verbose = false, $buildPath = true)
+    {
+        $path = $buildPath ? $this->expandPath($path) : $path;
+        $response = $this->transport->request($path, $method, $payload, $buildPath);
+        return ($verbose || !isset($response['_source']))
+            ? $response
+            : $response['_source'];
+    }
+
+    /**
+     * Index a new document or update it if existing
+     *
+     * @return array
+     * @param array $document
+     * @param mixed $id Optional
+     * @param array $options Allow sending query parameters to control indexing further
+     *        _refresh_ *bool* If set to true, immediately refresh the shard after indexing
+     */
+    public function index($document, $id = false, array $options = array())
+    {
+        if ($this->bulk) {
+            return $this->bulk->index($document, $id, $this->index, $this->type, $options);
+        }
+        return $this->transport->index($document, $id, $options);
+    }
+
+    /**
+     * Perform search, this is the sweet spot
+     *
+     * @return array
+     * @param $query
+     * @param array $options
+     */
+    public function search($query, array $options = array())
+    {
+        $start = microtime(true);
+        $result = $this->transport->search($query, $options);
+        $result['time'] = microtime(true) - $start;
+        return $result;
+    }
+
+    /**
+     * Perform scrollSearch
+     * @param $query
+     * @param string $scroll
+     * @param int $size
+     * @param array $options
+     * @return mixed
+     */
+    public function scrollSearch($query, $scroll = '10s', $size = 2000, array $options = array())
+    {
+        $start = microtime(true);
+        $result = $this->transport->scrollSearch($query, $scroll, $size, $options);
+        $result['time'] = microtime(true) - $start;
+        return $result;
+    }
+
+    /**
+     * Flush this index/type combination
+     *
+     * @return array
+     * @param mixed $id If id is supplied, delete that id for this index
+     *                  if not wipe the entire index
+     * @param array $options Parameters to pass to delete action
+     */
+    public function delete($id = false, array $options = array())
+    {
+        if ($this->bulk) {
+            return $this->bulk->delete($id, $this->index, $this->type, $options);
+        }
+        return $this->transport->delete($id, $options);
+    }
+
+    /**
+     * Flush this index/type combination
+     *
+     * @return array
+     * @param mixed $query Text or array based query to delete everything that matches
+     * @param array $options Parameters to pass to delete action
+     */
+    public function deleteByQuery($query, array $options = array())
+    {
+        return $this->transport->deleteByQuery($query, $options);
+    }
+
+    /**
+     * Perform refresh of current indexes
+     *
+     * @return array
+     */
+    public function refresh()
+    {
+        return $this->transport->request(array('_refresh'), 'GET');
+    }
+
+    /**
+     * Expand a given path (array or string)
+     * If this is not an absolute path index + type will be prepended
+     * If it is an absolute path it will be used as is
+     *
+     * @param mixed $path
+     * @return array
+     */
+    protected function expandPath($path)
+    {
+        $path = (array)$path;
+        $isAbsolute = $path[0][0] === '/';
+
+        return $isAbsolute
+            ? $path
+            : array_merge((array)$this->type, $path);
+    }
+
+    /**
+     * Parse a DSN string into an associative array
+     *
+     * @param string $dsn
+     * @return array
+     */
+    protected static function parseDsn($dsn)
+    {
+        $parts = parse_url($dsn);
+        $protocol = $parts['scheme'];
+        $servers = $parts['host'] . ':' . $parts['port'];
+        if (isset($parts['path'])) {
+            $path = explode('/', $parts['path']);
+            list($index, $type) = array_values(array_filter($path));
+        }
+        return compact('protocol', 'servers', 'index', 'type');
+    }
+
+    /**
+     * Create a bulk-transaction
+     *
+     * @return Bulk
+     */
+    public function createBulk()
+    {
+        return new Bulk($this);
+    }
+
+    /**
+     * 增加Index
+     *
+     * @param $index
+     * @param $mappings
+     * @return mixed
+     */
+    public function createBase($index, $mappings)
+    {
+        return $this->transport->createBase($index, $mappings);
+    }
+
+    /**
+     * 删除Index
+     *
+     * @param $index
+     * @return mixed
+     */
+    public function deleteBase($index)
+    {
+        return $this->transport->deleteBase($index);
+    }
+
+    /**
+     * Begin a transparent bulk-transaction
+     * if one is already running, return its handle
+     * @return Bulk
+     */
+
+    public function beginBulk()
+    {
+        if (!$this->bulk) {
+            $this->bulk = $this->createBulk();
+        }
+        return $this->bulk;
+    }
+
+    /**
+     * @see beginBulk
+     */
+    public function begin()
+    {
+        return $this->beginBulk();
+    }
+
+    /**
+     * commit a bulk-transaction
+     * @return array
+     */
+
+    public function commitBulk()
+    {
+        if ($this->bulk) {
+            $result = $this->bulk->commit();
+            $this->bulk = null;
+            return $result;
+        }
+    }
+
+    /**
+     * @see commitBulk
+     */
+    public function commit()
+    {
+        return $this->commitBulk();
+    }
+
+}

+ 79 - 0
Mall/Framework/SearchClient/DSL/Builder.Class.php

@@ -0,0 +1,79 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient\DSL;
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Helper stuff for working with the ElasticSearch DSL
+ * How to build a mildly complex query:
+ * $dsl = new ElasticSearchDSL;
+ * $bool = $dsl->bool(); // Return a new bool structure
+ *
+ * @author Raymond Julin <raymond.julin@gmail.com>
+ * @package ElasticSearchClient
+ * @since 0.1
+ * Created: 2010-07-23
+ */
+class Builder {
+
+    protected $dsl = array();
+
+    private $explain = null;
+    private $from = null;
+    private $size = null;
+    private $fields = null;
+    private $query = null;
+    private $facets = null;
+    private $sort = null;
+
+    /**
+     * Construct DSL object
+     *
+     * @return \Mall\Framework\SearchClient\DSL\Builder
+     * @param array $options
+     */
+    public function __construct(array $options=array()) {
+        foreach ($options as $key => $value)
+            $this->$key = $value;
+    }
+
+    /**
+     * Add array clause, can only be one
+     *
+     * @return \Mall\Framework\SearchClient\DSL\Query
+     * @param array $options
+     */
+    public function query(array $options=array()) {
+        if (!($this->query instanceof Query))
+            $this->query = new Query($options);
+        return $this->query;
+    }
+
+    /**
+     * Build the DSL as array
+     *
+     * @throws \ElasticSearch\Exception
+     * @return array
+     */
+    public function build() {
+        $built = array();
+        if ($this->from != null)
+            $built['from'] = $this->from;
+        if ($this->size != null)
+            $built['size'] = $this->size;
+        if ($this->sort && is_array($this->sort))
+            $built['sort'] = $this->sort;
+        if (!$this->query)
+            throw new \ElasticSearch\Exception("Query must be specified");
+        else
+            $built['query'] = $this->query->build();
+        return $built;
+    }
+}

+ 94 - 0
Mall/Framework/SearchClient/DSL/Query.Class.php

@@ -0,0 +1,94 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient\DSL;
+
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Handle the query sub dsl 
+ *
+ * @author Raymond Julin <raymond.julin@gmail.com>
+ * @package ElasticSearchClient
+ * @since 0.1
+ * Created: 2010-07-24
+ */
+class Query {
+    protected $term = null;
+    /**
+     * @var RangeQuery
+     */
+    protected $range;
+    protected $prefix = null;
+    protected $wildcard = null;
+    protected $matchAll = null;
+    protected $queryString = null;
+    protected $bool = null;
+    protected $disMax = null;
+    protected $constantScore = null;
+    protected $filteredQuery = null;
+
+    public function __construct(array $options=array()) {
+    }
+
+    /**
+     * Add a term to this query
+     *
+     * @return \Mall\Framework\SearchClient\DSL\Query
+     * @param string $term
+     * @param bool|string $field
+     */
+    public function term($term, $field=false) {
+        $this->term = ($field)
+            ? array($field => $term)
+            : $term;
+        return $this;
+    }
+
+    /**
+     * Add a wildcard to this query
+     *
+     * @return \Mall\Framework\SearchClient\DSL\Query
+     * @param $val
+     * @param bool|string $field
+     */
+    public function wildcard($val, $field=false) {
+        $this->wildcard = ($field)
+            ? array($field => $val)
+            : $val;
+        return $this;
+    }
+    
+    /**
+     * Add a range query
+     *
+     * @return \Mall\Framework\SearchClient\DSL\RangeQuery
+     * @param array $options
+     */
+    public function range(array $options=array()) {
+        $this->range = new RangeQuery($options);
+        return $this->range;
+    }
+
+    /**
+     * Build the DSL as array
+     *
+     * @return array
+     */
+    public function build() {
+        $built = array();
+        if ($this->term)
+            $built['term'] = $this->term;
+        elseif ($this->range)
+            $built['range'] = $this->range->build();
+        elseif ($this->wildcard)
+            $built['wildcard'] = $this->wildcard;
+        return $built;
+    }
+}

+ 117 - 0
Mall/Framework/SearchClient/DSL/RangeQuery.Class.php

@@ -0,0 +1,117 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient\DSL;
+
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Range queries
+ *
+ * @author Raymond Julin <raymond.julin@gmail.com>
+ * @package ElasticSearchClient
+ * @since 0.1
+ * Created: 2010-07-24
+ */
+class RangeQuery {
+    protected $fieldname = null;
+    protected $from = null;
+    protected $to = null;
+    protected $includeLower = null;
+    protected $includeUpper = null;
+    protected $boost = null;
+
+    
+    /**
+     * Construct new RangeQuery component
+     *
+     * @return \Mall\Framework\SearchClient\DSL\RangeQuery
+     * @param array $options
+     */
+    public function __construct(array $options=array()) {
+        $this->fieldname = key($options);
+        $values = current($options);
+        if (is_array($values)) {
+            foreach ($values as $key => $val)
+                $this->$key = $val;
+        }
+    }
+    
+    /**
+     * Setters
+     *
+     * @return \Mall\Framework\SearchClient\DSL\RangeQuery
+     * @param mixed $value
+     */
+    public function fieldname($value) {
+        $this->fieldname = $value;
+        return $this;
+    }
+
+    /**
+     * @param $value
+     * @return \Mall\Framework\SearchClient\DSL\RangeQuery $this
+     */
+    public function from($value) {
+        $this->from = $value;
+        return $this;
+    }
+    /**
+     * @param $value
+     * @return \Mall\Framework\SearchClient\DSL\RangeQuery $this
+     */
+    public function to($value) {
+        $this->to = $value;
+        return $this;
+    }
+    /**
+     * @param $value
+     * @return \Mall\Framework\SearchClient\DSL\RangeQuery $this
+     */
+    public function includeLower($value) {
+        $this->includeLower = $value;
+        return $this;
+    }
+    /**
+     * @param $value
+     * @return \Mall\Framework\SearchClient\DSL\RangeQuery $this
+     */
+    public function includeUpper($value) {
+        $this->includeUpper = $value;
+        return $this;
+    }
+    /**
+     * @param $value
+     * @return \Mall\Framework\SearchClient\DSL\RangeQuery $this
+     */
+    public function boost($value) {
+        $this->boost = $value;
+        return $this;
+    }
+
+    /**
+     * Build to array
+     *
+     * @throws \Mall\Framework\SearchClient\Exception
+     * @return array
+     */
+    public function build() {
+        $built = array();
+        if ($this->fieldname) {
+            $built[$this->fieldname] = array();
+            foreach (array("from","to","includeLower","includeUpper", "boost") as $opt) {
+                if ($this->$opt !== null)
+                    $built[$this->fieldname][$opt] = $this->$opt;
+            }
+            if (count($built[$this->fieldname]) == 0)
+                throw new \ElasticSearch\Exception("Empty RangeQuery cant be created");
+        }
+        return $built;
+    }
+}

+ 117 - 0
Mall/Framework/SearchClient/DSL/Stringify.Class.php

@@ -0,0 +1,117 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient\DSL;
+
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Parse a DSL object into a string based representation
+ * Return string representation of DSL for search.
+ * This will remove certain fields that are not supported
+ * in a string representation
+ *
+ * @author Raymond Julin <raymond.julin@gmail.com>
+ * @package ElasticSearch
+ * @since 0.1
+ * Created: 2010-07-24
+ */
+class Stringify {
+
+    protected $dsl = array();
+    
+    public function __construct(array $dsl) {
+        $this->dsl = $dsl;
+    }
+
+    public function __toString() {
+        $dsl = $this->dsl;
+        $query = $dsl['query'];
+
+        $string = "";
+        if (array_key_exists("term", $query))
+            $string .= $this->transformDSLTermToString($query['term']);
+        if (array_key_exists("wildcard", $query))
+            $string .= $this->transformDSLTermToString($query['wildcard']);
+        if (array_key_exists("sort", $dsl))
+            $string .= $this->transformDSLSortToString($dsl['sort']);
+        if (array_key_exists("fields", $dsl))
+            $string .= $this->transformDSLFieldsToString($dsl['fields']);
+        return $string;
+    }
+
+    /**
+     * A naive transformation of possible term and wildcard arrays in a DSL
+     * query
+     *
+     * @return string
+     * @param mixed $dslTerm
+     */
+    protected function transformDSLTermToString($dslTerm) {
+        $string = "";
+        if (is_array($dslTerm)) {
+            $key = key($dslTerm);
+            $value = $dslTerm[$key];
+            if (is_string($key))
+                $string .= "$key:";
+        }
+        else
+            $value = $dslTerm;
+        /**
+         * If a specific key is used as key in the array
+         * this should translate to searching in a specific field (field:term)
+         */
+        if (strpos($value, " ") !== false)
+            $string .= '"' . $value . '"';
+        else
+            $string .= $value;
+        return $string;
+    }
+
+    /**
+     * Transform search parameters to string
+     *
+     * @return string
+     * @param mixed $dslSort
+     */
+    protected function transformDSLSortToString($dslSort) {
+        $string = "";
+        if (is_array($dslSort)) {
+            foreach ($dslSort as $sort) {
+                if (is_array($sort)) {
+                    $field = key($sort);
+                    $info = current($sort);
+                }
+                else
+                    $field = $sort;
+                $string .= "&sort=" . $field;
+                if (isset($info)) {
+                    if (is_string($info) && $info == "desc")
+                        $string .= ":reverse";
+                    elseif (is_array($info) && array_key_exists("reverse", $info) && $info['reverse'])
+                        $string .= ":reverse";
+                }
+            }
+        }
+        return $string;
+    }
+
+    /**
+     * Transform a selection of fields to return to string form
+     *
+     * @return string
+     * @param mixed $dslFields
+     */
+    protected function transformDSLFieldsToString($dslFields) {
+        $string = "";
+        if (is_array($dslFields))
+            $string .= "&fields=" . join(",", $dslFields);
+        return $string;
+    }
+}

+ 14 - 0
Mall/Framework/SearchClient/Exception.Class.php

@@ -0,0 +1,14 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient;
+
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Exception extends \Exception {
+}

+ 73 - 0
Mall/Framework/SearchClient/Mapping.Class.php

@@ -0,0 +1,73 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient;
+
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Mapping {
+
+    protected $properties = array();
+    protected $config = array();
+
+    /**
+     * Build mapping data
+     *
+     * @param array $properties
+     * @param array $config
+     * @return Mapping
+     */
+    public function __construct(array $properties = array(), array $config = array()) {
+        $this->properties = $properties;
+        $this->config = $config;
+    }
+
+    /**
+     * Export mapping data as a json-ready array
+     *
+     * @return string
+     */
+    public function export() {
+        return array(
+            'properties' => $this->properties
+        );
+    }
+
+    /**
+     * Add or overwrite existing field by name
+     *
+     * @param string $field
+     * @param string|array $config
+     * @return $this
+     */
+    public function field($field, $config = array()) {
+        if (is_string($config)) $config = array('type' => $config);
+        $this->properties[$field] = $config;
+        return $this;
+    }
+
+    /**
+     * Get or set a config
+     *
+     * @param string $key
+     * @param mixed $value
+     * @throws \Exception
+     * @return array|void
+     */
+    public function config($key, $value = null) {
+        if (is_array($key))
+            $this->config = $key + $this->config;
+        else {
+            if ($value !== null) $this->config[$key] = $value;
+            if (!isset($this->config[$key]))
+                throw new \Exception("Configuration key `type` is not set");
+            return $this->config[$key];
+        }
+    }
+}

+ 148 - 0
Mall/Framework/SearchClient/Transport/Base.Class.php

@@ -0,0 +1,148 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient\Transport;
+
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+abstract class Base
+{
+
+    /**
+     * What host to connect to for server
+     * @var string
+     */
+    protected $host = "";
+
+    /**
+     * Port to connect on
+     * @var int
+     */
+    protected $port = 9200;
+
+    /**
+     * ElasticSearch index
+     * @var string
+     */
+    protected $index;
+
+    /**
+     * ElasticSearch document type
+     * @var string
+     */
+    protected $type;
+
+    /**
+     * Default constructor, just set host and port
+     * @param string $host
+     * @param int $port
+     */
+    public function __construct($host, $port)
+    {
+        $this->host = $host;
+        $this->port = $port;
+    }
+
+    /**
+     * Method for indexing a new document
+     *
+     * @param array|object $document
+     * @param mixed $id
+     * @param array $options
+     */
+    abstract public function index($document, $id = false, array $options = array());
+
+    /**
+     * Perform a request against the given path/method/payload combination
+     * Example:
+     * $es->request('/_status');
+     *
+     * @param string|array $path
+     * @param string $method
+     * @param array|bool $payload
+     * @return
+     */
+    abstract public function request($path, $method = "GET", $payload = false, $buildPath = true);
+
+    /**
+     * Delete a document by its id
+     * @param mixed $id
+     * @param array $options
+     */
+    abstract public function delete($id = false, array $options = array());
+
+    /**
+     * Perform a search based on query
+     * @param array|string $query
+     * @param array|string $options
+     */
+    abstract public function search($query, array $options = array());
+
+    abstract public function scrollSearch($query, $scroll = '10s', $size = 2000, array $options = array());
+
+    abstract public function batchUpdateFieldVaule($data, $query);
+
+    abstract public function createBase($index, $mappings);
+
+    abstract public function deleteBase($index);
+
+    /**
+     * Search
+     *
+     * @return array
+     * @param mixed $query String or array to use as criteria for delete
+     * @param array $options Parameters to pass to delete action
+     * @throws \Mall\Framework\Search
+     */
+    public function deleteByQuery($query, array $options = array())
+    {
+        throw new \Mall\Framework\SearchClient\Exception(__FUNCTION__ . ' not implemented for ' . __CLASS__);
+    }
+
+    /**
+     * Set what index to act against
+     * @param string $index
+     */
+    public function setIndex($index)
+    {
+        $this->index = $index;
+    }
+
+    /**
+     * Set what document types to act against
+     * @param string $type
+     */
+    public function setType($type)
+    {
+        $this->type = $type;
+    }
+
+    /**
+     * Build a callable url
+     *
+     * @return string
+     * @param array|bool $path
+     * @param array $options Query parameter options to pass
+     */
+    protected function buildUrl($path = false, array $options = array())
+    {
+        $isAbsolute = (is_array($path) ? isset($path[0][0])?$path[0][0] : '' : $path[0]) === '/';
+        $url = $isAbsolute ? '' : "/" . $this->index;
+
+        if ($path && is_array($path) && count($path) > 0)
+            $url .= "/" . implode("/", array_filter($path));
+        if (substr($url, -1) == "/")
+            $url = substr($url, 0, -1);
+        if (count($options) > 0)
+            $url .= "?" . http_build_query($options, '', '&');
+
+        return $url;
+    }
+
+}

+ 346 - 0
Mall/Framework/SearchClient/Transport/HTTP.Class.php

@@ -0,0 +1,346 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient\Transport;
+
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+if (!defined('CURLE_OPERATION_TIMEDOUT'))
+    define('CURLE_OPERATION_TIMEDOUT', 28);
+
+
+class HTTP extends Base
+{
+
+    /**
+     * How long before timing out CURL call
+     */
+    private $timeout = 10;
+
+    /**
+     * curl handler which is needed for reusing existing http connection to the server
+     * @var resource
+     */
+    protected $ch;
+
+    public function __construct($host = 'localhost', $port = 9200, $timeout = null)
+    {
+        parent::__construct($host, $port);
+        if (null !== $timeout) {
+            $this->setTimeout($timeout);
+        }
+        $this->ch = curl_init();
+    }
+
+    /**
+     * Index a new document or update it if existing
+     *
+     * @return array
+     * @param array $document
+     * @param mixed $id Optional
+     * @param array $options
+     */
+    public function index($document, $id = false, array $options = array())
+    {
+        $url = $this->buildUrl(array($this->type, $id), $options);
+        $method = ($id == false) ? "POST" : "PUT";
+        return $this->call($url, $method, $document);
+    }
+
+    /**
+     * Search
+     *
+     * @return array
+     * @param array|string $query
+     * @param array $options
+     */
+    public function search($query, array $options = array())
+    {
+        $result = false;
+        if (is_array($query)) {
+            /**
+             * Array implies using the JSON query DSL
+             */
+            $arg = "_search";
+            if (isset($options['routing'])) {
+                $arg = "_search?routing=" . $options['routing'];
+            }
+
+            $url = $this->buildUrl(array(
+                $this->type, $arg
+            ));
+
+            $result = $this->call($url, "GET", $query);
+        } elseif (is_string($query)) {
+            /**
+             * String based search means http query string search
+             */
+            $url = $this->buildUrl(array(
+                $this->type, "_search?q=" . $query
+            ));
+            $result = $this->call($url, "POST", $options);
+        }
+        return $result;
+    }
+
+    /**
+     * ScrollSearch
+     * @param $query
+     * @param string $scroll
+     * @param $size
+     * @param array $options
+     * @return array|bool
+     * @throws HTTPException
+     */
+    public function scrollSearch($query, $scroll = '10s', $size = 2000, array $options = array())
+    {
+        $result = false;
+        $arg = "_search?scroll=".$scroll.'&size='.$size;
+        if (is_array($query)) {
+
+            if (isset($options['routing'])) {
+                $arg .= "&routing=" . $options['routing'];
+            }
+
+            $url = $this->buildUrl(array(
+                $this->type, $arg
+            ));
+
+            $result = $this->call($url, "GET", $query);
+        } elseif (is_string($query)) {
+            /**
+             * String based search means http query string search
+             */
+            $url = $this->buildUrl(array(
+                $this->type, $arg."&q=" . $query
+            ));
+            $result = $this->call($url, "POST", $options);
+        }
+        return $result;
+    }
+
+    public function batchUpdateFieldVaule($data, $query)
+    {
+        $url = $this->buildUrl(array(
+            $this->type, "_update_by_query"
+        ));
+
+        $field_str = "";
+        $condition = "";
+        foreach ($data as $field => $v) {
+            if(!empty($field_str)) {
+                $condition = ";";
+            }
+            $field_str .= $condition . "ctx._source.$field = params.$field";
+        }
+
+        $update = [
+            'script' => [
+                'lang' => "painless",
+                'inline' => $field_str,
+                "params" => $data
+            ]
+        ];
+
+        $result = $this->call($url, "POST", array_merge($query, $update));
+
+        return $result;
+    }
+
+    /**
+     * 增加站点索引Index
+     *
+     * @param $index
+     * @param $mappings
+     * @return array|bool
+     * @throws HTTPException
+     * @author feng
+     */
+    public function createBase($index, $mappings)
+    {
+        $result = false;
+        if (is_string($index)) {
+            $url = '/' . $index;
+            $result = $this->call($url, 'PUT', $mappings);
+        }
+        return $result;
+    }
+
+    /**
+     * 删除站点索引Index
+     *
+     * @param $index
+     * @return array|bool
+     * @throws HTTPException
+     */
+    public function deleteBase($index)
+    {
+        $result = false;
+        if (is_string($index)) {
+            $url = '/' . $index;
+            $result = $this->call($url, 'DELETE');
+        }
+        return $result;
+    }
+
+    /**
+     * Search
+     *
+     * @return array
+     * @param mixed $query
+     * @param array $options Parameters to pass to delete action
+     */
+    public function deleteByQuery($query, array $options = array())
+    {
+        $options += array(
+            'refresh' => true
+        );
+        if (is_array($query)) {
+            /**
+             * Array implies using the JSON query DSL
+             */
+            $url = $this->buildUrl(array($this->type, "_query"));
+            $result = $this->call($url, "DELETE", $query);
+        } elseif (is_string($query)) {
+            /**
+             * String based search means http query string search
+             */
+            $url = $this->buildUrl(array($this->type, "_query"), array('q' => $query));
+            $result = $this->call($url, "DELETE");
+        }
+        if ($options['refresh']) {
+            $this->request('_refresh', "POST");
+        }
+        return !isset($result['error']);
+    }
+
+    /**
+     * Perform a request against the given path/method/payload combination
+     * Example:
+     * $es->request('/_status');
+     *
+     * @param string|array $path
+     * @param string $method
+     * @param array|bool $payload
+     * @param bool $buildUrl
+     * @return array
+     */
+    public function request($path, $method = "GET", $payload = false, $buildUrl = true)
+    {
+        $path = $buildUrl ? $this->buildUrl($path) : $path;
+        return $this->call($path, $method, $payload);
+    }
+
+    /**
+     * Flush this index/type combination
+     *
+     * @return array
+     * @param mixed $id Id of document to delete
+     * @param array $options Parameters to pass to delete action
+     */
+    public function delete($id = false, array $options = array())
+    {
+        if ($id)
+            return $this->call($this->buildUrl(array($this->type, $id), $options), "DELETE");
+        else
+            return $this->request(false, "DELETE");
+    }
+
+    /**
+     * Perform a http call against an url with an optional payload
+     *
+     * @return array
+     * @param string $url
+     * @param string $method (GET/POST/PUT/DELETE)
+     * @param array|bool $payload The document/instructions to pass along
+     * @throws HTTPException
+     */
+    protected function call($url, $method = "GET", $payload = null)
+    {
+        $conn = $this->ch;
+        $protocol = "http";
+        $requestURL = $protocol . "://" . $this->host . ':' . $this->port . $url;
+
+        // 设置header需要发送的参数
+        $header = ['Content-Type:application/json'];
+        curl_setopt($conn, CURLOPT_HTTPHEADER  , $header);
+        curl_setopt($conn, CURLOPT_URL, $requestURL);
+        curl_setopt($conn, CURLOPT_TIMEOUT, $this->timeout);
+        curl_setopt($conn, CURLOPT_PORT, $this->port);
+        curl_setopt($conn, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($conn, CURLOPT_CUSTOMREQUEST, strtoupper($method));
+        curl_setopt($conn, CURLOPT_FORBID_REUSE, 0);
+
+        if (is_array($payload) && count($payload) > 0)
+            curl_setopt($conn, CURLOPT_POSTFIELDS, json_encode($payload));
+        else
+            curl_setopt($conn, CURLOPT_POSTFIELDS, $payload);
+
+        $response = curl_exec($conn);
+        if ($response !== false) {
+            $data = json_decode($response, true);
+            if (!$data) {
+                $data = array('error' => $response, "code" => curl_getinfo($conn, CURLINFO_HTTP_CODE));
+            }
+        } else {
+            /**
+             * cUrl error code reference can be found here:
+             * http://curl.haxx.se/libcurl/c/libcurl-errors.html
+             */
+            $errno = curl_errno($conn);
+            switch ($errno) {
+                case CURLE_UNSUPPORTED_PROTOCOL:
+                    $error = "Unsupported protocol [$protocol]";
+                    break;
+                case CURLE_FAILED_INIT:
+                    $error = "Internal cUrl error?";
+                    break;
+                case CURLE_URL_MALFORMAT:
+                    $error = "Malformed URL [$requestURL] -d " . json_encode($payload);
+                    break;
+                case CURLE_COULDNT_RESOLVE_PROXY:
+                    $error = "Couldnt resolve proxy";
+                    break;
+                case CURLE_COULDNT_RESOLVE_HOST:
+                    $error = "Couldnt resolve host";
+                    break;
+                case CURLE_COULDNT_CONNECT:
+                    $error = "Couldnt connect to host [{$this->host}], ElasticSearch down?";
+                    break;
+                case CURLE_OPERATION_TIMEDOUT:
+                    $error = "Operation timed out on [$requestURL]";
+                    break;
+                default:
+                    $error = "Unknown error";
+                    if ($errno == 0)
+                        $error .= ". Non-cUrl error";
+                    break;
+            }
+            $exception = new HTTPException($error);
+            $exception->payload = $payload;
+            $exception->port = $this->port;
+            $exception->protocol = $protocol;
+            $exception->host = $this->host;
+            $exception->method = $method;
+            throw $exception;
+        }
+
+        return $data;
+    }
+
+    public function setTimeout($timeout)
+    {
+        $this->timeout = $timeout;
+    }
+
+    public function getTimeout()
+    {
+        return $this->timeout;
+    }
+}

+ 59 - 0
Mall/Framework/SearchClient/Transport/HTTPException.Class.php

@@ -0,0 +1,59 @@
+<?php // vim:set ts=4 sw=4 et:
+
+namespace Mall\Framework\SearchClient\Transport;
+
+/**
+ * This file is part of the ElasticSearch PHP client
+ *
+ * (c) Raymond Julin <raymond.julin@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class HTTPException extends \Exception {
+    /**
+     * Exception data
+     * @var array
+     */
+    protected $data = array(
+        'payload' => null,
+        'protocol' => null,
+        'port' => null,
+        'host' => null,
+        'url' => null,
+        'method' => null,
+    );
+
+    /**
+     * Setter
+     * @param mixed $key
+     * @param mixed $value
+     */
+    public function __set($key, $value) {
+        if (array_key_exists($key, $this->data))
+            $this->data[$key] = $value;
+    }
+
+    /**
+     * Getter
+     * @param mixed $key
+     * @return mixed
+     */
+    public function __get($key) {
+        if (array_key_exists($key, $this->data))
+            return $this->data[$key];
+        else
+            return false;
+    }
+
+    /**
+     * Rebuild CLI command using curl to further investigate the failure
+     * @return string
+     */
+    public function getCLICommand() {
+        $postData = json_encode($this->payload);
+        $curlCall = "curl -X{$method} 'http://{$this->host}:{$this->port}$this->url' -d '$postData'";
+        return $curlCall;
+    }
+}

+ 127 - 0
Mall/Framework/Session/SaveHandler/RedisSession.Class.php

@@ -0,0 +1,127 @@
+<?php
+
+namespace Mall\Framework\Session\SaveHandler;
+
+use Mall\Framework\Factory;
+
+class RedisSession implements SaveHandlerInterface
+{
+	/**
+	 * Session Save Path
+	 *
+	 * @var string
+	 */
+	protected $sessionSavePath;
+
+	/**
+	 * Session Name
+	 *
+	 * @var string
+	 */
+	protected $sessionName;
+
+	/**
+	 * The cache storage
+	 *
+	 * @var CacheStorage
+	 */
+	protected $cacheStorage;
+
+	public function __construct($storage = '')
+	{
+        $storage = $storage ?: Factory::cache();
+        $this->setCacheStorage($storage);
+	}
+
+    /**
+     * Open Session
+     *
+     * @param string $savePath
+     * @param string $name
+     * @return bool
+     */
+    public function open($savePath, $name)
+    {
+        $this->sessionSavePath = $savePath;
+        $this->sessionName = $name;
+
+        return true;
+    }
+
+	/**
+	 * Close session
+	 *
+	 * @return bool
+	 */
+	public function close()
+	{
+		return true;
+	}
+
+	/**
+	 * Read session data
+	 *
+	 * @param string $id
+	 * @return string
+	 */
+	public function read($id)
+	{
+        return $this->getCacheStorage()->get($id) ?: '';
+    }
+
+	/**
+	 * Write session data
+	 *
+	 * @param string $id
+	 * @param string $data
+	 * @return bool
+	 */
+	public function write($id, $data)
+	{
+        return $this->getCacheStorage()->set($id, $data);
+	}
+
+	/**
+	 * Destroy session
+	 *
+	 * @param string $id
+	 * @return bool
+	 */
+	public function destroy($id)
+	{
+		return $this->getCacheStorage()->delete($id);
+	}
+
+	/**
+	 * Garbage Collection
+	 *
+	 * @param int $maxlifetime
+	 * @return bool
+	 */
+	public function gc($maxlifetime)
+	{
+		return true;
+	}
+
+	/**
+	 * Set cache storage
+	 *
+	 * @param  CacheStorage $cacheStorage
+	 * @return Cache
+	 */
+	public function setCacheStorage($cacheStorage)
+	{
+		$this->cacheStorage = $cacheStorage;
+		return $this;
+	}
+
+	/**
+	 * Get cache storage
+	 *
+	 * @return CacheStorage
+	 */
+	public function getCacheStorage()
+	{
+		return $this->cacheStorage;
+	}
+}

+ 50 - 0
Mall/Framework/Session/SaveHandler/SaveHandlerInterface.Class.php

@@ -0,0 +1,50 @@
+<?php
+namespace Mall\Framework\Session\SaveHandler;
+
+interface SaveHandlerInterface
+{
+	/**
+	 * Open Session - retrieve resources
+	 *
+	 * @param string $savePath
+	 * @param string $name
+	 */
+	public function open($savePath, $name);
+
+	/**
+	 * Close Session - free resources
+
+	 */
+	public function close();
+
+	/**
+	 * Read session data
+	 *
+	 * @param string $id
+	 */
+	public function read($id);
+
+	/**
+	 * Write Session - commit data to resource
+	 *
+	 * @param string $id
+	 * @param mixed $data
+	 */
+	public function write($id, $data);
+
+	/**
+	 * Destroy Session - remove data from resource for
+	 * given session id
+	 *
+	 * @param string $id
+	 */
+	public function destroy($id);
+
+	/**
+	 * Garbage Collection - remove old session data older
+	 * than $maxlifetime (in seconds)
+	 *
+	 * @param int $maxlifetime
+	 */
+	public function gc($maxlifetime);
+}

+ 110 - 0
Mall/Framework/Smarty/libs/Autoloader.php

@@ -0,0 +1,110 @@
+<?php
+/**
+ * Smarty Autoloader
+ *
+ * @package    Smarty
+ */
+
+/**
+ * Smarty Autoloader
+ *
+ * @package    Smarty
+ * @author     Uwe Tews
+ *             Usage:
+ *                  require_once '...path/Autoloader.php';
+ *                  Smarty_Autoloader::register();
+ *             or
+ *                  include '...path/bootstrap.php';
+ *
+ *                  $smarty = new Smarty();
+ */
+class Smarty_Autoloader
+{
+   /**
+     * Filepath to Smarty root
+     *
+     * @var string
+     */
+    public static $SMARTY_DIR = null;
+
+    /**
+     * Filepath to Smarty internal plugins
+     *
+     * @var string
+     */
+    public static $SMARTY_SYSPLUGINS_DIR = null;
+
+    /**
+     * Array with Smarty core classes and their filename
+     *
+     * @var array
+     */
+    public static $rootClasses = array('smarty' => 'Smarty.class.php', 'smartybc' => 'SmartyBC.class.php',);
+
+    /**
+     * Registers Smarty_Autoloader backward compatible to older installations.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not.
+     */
+    public static function registerBC($prepend = false)
+    {
+        /**
+         * register the class autoloader
+         */
+        if (!defined('SMARTY_SPL_AUTOLOAD')) {
+            define('SMARTY_SPL_AUTOLOAD', 0);
+        }
+        if (SMARTY_SPL_AUTOLOAD &&
+            set_include_path(get_include_path() . PATH_SEPARATOR . SMARTY_SYSPLUGINS_DIR) !== false
+        ) {
+            $registeredAutoLoadFunctions = spl_autoload_functions();
+            if (!isset($registeredAutoLoadFunctions[ 'spl_autoload' ])) {
+                spl_autoload_register();
+            }
+        } else {
+            self::register($prepend);
+        }
+    }
+
+    /**
+     * Registers Smarty_Autoloader as an SPL autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not.
+     */
+    public static function register($prepend = false)
+    {
+        self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : dirname(__FILE__) . DIRECTORY_SEPARATOR;
+        self::$SMARTY_SYSPLUGINS_DIR = defined('SMARTY_SYSPLUGINS_DIR') ? SMARTY_SYSPLUGINS_DIR :
+            self::$SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR;
+        if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
+            spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
+        } else {
+            spl_autoload_register(array(__CLASS__, 'autoload'));
+        }
+    }
+
+    /**
+     * Handles auto loading of classes.
+     *
+     * @param string $class A class name.
+     */
+    public static function autoload($class)
+    {
+        if ($class[ 0 ] !== 'S' && strpos($class, 'Smarty') !== 0) {
+            return;
+        }
+        $_class = strtolower($class);
+        if (isset(self::$rootClasses[ $_class ])) {
+            $file = self::$SMARTY_DIR . self::$rootClasses[ $_class ];
+            if (is_file($file)) {
+                include $file;
+            }
+        } else {
+            $file = self::$SMARTY_SYSPLUGINS_DIR . $_class . '.php';
+            if (is_file($file)) {
+                include $file;
+            }
+        }
+        return;
+    }
+}

+ 1549 - 0
Mall/Framework/Smarty/libs/Smarty.class.php

@@ -0,0 +1,1549 @@
+<?php
+/**
+ * Project:     Smarty: the PHP compiling template engine
+ * File:        Smarty.class.php
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * For questions, help, comments, discussion, etc., please join the
+ * Smarty mailing list. Send a blank e-mail to
+ * smarty-discussion-subscribe@googlegroups.com
+ *
+ * @link      http://www.smarty.net/
+ * @copyright 2017 New Digital Group, Inc.
+ * @copyright 2017 Uwe Tews
+ * @author    Monte Ohrt <monte at ohrt dot com>
+ * @author    Uwe Tews
+ * @author    Rodney Rehm
+ * @package   Smarty
+ * @version   3.1.32-dev
+ */
+
+/**
+ * set SMARTY_DIR to absolute path to Smarty library files.
+ * Sets SMARTY_DIR only if user application has not already defined it.
+ */
+if (!defined('SMARTY_DIR')) {
+    define('SMARTY_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR);
+}
+
+/**
+ * set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins.
+ * Sets SMARTY_SYSPLUGINS_DIR only if user application has not already defined it.
+ */
+if (!defined('SMARTY_SYSPLUGINS_DIR')) {
+    define('SMARTY_SYSPLUGINS_DIR', SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR);
+}
+if (!defined('SMARTY_PLUGINS_DIR')) {
+    define('SMARTY_PLUGINS_DIR', SMARTY_DIR . 'plugins' . DIRECTORY_SEPARATOR);
+}
+if (!defined('SMARTY_MBSTRING')) {
+    define('SMARTY_MBSTRING', function_exists('mb_get_info'));
+}
+if (!defined('SMARTY_RESOURCE_CHAR_SET')) {
+    // UTF-8 can only be done properly when mbstring is available!
+    /**
+     * @deprecated in favor of Smarty::$_CHARSET
+     */
+    define('SMARTY_RESOURCE_CHAR_SET', SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1');
+}
+if (!defined('SMARTY_RESOURCE_DATE_FORMAT')) {
+    /**
+     * @deprecated in favor of Smarty::$_DATE_FORMAT
+     */
+    define('SMARTY_RESOURCE_DATE_FORMAT', '%b %e, %Y');
+}
+
+/**
+ * Load Smarty_Autoloader
+ */
+if (!class_exists('Smarty_Autoloader')) {
+    include dirname(__FILE__) . '/bootstrap.php';
+}
+
+/**
+ * Load always needed external class files
+ */
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_data.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_extension_handler.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_templatebase.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_template.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_resource.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_variable.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_source.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_resource_base.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_resource_file.php';
+
+/**
+ * This is the main Smarty class
+ *
+ * @package Smarty
+ *
+ * The following methods will be dynamically loaded by the extension handler when they are called.
+ * They are located in a corresponding Smarty_Internal_Method_xxxx class
+ *
+ * @method int clearAllCache(int $exp_time = null, string $type = null)
+ * @method int clearCache(string $template_name, string $cache_id = null, string $compile_id = null, int $exp_time = null, string $type = null)
+ * @method int compileAllTemplates(string $extension = '.tpl', bool $force_compile = false, int $time_limit = 0, int $max_errors = null)
+ * @method int compileAllConfig(string $extension = '.conf', bool $force_compile = false, int $time_limit = 0, int $max_errors = null)
+ * @method int clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null)
+ */
+class Smarty extends Smarty_Internal_TemplateBase
+{
+    /**#@+
+     * constant definitions
+     */
+
+    /**
+     * smarty version
+     */
+    const SMARTY_VERSION = '3.1.32-dev-11';
+
+    /**
+     * define variable scopes
+     */
+    const SCOPE_LOCAL = 1;
+
+    const SCOPE_PARENT = 2;
+
+    const SCOPE_TPL_ROOT = 4;
+
+    const SCOPE_ROOT = 8;
+
+    const SCOPE_SMARTY = 16;
+
+    const SCOPE_GLOBAL = 32;
+
+    /**
+     * define caching modes
+     */
+    const CACHING_OFF = 0;
+
+    const CACHING_LIFETIME_CURRENT = 1;
+
+    const CACHING_LIFETIME_SAVED = 2;
+
+    /**
+     * define constant for clearing cache files be saved expiration dates
+     */
+    const CLEAR_EXPIRED = - 1;
+
+    /**
+     * define compile check modes
+     */
+    const COMPILECHECK_OFF = 0;
+
+    const COMPILECHECK_ON = 1;
+
+    const COMPILECHECK_CACHEMISS = 2;
+
+    /**
+     * define debug modes
+     */
+    const DEBUG_OFF = 0;
+
+    const DEBUG_ON = 1;
+
+    const DEBUG_INDIVIDUAL = 2;
+
+    /**
+     * modes for handling of "<?php ... ?>" tags in templates.
+     */
+    const PHP_PASSTHRU = 0; //-> print tags as plain text
+
+    const PHP_QUOTE = 1; //-> escape tags as entities
+
+    const PHP_REMOVE = 2; //-> escape tags as entities
+
+    const PHP_ALLOW = 3; //-> escape tags as entities
+
+    /**
+     * filter types
+     */
+    const FILTER_POST = 'post';
+
+    const FILTER_PRE = 'pre';
+
+    const FILTER_OUTPUT = 'output';
+
+    const FILTER_VARIABLE = 'variable';
+
+    /**
+     * plugin types
+     */
+    const PLUGIN_FUNCTION = 'function';
+
+    const PLUGIN_BLOCK = 'block';
+
+    const PLUGIN_COMPILER = 'compiler';
+
+    const PLUGIN_MODIFIER = 'modifier';
+
+    const PLUGIN_MODIFIERCOMPILER = 'modifiercompiler';
+
+    /**
+     * Resource caching modes
+     * (not used since 3.1.30)
+     */
+    const RESOURCE_CACHE_OFF = 0;
+
+    const RESOURCE_CACHE_AUTOMATIC = 1; // cache template objects by rules
+
+    const RESOURCE_CACHE_TEMPLATE = 2; // cache all template objects
+
+    const RESOURCE_CACHE_ON = 4;    // cache source and compiled resources
+
+    /**#@-*/
+
+    /**
+     * assigned global tpl vars
+     */
+    public static $global_tpl_vars = array();
+
+    /**
+     * error handler returned by set_error_handler() in Smarty::muteExpectedErrors()
+     */
+    public static $_previous_error_handler = null;
+
+    /**
+     * contains directories outside of SMARTY_DIR that are to be muted by muteExpectedErrors()
+     */
+    public static $_muted_directories = array();
+
+    /**
+     * Flag denoting if Multibyte String functions are available
+     */
+    public static $_MBSTRING = SMARTY_MBSTRING;
+
+    /**
+     * The character set to adhere to (e.g. "UTF-8")
+     */
+    public static $_CHARSET = SMARTY_RESOURCE_CHAR_SET;
+
+    /**
+     * The date format to be used internally
+     * (accepts date() and strftime())
+     */
+    public static $_DATE_FORMAT = SMARTY_RESOURCE_DATE_FORMAT;
+
+    /**
+     * Flag denoting if PCRE should run in UTF-8 mode
+     */
+    public static $_UTF8_MODIFIER = 'u';
+
+    /**
+     * Flag denoting if operating system is windows
+     */
+    public static $_IS_WINDOWS = false;
+
+    /**#@+
+     * variables
+     */
+
+    /**
+     * auto literal on delimiters with whitespace
+     *
+     * @var boolean
+     */
+    public $auto_literal = true;
+
+    /**
+     * display error on not assigned variables
+     *
+     * @var boolean
+     */
+    public $error_unassigned = false;
+
+    /**
+     * look up relative file path in include_path
+     *
+     * @var boolean
+     */
+    public $use_include_path = false;
+
+    /**
+     * template directory
+     *
+     * @var array
+     */
+    protected $template_dir = array('./templates/');
+
+    /**
+     * flags for normalized template directory entries
+     *
+     * @var array
+     */
+    protected $_processedTemplateDir = array();
+
+    /**
+     * flag if template_dir is normalized
+     *
+     * @var bool
+     */
+    public $_templateDirNormalized = false;
+
+    /**
+     * joined template directory string used in cache keys
+     *
+     * @var string
+     */
+    public $_joined_template_dir = null;
+
+    /**
+     * config directory
+     *
+     * @var array
+     */
+    protected $config_dir = array('./configs/');
+
+    /**
+     * flags for normalized template directory entries
+     *
+     * @var array
+     */
+    protected $_processedConfigDir = array();
+
+    /**
+     * flag if config_dir is normalized
+     *
+     * @var bool
+     */
+    public $_configDirNormalized = false;
+
+    /**
+     * joined config directory string used in cache keys
+     *
+     * @var string
+     */
+    public $_joined_config_dir = null;
+
+    /**
+     * default template handler
+     *
+     * @var callable
+     */
+    public $default_template_handler_func = null;
+
+    /**
+     * default config handler
+     *
+     * @var callable
+     */
+    public $default_config_handler_func = null;
+
+    /**
+     * default plugin handler
+     *
+     * @var callable
+     */
+    public $default_plugin_handler_func = null;
+
+    /**
+     * compile directory
+     *
+     * @var string
+     */
+    protected $compile_dir = './templates_c/';
+
+    /**
+     * flag if template_dir is normalized
+     *
+     * @var bool
+     */
+    public $_compileDirNormalized = false;
+
+    /**
+     * plugins directory
+     *
+     * @var array
+     */
+    protected $plugins_dir = array();
+
+    /**
+     * flag if plugins_dir is normalized
+     *
+     * @var bool
+     */
+    public $_pluginsDirNormalized = false;
+
+    /**
+     * cache directory
+     *
+     * @var string
+     */
+    protected $cache_dir = './cache/';
+
+    /**
+     * flag if template_dir is normalized
+     *
+     * @var bool
+     */
+    public $_cacheDirNormalized = false;
+
+    /**
+     * force template compiling?
+     *
+     * @var boolean
+     */
+    public $force_compile = false;
+
+    /**
+     * check template for modifications?
+     *
+     * @var boolean
+     */
+    public $compile_check = true;
+
+    /**
+     * use sub dirs for compiled/cached files?
+     *
+     * @var boolean
+     */
+    public $use_sub_dirs = false;
+
+    /**
+     * allow ambiguous resources (that are made unique by the resource handler)
+     *
+     * @var boolean
+     */
+    public $allow_ambiguous_resources = false;
+
+    /**
+     * merge compiled includes
+     *
+     * @var boolean
+     */
+    public $merge_compiled_includes = false;
+
+    /*
+    * flag for behaviour when extends: resource  and {extends} tag are used simultaneous
+    *   if false disable execution of {extends} in templates called by extends resource.
+    *   (behaviour as versions < 3.1.28)
+    *
+    * @var boolean
+    */
+    public $extends_recursion = true;
+
+    /**
+     * force cache file creation
+     *
+     * @var boolean
+     */
+    public $force_cache = false;
+
+    /**
+     * template left-delimiter
+     *
+     * @var string
+     */
+    public $left_delimiter = "{";
+
+    /**
+     * template right-delimiter
+     *
+     * @var string
+     */
+    public $right_delimiter = "}";
+
+    /**#@+
+     * security
+     */
+    /**
+     * class name
+     * This should be instance of Smarty_Security.
+     *
+     * @var string
+     * @see Smarty_Security
+     */
+    public $security_class = 'Smarty_Security';
+
+    /**
+     * implementation of security class
+     *
+     * @var Smarty_Security
+     */
+    public $security_policy = null;
+
+    /**
+     * controls handling of PHP-blocks
+     *
+     * @var integer
+     */
+    public $php_handling = self::PHP_PASSTHRU;
+
+    /**
+     * controls if the php template file resource is allowed
+     *
+     * @var bool
+     */
+    public $allow_php_templates = false;
+
+    /**#@-*/
+    /**
+     * debug mode
+     * Setting this to true enables the debug-console.
+     *
+     * @var boolean
+     */
+    public $debugging = false;
+
+    /**
+     * This determines if debugging is enable-able from the browser.
+     * <ul>
+     *  <li>NONE => no debugging control allowed</li>
+     *  <li>URL => enable debugging when SMARTY_DEBUG is found in the URL.</li>
+     * </ul>
+     *
+     * @var string
+     */
+    public $debugging_ctrl = 'NONE';
+
+    /**
+     * Name of debugging URL-param.
+     * Only used when $debugging_ctrl is set to 'URL'.
+     * The name of the URL-parameter that activates debugging.
+     *
+     * @var string
+     */
+    public $smarty_debug_id = 'SMARTY_DEBUG';
+
+    /**
+     * Path of debug template.
+     *
+     * @var string
+     */
+    public $debug_tpl = null;
+
+    /**
+     * When set, smarty uses this value as error_reporting-level.
+     *
+     * @var int
+     */
+    public $error_reporting = null;
+
+    /**#@+
+     * config var settings
+     */
+
+    /**
+     * Controls whether variables with the same name overwrite each other.
+     *
+     * @var boolean
+     */
+    public $config_overwrite = true;
+
+    /**
+     * Controls whether config values of on/true/yes and off/false/no get converted to boolean.
+     *
+     * @var boolean
+     */
+    public $config_booleanize = true;
+
+    /**
+     * Controls whether hidden config sections/vars are read from the file.
+     *
+     * @var boolean
+     */
+    public $config_read_hidden = false;
+
+    /**#@-*/
+
+    /**#@+
+     * resource locking
+     */
+
+    /**
+     * locking concurrent compiles
+     *
+     * @var boolean
+     */
+    public $compile_locking = true;
+
+    /**
+     * Controls whether cache resources should use locking mechanism
+     *
+     * @var boolean
+     */
+    public $cache_locking = false;
+
+    /**
+     * seconds to wait for acquiring a lock before ignoring the write lock
+     *
+     * @var float
+     */
+    public $locking_timeout = 10;
+
+    /**#@-*/
+
+    /**
+     * resource type used if none given
+     * Must be an valid key of $registered_resources.
+     *
+     * @var string
+     */
+    public $default_resource_type = 'file';
+
+    /**
+     * caching type
+     * Must be an element of $cache_resource_types.
+     *
+     * @var string
+     */
+    public $caching_type = 'file';
+
+    /**
+     * config type
+     *
+     * @var string
+     */
+    public $default_config_type = 'file';
+
+    /**
+     * check If-Modified-Since headers
+     *
+     * @var boolean
+     */
+    public $cache_modified_check = false;
+
+    /**
+     * registered plugins
+     *
+     * @var array
+     */
+    public $registered_plugins = array();
+
+    /**
+     * registered objects
+     *
+     * @var array
+     */
+    public $registered_objects = array();
+
+    /**
+     * registered classes
+     *
+     * @var array
+     */
+    public $registered_classes = array();
+
+    /**
+     * registered filters
+     *
+     * @var array
+     */
+    public $registered_filters = array();
+
+    /**
+     * registered resources
+     *
+     * @var array
+     */
+    public $registered_resources = array();
+
+    /**
+     * registered cache resources
+     *
+     * @var array
+     */
+    public $registered_cache_resources = array();
+
+    /**
+     * autoload filter
+     *
+     * @var array
+     */
+    public $autoload_filters = array();
+
+    /**
+     * default modifier
+     *
+     * @var array
+     */
+    public $default_modifiers = array();
+
+    /**
+     * autoescape variable output
+     *
+     * @var boolean
+     */
+    public $escape_html = false;
+
+    /**
+     * start time for execution time calculation
+     *
+     * @var int
+     */
+    public $start_time = 0;
+
+    /**
+     * required by the compiler for BC
+     *
+     * @var string
+     */
+    public $_current_file = null;
+
+    /**
+     * internal flag to enable parser debugging
+     *
+     * @var bool
+     */
+    public $_parserdebug = false;
+
+    /**
+     * This object type (Smarty = 1, template = 2, data = 4)
+     *
+     * @var int
+     */
+    public $_objType = 1;
+
+    /**
+     * Debug object
+     *
+     * @var Smarty_Internal_Debug
+     */
+    public $_debug = null;
+
+    /**
+     * Directory separator
+     *
+     * @var string
+     */
+    public $ds = DIRECTORY_SEPARATOR;
+
+    /**
+     * removed properties
+     *
+     * @var string[]
+     */
+    protected $obsoleteProperties = array('resource_caching', 'template_resource_caching', 'direct_access_security',
+                                        '_dir_perms', '_file_perms', 'plugin_search_order',
+                                        'inheritance_merge_compiled_includes', 'resource_cache_mode',);
+
+    /**
+     * List of private properties which will call getter/setter on a direct access
+     *
+     * @var string[]
+     */
+    protected $accessMap = array('template_dir' => 'TemplateDir', 'config_dir' => 'ConfigDir',
+                               'plugins_dir' => 'PluginsDir', 'compile_dir' => 'CompileDir',
+                               'cache_dir' => 'CacheDir',);
+
+    /**#@-*/
+
+    /**
+     * Initialize new Smarty object
+     */
+    public function __construct()
+    {
+        $this->_clearTemplateCache();
+        parent::__construct();
+        if (is_callable('mb_internal_encoding')) {
+            mb_internal_encoding(Smarty::$_CHARSET);
+        }
+        $this->start_time = microtime(true);
+
+        if (isset($_SERVER[ 'SCRIPT_NAME' ])) {
+            Smarty::$global_tpl_vars[ 'SCRIPT_NAME' ] = new Smarty_Variable($_SERVER[ 'SCRIPT_NAME' ]);
+        }
+
+        // Check if we're running on windows
+        Smarty::$_IS_WINDOWS = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
+        // let PCRE (preg_*) treat strings as ISO-8859-1 if we're not dealing with UTF-8
+        if (Smarty::$_CHARSET !== 'UTF-8') {
+            Smarty::$_UTF8_MODIFIER = '';
+        }
+    }
+
+    /**
+     * Check if a template resource exists
+     *
+     * @param  string $resource_name template name
+     *
+     * @return boolean status
+     */
+    public function templateExists($resource_name)
+    {
+        // create source object
+        $source = Smarty_Template_Source::load(null, $this, $resource_name);
+        return $source->exists;
+    }
+
+    /**
+     * Loads security class and enables security
+     *
+     * @param  string|Smarty_Security $security_class if a string is used, it must be class-name
+     *
+     * @return Smarty                 current Smarty instance for chaining
+     * @throws SmartyException        when an invalid class name is provided
+     */
+    public function enableSecurity($security_class = null)
+    {
+        Smarty_Security::enableSecurity($this, $security_class);
+        return $this;
+    }
+
+    /**
+     * Disable security
+     *
+     * @return Smarty current Smarty instance for chaining
+     */
+    public function disableSecurity()
+    {
+        $this->security_policy = null;
+
+        return $this;
+    }
+
+    /**
+     * Set template directory
+     *
+     * @param  string|array $template_dir directory(s) of template sources
+     * @param bool          $isConfig     true for config_dir
+     *
+     * @return \Smarty current Smarty instance for chaining
+     */
+    public function setTemplateDir($template_dir, $isConfig = false)
+    {
+        if ($isConfig) {
+            $this->config_dir = array();
+            $this->_processedConfigDir = array();
+        } else {
+            $this->template_dir = array();
+            $this->_processedTemplateDir = array();
+        }
+        $this->addTemplateDir($template_dir, null, $isConfig);
+        return $this;
+    }
+
+    /**
+     * Add template directory(s)
+     *
+     * @param  string|array $template_dir directory(s) of template sources
+     * @param  string       $key          of the array element to assign the template dir to
+     * @param bool          $isConfig     true for config_dir
+     *
+     * @return Smarty          current Smarty instance for chaining
+     */
+    public function addTemplateDir($template_dir, $key = null, $isConfig = false)
+    {
+        if ($isConfig) {
+            $processed = &$this->_processedConfigDir;
+            $dir = &$this->config_dir;
+            $this->_configDirNormalized = false;
+        } else {
+            $processed = &$this->_processedTemplateDir;
+            $dir = &$this->template_dir;
+            $this->_templateDirNormalized = false;
+        }
+        if (is_array($template_dir)) {
+            foreach ($template_dir as $k => $v) {
+                if (is_int($k)) {
+                    // indexes are not merged but appended
+                    $dir[] = $v;
+                } else {
+                    // string indexes are overridden
+                    $dir[ $k ] = $v;
+                    unset($processed[ $key ]);
+                }
+            }
+        } else {
+            if ($key !== null) {
+                // override directory at specified index
+                $dir[ $key ] = $template_dir;
+                unset($processed[ $key ]);
+            } else {
+                // append new directory
+                $dir[] = $template_dir;
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Get template directories
+     *
+     * @param mixed $index    index of directory to get, null to get all
+     * @param bool  $isConfig true for config_dir
+     *
+     * @return array list of template directories, or directory of $index
+     */
+    public function getTemplateDir($index = null, $isConfig = false)
+    {
+        if ($isConfig) {
+            $dir = &$this->config_dir;
+        } else {
+            $dir = &$this->template_dir;
+        }
+        if ($isConfig ? !$this->_configDirNormalized : !$this->_templateDirNormalized) {
+            $this->_nomalizeTemplateConfig($isConfig);
+        }
+        if ($index !== null) {
+            return isset($dir[ $index ]) ? $dir[ $index ] : null;
+        }
+        return $dir;
+    }
+
+    /**
+     * Set config directory
+     *
+     * @param $config_dir
+     *
+     * @return Smarty       current Smarty instance for chaining
+     */
+    public function setConfigDir($config_dir)
+    {
+        return $this->setTemplateDir($config_dir, true);
+    }
+
+    /**
+     * Add config directory(s)
+     *
+     * @param string|array $config_dir directory(s) of config sources
+     * @param mixed        $key        key of the array element to assign the config dir to
+     *
+     * @return Smarty current Smarty instance for chaining
+     */
+    public function addConfigDir($config_dir, $key = null)
+    {
+        return $this->addTemplateDir($config_dir, $key, true);
+    }
+
+    /**
+     * Get config directory
+     *
+     * @param mixed $index index of directory to get, null to get all
+     *
+     * @return array configuration directory
+     */
+    public function getConfigDir($index = null)
+    {
+        return $this->getTemplateDir($index, true);
+    }
+
+    /**
+     * Set plugins directory
+     *
+     * @param  string|array $plugins_dir directory(s) of plugins
+     *
+     * @return Smarty       current Smarty instance for chaining
+     */
+    public function setPluginsDir($plugins_dir)
+    {
+        $this->plugins_dir = (array) $plugins_dir;
+        $this->_pluginsDirNormalized = false;
+        return $this;
+    }
+
+    /**
+     * Adds directory of plugin files
+     *
+     * @param null|array|string $plugins_dir
+     *
+     * @return Smarty current Smarty instance for chaining
+     */
+    public function addPluginsDir($plugins_dir)
+    {
+        if (empty($this->plugins_dir)) {
+            $this->plugins_dir[] = SMARTY_PLUGINS_DIR;
+        }
+        $this->plugins_dir = array_merge($this->plugins_dir, (array) $plugins_dir);
+        $this->_pluginsDirNormalized = false;
+        return $this;
+    }
+
+    /**
+     * Get plugin directories
+     *
+     * @return array list of plugin directories
+     */
+    public function getPluginsDir()
+    {
+        if (empty($this->plugins_dir)) {
+            $this->plugins_dir[] = SMARTY_PLUGINS_DIR;
+            $this->_pluginsDirNormalized = false;
+        }
+        if (!$this->_pluginsDirNormalized) {
+            if (!is_array($this->plugins_dir)) {
+                $this->plugins_dir = (array) $this->plugins_dir;
+            }
+            foreach ($this->plugins_dir as $k => $v) {
+                $this->plugins_dir[ $k ] = $this->_realpath(rtrim($v, "/\\") . $this->ds, true);
+            }
+            $this->_cache[ 'plugin_files' ] = array();
+            $this->_pluginsDirNormalized = true;
+        }
+        return $this->plugins_dir;
+    }
+
+    /**
+     *
+     * @param  string $compile_dir directory to store compiled templates in
+     *
+     * @return Smarty current Smarty instance for chaining
+     */
+    public function setCompileDir($compile_dir)
+    {
+        $this->_normalizeDir('compile_dir', $compile_dir);
+        $this->_compileDirNormalized = true;
+        return $this;
+    }
+
+    /**
+     * Get compiled directory
+     *
+     * @return string path to compiled templates
+     */
+    public function getCompileDir()
+    {
+        if (!$this->_compileDirNormalized) {
+            $this->_normalizeDir('compile_dir', $this->compile_dir);
+            $this->_compileDirNormalized = true;
+        }
+        return $this->compile_dir;
+    }
+
+    /**
+     * Set cache directory
+     *
+     * @param  string $cache_dir directory to store cached templates in
+     *
+     * @return Smarty current Smarty instance for chaining
+     */
+    public function setCacheDir($cache_dir)
+    {
+        $this->_normalizeDir('cache_dir', $cache_dir);
+        $this->_cacheDirNormalized = true;
+        return $this;
+    }
+
+    /**
+     * Get cache directory
+     *
+     * @return string path of cache directory
+     */
+    public function getCacheDir()
+    {
+        if (!$this->_cacheDirNormalized) {
+            $this->_normalizeDir('cache_dir', $this->cache_dir);
+            $this->_cacheDirNormalized = true;
+        }
+        return $this->cache_dir;
+    }
+
+    /**
+     * Normalize and set directory string
+     *
+     * @param string $dirName cache_dir or compile_dir
+     * @param string $dir     filepath of folder
+     */
+    private function _normalizeDir($dirName, $dir)
+    {
+        $this->{$dirName} = $this->_realpath(rtrim($dir, "/\\") . $this->ds, true);
+        if (!isset(Smarty::$_muted_directories[ $this->{$dirName} ])) {
+            Smarty::$_muted_directories[ $this->{$dirName} ] = null;
+        }
+    }
+
+    /**
+     * Normalize template_dir or config_dir
+     *
+     * @param bool $isConfig true for config_dir
+     *
+     */
+    private function _nomalizeTemplateConfig($isConfig)
+    {
+        if ($isConfig) {
+            $processed = &$this->_processedConfigDir;
+            $dir = &$this->config_dir;
+        } else {
+            $processed = &$this->_processedTemplateDir;
+            $dir = &$this->template_dir;
+        }
+        if (!is_array($dir)) {
+            $dir = (array) $dir;
+        }
+        foreach ($dir as $k => $v) {
+            if (!isset($processed[ $k ])) {
+                $dir[ $k ] = $v = $this->_realpath(rtrim($v, "/\\") . $this->ds, true);
+                $processed[ $k ] = true;
+            }
+        }
+        $isConfig ? $this->_configDirNormalized = true : $this->_templateDirNormalized = true;
+        $isConfig ? $this->_joined_config_dir = join('#', $this->config_dir) :
+            $this->_joined_template_dir = join('#', $this->template_dir);
+    }
+
+    /**
+     * creates a template object
+     *
+     * @param  string  $template   the resource handle of the template file
+     * @param  mixed   $cache_id   cache id to be used with this template
+     * @param  mixed   $compile_id compile id to be used with this template
+     * @param  object  $parent     next higher level of Smarty variables
+     * @param  boolean $do_clone   flag is Smarty object shall be cloned
+     *
+     * @return object  template object
+     */
+    public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true)
+    {
+        if ($cache_id !== null && (is_object($cache_id) || is_array($cache_id))) {
+            $parent = $cache_id;
+            $cache_id = null;
+        }
+        if ($parent !== null && is_array($parent)) {
+            $data = $parent;
+            $parent = null;
+        } else {
+            $data = null;
+        }
+        if (!$this->_templateDirNormalized) {
+            $this->_nomalizeTemplateConfig(false);
+        }
+        $_templateId = $this->_getTemplateId($template, $cache_id, $compile_id);
+        $tpl = null;
+        if ($this->caching && isset(Smarty_Internal_Template::$isCacheTplObj[ $_templateId ])) {
+            $tpl = $do_clone ? clone Smarty_Internal_Template::$isCacheTplObj[ $_templateId ] :
+                Smarty_Internal_Template::$isCacheTplObj[ $_templateId ];
+            $tpl->inheritance = null;
+            $tpl->tpl_vars = $tpl->config_vars = array();
+        } else if (!$do_clone && isset(Smarty_Internal_Template::$tplObjCache[ $_templateId ])) {
+            $tpl = clone Smarty_Internal_Template::$tplObjCache[ $_templateId ];
+            $tpl->inheritance = null;
+            $tpl->tpl_vars = $tpl->config_vars = array();
+        } else {
+            /* @var Smarty_Internal_Template $tpl */
+            $tpl = new $this->template_class($template, $this, null, $cache_id, $compile_id, null, null);
+            $tpl->templateId = $_templateId;
+        }
+        if ($do_clone) {
+            $tpl->smarty = clone $tpl->smarty;
+        }
+        $tpl->parent = $parent ? $parent : $this;
+        // fill data if present
+        if (!empty($data) && is_array($data)) {
+            // set up variable values
+            foreach ($data as $_key => $_val) {
+                $tpl->tpl_vars[ $_key ] = new Smarty_Variable($_val);
+            }
+        }
+        if ($this->debugging || $this->debugging_ctrl == 'URL') {
+            $tpl->smarty->_debug = new Smarty_Internal_Debug();
+            // check URL debugging control
+            if (!$this->debugging && $this->debugging_ctrl == 'URL') {
+                $tpl->smarty->_debug->debugUrl($tpl->smarty);
+            }
+        }
+        return $tpl;
+    }
+
+    /**
+     * Takes unknown classes and loads plugin files for them
+     * class name format: Smarty_PluginType_PluginName
+     * plugin filename format: plugintype.pluginname.php
+     *
+     * @param  string $plugin_name class plugin name to load
+     * @param  bool   $check       check if already loaded
+     *
+     * @throws SmartyException
+     * @return string |boolean filepath of loaded file or false
+     */
+    public function loadPlugin($plugin_name, $check = true)
+    {
+        return $this->ext->loadPlugin->loadPlugin($this, $plugin_name, $check);
+    }
+
+    /**
+     * Get unique template id
+     *
+     * @param string                    $template_name
+     * @param null|mixed                $cache_id
+     * @param null|mixed                $compile_id
+     * @param null                      $caching
+     * @param \Smarty_Internal_Template $template
+     *
+     * @return string
+     */
+    public function _getTemplateId($template_name, $cache_id = null, $compile_id = null, $caching = null,
+                                   Smarty_Internal_Template $template = null)
+    {
+        $template_name = (strpos($template_name, ':') === false) ? "{$this->default_resource_type}:{$template_name}" :
+            $template_name;
+        $cache_id = $cache_id === null ? $this->cache_id : $cache_id;
+        $compile_id = $compile_id === null ? $this->compile_id : $compile_id;
+        $caching = (int) ($caching === null ? $this->caching : $caching);
+
+        if ((isset($template) && strpos($template_name, ':.') !== false) || $this->allow_ambiguous_resources) {
+            $_templateId =
+                Smarty_Resource::getUniqueTemplateName((isset($template) ? $template : $this), $template_name) .
+                "#{$cache_id}#{$compile_id}#{$caching}";
+        } else {
+            $_templateId = $this->_joined_template_dir . "#{$template_name}#{$cache_id}#{$compile_id}#{$caching}";
+        }
+        if (isset($_templateId[ 150 ])) {
+            $_templateId = sha1($_templateId);
+        }
+        return $_templateId;
+    }
+
+    /**
+     * Normalize path
+     *  - remove /./ and /../
+     *  - make it absolute if required
+     *
+     * @param string $path      file path
+     * @param bool   $realpath  if true - convert to absolute
+     *                          false - convert to relative
+     *                          null - keep as it is but remove /./ /../
+     *
+     * @return string
+     */
+    public function _realpath($path, $realpath = null)
+    {
+        $nds = $this->ds == '/' ? '\\' : '/';
+        // normalize $this->ds
+        $path = str_replace($nds, $this->ds, $path);
+        preg_match('%^(?<root>(?:[[:alpha:]]:[\\\\]|/|[\\\\]{2}[[:alpha:]]+|[[:print:]]{2,}:[/]{2}|[\\\\])?)(?<path>(?:[[:print:]]*))$%',
+                   $path, $parts);
+        $path = $parts[ 'path' ];
+        if ($parts[ 'root' ] == '\\') {
+            $parts[ 'root' ] = substr(getcwd(), 0, 2) . $parts[ 'root' ];
+        } else {
+            if ($realpath !== null && !$parts[ 'root' ]) {
+                $path = getcwd() . $this->ds . $path;
+            }
+        }
+        // remove noop 'DIRECTORY_SEPARATOR DIRECTORY_SEPARATOR' and 'DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR' patterns
+        $path = preg_replace('#([\\\\/]([.]?[\\\\/])+)#', $this->ds, $path);
+        // resolve '..DIRECTORY_SEPARATOR' pattern, smallest first
+        if (strpos($path, '..' . $this->ds) != false &&
+            preg_match_all('#(([.]?[\\\\/])*([.][.])[\\\\/]([.]?[\\\\/])*)+#', $path, $match)
+        ) {
+            $counts = array();
+            foreach ($match[ 0 ] as $m) {
+                $counts[] = (int) ((strlen($m) - 1) / 3);
+            }
+            sort($counts);
+            foreach ($counts as $count) {
+                $path = preg_replace('#(([\\\\/]([.]?[\\\\/])*[^\\\\/.]+){' . $count .
+                                     '}[\\\\/]([.]?[\\\\/])*([.][.][\\\\/]([.]?[\\\\/])*){' . $count . '})(?=[^.])#',
+                                     $this->ds, $path);
+            }
+        }
+
+        return $parts[ 'root' ] . $path;
+    }
+
+    /**
+     * Empty template objects cache
+     */
+    public function _clearTemplateCache()
+    {
+        Smarty_Internal_Template::$isCacheTplObj = array();
+        Smarty_Internal_Template::$tplObjCache = array();
+    }
+
+    /**
+     * Get Smarty object
+     *
+     * @return Smarty
+     */
+    public function _getSmartyObj()
+    {
+        return $this;
+    }
+
+    /**
+     * @param boolean $compile_check
+     */
+    public function setCompileCheck($compile_check)
+    {
+        $this->compile_check = $compile_check;
+    }
+
+    /**
+     * @param boolean $use_sub_dirs
+     */
+    public function setUseSubDirs($use_sub_dirs)
+    {
+        $this->use_sub_dirs = $use_sub_dirs;
+    }
+
+    /**
+     * @param int $error_reporting
+     */
+    public function setErrorReporting($error_reporting)
+    {
+        $this->error_reporting = $error_reporting;
+    }
+
+    /**
+     * @param boolean $escape_html
+     */
+    public function setEscapeHtml($escape_html)
+    {
+        $this->escape_html = $escape_html;
+    }
+
+    /**
+     * @param boolean $auto_literal
+     */
+    public function setAutoLiteral($auto_literal)
+    {
+        $this->auto_literal = $auto_literal;
+    }
+
+    /**
+     * @param boolean $force_compile
+     */
+    public function setForceCompile($force_compile)
+    {
+        $this->force_compile = $force_compile;
+    }
+
+    /**
+     * @param boolean $merge_compiled_includes
+     */
+    public function setMergeCompiledIncludes($merge_compiled_includes)
+    {
+        $this->merge_compiled_includes = $merge_compiled_includes;
+    }
+
+    /**
+     * @param string $left_delimiter
+     */
+    public function setLeftDelimiter($left_delimiter)
+    {
+        $this->left_delimiter = $left_delimiter;
+    }
+
+    /**
+     * @param string $right_delimiter
+     */
+    public function setRightDelimiter($right_delimiter)
+    {
+        $this->right_delimiter = $right_delimiter;
+    }
+
+    /**
+     * @param boolean $debugging
+     */
+    public function setDebugging($debugging)
+    {
+        $this->debugging = $debugging;
+    }
+
+    /**
+     * @param boolean $config_overwrite
+     */
+    public function setConfigOverwrite($config_overwrite)
+    {
+        $this->config_overwrite = $config_overwrite;
+    }
+
+    /**
+     * @param boolean $config_booleanize
+     */
+    public function setConfigBooleanize($config_booleanize)
+    {
+        $this->config_booleanize = $config_booleanize;
+    }
+
+    /**
+     * @param boolean $config_read_hidden
+     */
+    public function setConfigReadHidden($config_read_hidden)
+    {
+        $this->config_read_hidden = $config_read_hidden;
+    }
+
+    /**
+     * @param boolean $compile_locking
+     */
+    public function setCompileLocking($compile_locking)
+    {
+        $this->compile_locking = $compile_locking;
+    }
+
+    /**
+     * @param string $default_resource_type
+     */
+    public function setDefaultResourceType($default_resource_type)
+    {
+        $this->default_resource_type = $default_resource_type;
+    }
+
+    /**
+     * @param string $caching_type
+     */
+    public function setCachingType($caching_type)
+    {
+        $this->caching_type = $caching_type;
+    }
+
+    /**
+     * Test install
+     *
+     * @param null $errors
+     */
+    public function testInstall(&$errors = null)
+    {
+        Smarty_Internal_TestInstall::testInstall($this, $errors);
+    }
+
+    /**
+     * <<magic>> Generic getter.
+     * Calls the appropriate getter function.
+     * Issues an E_USER_NOTICE if no valid getter is found.
+     *
+     * @param  string $name property name
+     *
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        if (isset($this->accessMap[ $name ])) {
+            $method = 'get' . $this->accessMap[ $name ];
+            return $this->{$method}();
+        } elseif (isset($this->_cache[ $name ])) {
+            return $this->_cache[ $name ];
+        } elseif (in_array($name, $this->obsoleteProperties)) {
+            return null;
+        } else {
+            trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE);
+        }
+        return null;
+    }
+
+    /**
+     * <<magic>> Generic setter.
+     * Calls the appropriate setter function.
+     * Issues an E_USER_NOTICE if no valid setter is found.
+     *
+     * @param string $name  property name
+     * @param mixed  $value parameter passed to setter
+     */
+    public function __set($name, $value)
+    {
+        if (isset($this->accessMap[ $name ])) {
+            $method = 'set' . $this->accessMap[ $name ];
+            $this->{$method}($value);
+        } elseif (in_array($name, $this->obsoleteProperties)) {
+            return;
+        } else {
+            if (is_object($value) && method_exists($value, $name)) {
+                $this->$name = $value;
+            } else {
+                trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE);
+            }
+        }
+    }
+
+    /**
+     * Error Handler to mute expected messages
+     *
+     * @link http://php.net/set_error_handler
+     *
+     * @param  integer $errno Error level
+     * @param          $errstr
+     * @param          $errfile
+     * @param          $errline
+     * @param          $errcontext
+     *
+     * @return bool|void
+     */
+    public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $errcontext)
+    {
+        $_is_muted_directory = false;
+
+        // add the SMARTY_DIR to the list of muted directories
+        if (!isset(Smarty::$_muted_directories[ SMARTY_DIR ])) {
+            $smarty_dir = realpath(SMARTY_DIR);
+            if ($smarty_dir !== false) {
+                Smarty::$_muted_directories[ SMARTY_DIR ] =
+                    array('file' => $smarty_dir, 'length' => strlen($smarty_dir),);
+            }
+        }
+
+        // walk the muted directories and test against $errfile
+        foreach (Smarty::$_muted_directories as $key => &$dir) {
+            if (!$dir) {
+                // resolve directory and length for speedy comparisons
+                $file = realpath($key);
+                if ($file === false) {
+                    // this directory does not exist, remove and skip it
+                    unset(Smarty::$_muted_directories[ $key ]);
+                    continue;
+                }
+                $dir = array('file' => $file, 'length' => strlen($file),);
+            }
+            if (!strncmp($errfile, $dir[ 'file' ], $dir[ 'length' ])) {
+                $_is_muted_directory = true;
+                break;
+            }
+        }
+        // pass to next error handler if this error did not occur inside SMARTY_DIR
+        // or the error was within smarty but masked to be ignored
+        if (!$_is_muted_directory || ($errno && $errno & error_reporting())) {
+            if (Smarty::$_previous_error_handler) {
+                return call_user_func(Smarty::$_previous_error_handler, $errno, $errstr, $errfile, $errline,
+                                      $errcontext);
+            } else {
+                return false;
+            }
+        }
+        return;
+    }
+
+    /**
+     * Enable error handler to mute expected messages
+     *
+     * @return void
+     */
+    public static function muteExpectedErrors()
+    {
+        /*
+            error muting is done because some people implemented custom error_handlers using
+            http://php.net/set_error_handler and for some reason did not understand the following paragraph:
+
+                It is important to remember that the standard PHP error handler is completely bypassed for the
+                error types specified by error_types unless the callback function returns FALSE.
+                error_reporting() settings will have no effect and your error handler will be called regardless -
+                however you are still able to read the current value of error_reporting and act appropriately.
+                Of particular note is that this value will be 0 if the statement that caused the error was
+                prepended by the @ error-control operator.
+
+            Smarty deliberately uses @filemtime() over file_exists() and filemtime() in some places. Reasons include
+                - @filemtime() is almost twice as fast as using an additional file_exists()
+                - between file_exists() and filemtime() a possible race condition is opened,
+                  which does not exist using the simple @filemtime() approach.
+        */
+        $error_handler = array('Smarty', 'mutingErrorHandler');
+        $previous = set_error_handler($error_handler);
+
+        // avoid dead loops
+        if ($previous !== $error_handler) {
+            Smarty::$_previous_error_handler = $previous;
+        }
+    }
+
+    /**
+     * Disable error handler muting expected messages
+     *
+     * @return void
+     */
+    public static function unmuteExpectedErrors()
+    {
+        restore_error_handler();
+    }
+}

+ 455 - 0
Mall/Framework/Smarty/libs/SmartyBC.class.php

@@ -0,0 +1,455 @@
+<?php
+/**
+ * Project:     Smarty: the PHP compiling template engine
+ * File:        SmartyBC.class.php
+ * SVN:         $Id: $
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * For questions, help, comments, discussion, etc., please join the
+ * Smarty mailing list. Send a blank e-mail to
+ * smarty-discussion-subscribe@googlegroups.com
+ *
+ * @link      http://www.smarty.net/
+ * @copyright 2008 New Digital Group, Inc.
+ * @author    Monte Ohrt <monte at ohrt dot com>
+ * @author    Uwe Tews
+ * @author    Rodney Rehm
+ * @package   Smarty
+ */
+/**
+ * @ignore
+ */
+require_once(dirname(__FILE__) . '/Smarty.class.php');
+
+/**
+ * Smarty Backward Compatibility Wrapper Class
+ *
+ * @package Smarty
+ */
+class SmartyBC extends Smarty
+{
+    /**
+     * Smarty 2 BC
+     *
+     * @var string
+     */
+    public $_version = self::SMARTY_VERSION;
+
+    /**
+     * This is an array of directories where trusted php scripts reside.
+     *
+     * @var array
+     */
+    public $trusted_dir = array();
+
+    /**
+     * Initialize new SmartyBC object
+     *
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * wrapper for assign_by_ref
+     *
+     * @param string $tpl_var the template variable name
+     * @param mixed  &$value  the referenced value to assign
+     */
+    public function assign_by_ref($tpl_var, &$value)
+    {
+        $this->assignByRef($tpl_var, $value);
+    }
+
+    /**
+     * wrapper for append_by_ref
+     *
+     * @param string  $tpl_var the template variable name
+     * @param mixed   &$value  the referenced value to append
+     * @param boolean $merge   flag if array elements shall be merged
+     */
+    public function append_by_ref($tpl_var, &$value, $merge = false)
+    {
+        $this->appendByRef($tpl_var, $value, $merge);
+    }
+
+    /**
+     * clear the given assigned template variable.
+     *
+     * @param string $tpl_var the template variable to clear
+     */
+    public function clear_assign($tpl_var)
+    {
+        $this->clearAssign($tpl_var);
+    }
+
+    /**
+     * Registers custom function to be used in templates
+     *
+     * @param string $function      the name of the template function
+     * @param string $function_impl the name of the PHP function to register
+     * @param bool   $cacheable
+     * @param mixed  $cache_attrs
+     */
+    public function register_function($function, $function_impl, $cacheable = true, $cache_attrs = null)
+    {
+        $this->registerPlugin('function', $function, $function_impl, $cacheable, $cache_attrs);
+    }
+
+    /**
+     * Unregister custom function
+     *
+     * @param string $function name of template function
+     */
+    public function unregister_function($function)
+    {
+        $this->unregisterPlugin('function', $function);
+    }
+
+    /**
+     * Registers object to be used in templates
+     *
+     * @param string  $object        name of template object
+     * @param object  $object_impl   the referenced PHP object to register
+     * @param array   $allowed       list of allowed methods (empty = all)
+     * @param boolean $smarty_args   smarty argument format, else traditional
+     * @param array   $block_methods list of methods that are block format
+     *
+     * @throws SmartyException
+     * @internal param array $block_functs list of methods that are block format
+     */
+    public function register_object($object, $object_impl, $allowed = array(), $smarty_args = true,
+                                    $block_methods = array())
+    {
+        settype($allowed, 'array');
+        settype($smarty_args, 'boolean');
+        $this->registerObject($object, $object_impl, $allowed, $smarty_args, $block_methods);
+    }
+
+    /**
+     * Unregister object
+     *
+     * @param string $object name of template object
+     */
+    public function unregister_object($object)
+    {
+        $this->unregisterObject($object);
+    }
+
+    /**
+     * Registers block function to be used in templates
+     *
+     * @param string $block      name of template block
+     * @param string $block_impl PHP function to register
+     * @param bool   $cacheable
+     * @param mixed  $cache_attrs
+     */
+    public function register_block($block, $block_impl, $cacheable = true, $cache_attrs = null)
+    {
+        $this->registerPlugin('block', $block, $block_impl, $cacheable, $cache_attrs);
+    }
+
+    /**
+     * Unregister block function
+     *
+     * @param string $block name of template function
+     */
+    public function unregister_block($block)
+    {
+        $this->unregisterPlugin('block', $block);
+    }
+
+    /**
+     * Registers compiler function
+     *
+     * @param string $function      name of template function
+     * @param string $function_impl name of PHP function to register
+     * @param bool   $cacheable
+     */
+    public function register_compiler_function($function, $function_impl, $cacheable = true)
+    {
+        $this->registerPlugin('compiler', $function, $function_impl, $cacheable);
+    }
+
+    /**
+     * Unregister compiler function
+     *
+     * @param string $function name of template function
+     */
+    public function unregister_compiler_function($function)
+    {
+        $this->unregisterPlugin('compiler', $function);
+    }
+
+    /**
+     * Registers modifier to be used in templates
+     *
+     * @param string $modifier      name of template modifier
+     * @param string $modifier_impl name of PHP function to register
+     */
+    public function register_modifier($modifier, $modifier_impl)
+    {
+        $this->registerPlugin('modifier', $modifier, $modifier_impl);
+    }
+
+    /**
+     * Unregister modifier
+     *
+     * @param string $modifier name of template modifier
+     */
+    public function unregister_modifier($modifier)
+    {
+        $this->unregisterPlugin('modifier', $modifier);
+    }
+
+    /**
+     * Registers a resource to fetch a template
+     *
+     * @param string $type      name of resource
+     * @param array  $functions array of functions to handle resource
+     */
+    public function register_resource($type, $functions)
+    {
+        $this->registerResource($type, $functions);
+    }
+
+    /**
+     * Unregister a resource
+     *
+     * @param string $type name of resource
+     */
+    public function unregister_resource($type)
+    {
+        $this->unregisterResource($type);
+    }
+
+    /**
+     * Registers a prefilter function to apply
+     * to a template before compiling
+     *
+     * @param callable $function
+     */
+    public function register_prefilter($function)
+    {
+        $this->registerFilter('pre', $function);
+    }
+
+    /**
+     * Unregister a prefilter function
+     *
+     * @param callable $function
+     */
+    public function unregister_prefilter($function)
+    {
+        $this->unregisterFilter('pre', $function);
+    }
+
+    /**
+     * Registers a postfilter function to apply
+     * to a compiled template after compilation
+     *
+     * @param callable $function
+     */
+    public function register_postfilter($function)
+    {
+        $this->registerFilter('post', $function);
+    }
+
+    /**
+     * Unregister a postfilter function
+     *
+     * @param callable $function
+     */
+    public function unregister_postfilter($function)
+    {
+        $this->unregisterFilter('post', $function);
+    }
+
+    /**
+     * Registers an output filter function to apply
+     * to a template output
+     *
+     * @param callable $function
+     */
+    public function register_outputfilter($function)
+    {
+        $this->registerFilter('output', $function);
+    }
+
+    /**
+     * Unregister an outputfilter function
+     *
+     * @param callable $function
+     */
+    public function unregister_outputfilter($function)
+    {
+        $this->unregisterFilter('output', $function);
+    }
+
+    /**
+     * load a filter of specified type and name
+     *
+     * @param string $type filter type
+     * @param string $name filter name
+     */
+    public function load_filter($type, $name)
+    {
+        $this->loadFilter($type, $name);
+    }
+
+    /**
+     * clear cached content for the given template and cache id
+     *
+     * @param  string $tpl_file   name of template file
+     * @param  string $cache_id   name of cache_id
+     * @param  string $compile_id name of compile_id
+     * @param  string $exp_time   expiration time
+     *
+     * @return boolean
+     */
+    public function clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null)
+    {
+        return $this->clearCache($tpl_file, $cache_id, $compile_id, $exp_time);
+    }
+
+    /**
+     * clear the entire contents of cache (all templates)
+     *
+     * @param  string $exp_time expire time
+     *
+     * @return boolean
+     */
+    public function clear_all_cache($exp_time = null)
+    {
+        return $this->clearCache(null, null, null, $exp_time);
+    }
+
+    /**
+     * test to see if valid cache exists for this template
+     *
+     * @param  string $tpl_file name of template file
+     * @param  string $cache_id
+     * @param  string $compile_id
+     *
+     * @return boolean
+     */
+    public function is_cached($tpl_file, $cache_id = null, $compile_id = null)
+    {
+        return $this->isCached($tpl_file, $cache_id, $compile_id);
+    }
+
+    /**
+     * clear all the assigned template variables.
+     */
+    public function clear_all_assign()
+    {
+        $this->clearAllAssign();
+    }
+
+    /**
+     * clears compiled version of specified template resource,
+     * or all compiled template files if one is not specified.
+     * This function is for advanced use only, not normally needed.
+     *
+     * @param  string $tpl_file
+     * @param  string $compile_id
+     * @param  string $exp_time
+     *
+     * @return boolean results of {@link smarty_core_rm_auto()}
+     */
+    public function clear_compiled_tpl($tpl_file = null, $compile_id = null, $exp_time = null)
+    {
+        return $this->clearCompiledTemplate($tpl_file, $compile_id, $exp_time);
+    }
+
+    /**
+     * Checks whether requested template exists.
+     *
+     * @param  string $tpl_file
+     *
+     * @return boolean
+     */
+    public function template_exists($tpl_file)
+    {
+        return $this->templateExists($tpl_file);
+    }
+
+    /**
+     * Returns an array containing template variables
+     *
+     * @param  string $name
+     *
+     * @return array
+     */
+    public function get_template_vars($name = null)
+    {
+        return $this->getTemplateVars($name);
+    }
+
+    /**
+     * Returns an array containing config variables
+     *
+     * @param  string $name
+     *
+     * @return array
+     */
+    public function get_config_vars($name = null)
+    {
+        return $this->getConfigVars($name);
+    }
+
+    /**
+     * load configuration values
+     *
+     * @param string $file
+     * @param string $section
+     * @param string $scope
+     */
+    public function config_load($file, $section = null, $scope = 'global')
+    {
+        $this->ConfigLoad($file, $section, $scope);
+    }
+
+    /**
+     * return a reference to a registered object
+     *
+     * @param  string $name
+     *
+     * @return object
+     */
+    public function get_registered_object($name)
+    {
+        return $this->getRegisteredObject($name);
+    }
+
+    /**
+     * clear configuration values
+     *
+     * @param string $var
+     */
+    public function clear_config($var = null)
+    {
+        $this->clearConfig($var);
+    }
+
+    /**
+     * trigger Smarty error
+     *
+     * @param string  $error_msg
+     * @param integer $error_type
+     */
+    public function trigger_error($error_msg, $error_type = E_USER_WARNING)
+    {
+        trigger_error("Smarty error: $error_msg", $error_type);
+    }
+}

+ 17 - 0
Mall/Framework/Smarty/libs/bootstrap.php

@@ -0,0 +1,17 @@
+<?php
+/*
+ * This file is part of the Smarty package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/*
+ * Load and register Smarty Autoloader
+ */
+if (!class_exists('Smarty_Autoloader')) {
+    require dirname(__FILE__) . '/Autoloader.php';
+}
+Smarty_Autoloader::register();

+ 160 - 0
Mall/Framework/Smarty/libs/debug.tpl

@@ -0,0 +1,160 @@
+{capture name='_smarty_debug' assign=debug_output}
+    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+    <head>
+        <title>Smarty Debug Console</title>
+        <style type="text/css">
+            {literal}
+            body, h1, h2, h3, td, th, p {
+                font-family: sans-serif;
+                font-weight: normal;
+                font-size: 0.9em;
+                margin: 1px;
+                padding: 0;
+            }
+
+            h1 {
+                margin: 0;
+                text-align: left;
+                padding: 2px;
+                background-color: #f0c040;
+                color: black;
+                font-weight: bold;
+                font-size: 1.2em;
+            }
+
+            h2 {
+                background-color: #9B410E;
+                color: white;
+                text-align: left;
+                font-weight: bold;
+                padding: 2px;
+                border-top: 1px solid black;
+            }
+            h3 {
+                text-align: left;
+                font-weight: bold;
+                color: black;
+                font-size: 0.7em;
+                padding: 2px;
+            }
+
+            body {
+                background: black;
+            }
+
+            p, table, div {
+                background: #f0ead8;
+            }
+
+            p {
+                margin: 0;
+                font-style: italic;
+                text-align: center;
+            }
+
+            table {
+                width: 100%;
+            }
+
+            th, td {
+                font-family: monospace;
+                vertical-align: top;
+                text-align: left;
+            }
+
+            td {
+                color: green;
+            }
+
+            .odd {
+                background-color: #eeeeee;
+            }
+
+            .even {
+                background-color: #fafafa;
+            }
+
+            .exectime {
+                font-size: 0.8em;
+                font-style: italic;
+            }
+
+            #bold div {
+                color: black;
+                font-weight: bold;
+            }
+            #blue h3 {
+                color: blue;
+            }
+            #normal div {
+                color: black;
+                font-weight: normal;
+            }
+            #table_assigned_vars th {
+                color: blue;
+                font-weight: bold;
+            }
+
+            #table_config_vars th {
+                color: maroon;
+            }
+
+            {/literal}
+        </style>
+    </head>
+    <body>
+
+    <h1>Smarty {Smarty::SMARTY_VERSION} Debug Console
+        -  {if isset($template_name)}{$template_name|debug_print_var nofilter} {/if}{if !empty($template_data)}Total Time {$execution_time|string_format:"%.5f"}{/if}</h1>
+
+    {if !empty($template_data)}
+        <h2>included templates &amp; config files (load time in seconds)</h2>
+        <div>
+            {foreach $template_data as $template}
+                <font color=brown>{$template.name}</font>
+                <br>&nbsp;&nbsp;<span class="exectime">
+                (compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"})
+                 </span>
+                <br>
+            {/foreach}
+        </div>
+    {/if}
+
+    <h2>assigned template variables</h2>
+
+    <table id="table_assigned_vars">
+        {foreach $assigned_vars as $vars}
+            <tr class="{if $vars@iteration % 2 eq 0}odd{else}even{/if}">
+                <td><h3><font color=blue>${$vars@key}</font></h3>
+                    {if isset($vars['nocache'])}<b>Nocache</b></br>{/if}
+                    {if isset($vars['scope'])}<b>Origin:</b> {$vars['scope']|debug_print_var nofilter}{/if}
+                </td>
+                <td><h3>Value</h3>{$vars['value']|debug_print_var:10:80 nofilter}</td>
+                <td>{if isset($vars['attributes'])}<h3>Attributes</h3>{$vars['attributes']|debug_print_var nofilter} {/if}</td>
+         {/foreach}
+    </table>
+
+    <h2>assigned config file variables</h2>
+
+    <table id="table_config_vars">
+        {foreach $config_vars as $vars}
+            <tr class="{if $vars@iteration % 2 eq 0}odd{else}even{/if}">
+                <td><h3><font color=blue>#{$vars@key}#</font></h3>
+                    {if isset($vars['scope'])}<b>Origin:</b> {$vars['scope']|debug_print_var nofilter}{/if}
+                </td>
+                <td>{$vars['value']|debug_print_var:10:80 nofilter}</td>
+            </tr>
+        {/foreach}
+
+    </table>
+    </body>
+    </html>
+{/capture}
+<script type="text/javascript">
+    {$id = '__Smarty__'}
+    {if $display_mode}{$id = "$offset$template_name"|md5}{/if}
+    _smarty_console = window.open("", "console{$id}", "width=1024,height=600,left={$offset},top={$offset},resizable,scrollbars=yes");
+    _smarty_console.document.write("{$debug_output|escape:'javascript' nofilter}");
+    _smarty_console.document.close();
+</script>

+ 117 - 0
Mall/Framework/Smarty/libs/plugins/block.textformat.php

@@ -0,0 +1,117 @@
+<?php
+/**
+ * Smarty plugin to format text blocks
+ *
+ * @package    Smarty
+ * @subpackage PluginsBlock
+ */
+
+/**
+ * Smarty {textformat}{/textformat} block plugin
+ * Type:     block function<br>
+ * Name:     textformat<br>
+ * Purpose:  format text a certain way with preset styles
+ *           or custom wrap/indent settings<br>
+ * Params:
+ * <pre>
+ * - style         - string (email)
+ * - indent        - integer (0)
+ * - wrap          - integer (80)
+ * - wrap_char     - string ("\n")
+ * - indent_char   - string (" ")
+ * - wrap_boundary - boolean (true)
+ * </pre>
+ *
+ * @link   http://www.smarty.net/manual/en/language.function.textformat.php {textformat}
+ *         (Smarty online manual)
+ *
+ * @param array                    $params   parameters
+ * @param string                   $content  contents of the block
+ * @param Smarty_Internal_Template $template template object
+ * @param boolean                  &$repeat  repeat flag
+ *
+ * @return string content re-formatted
+ * @author Monte Ohrt <monte at ohrt dot com>
+ */
+function smarty_block_textformat($params, $content, $template, &$repeat)
+{
+    if (is_null($content)) {
+        return;
+    }
+    if (!isset($template->smarty->_cache[ '_required_smw' ])) {
+        require_once(SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php');
+        $template->smarty->_cache[ '_required_smw' ] = true;
+    }
+
+    $style = null;
+    $indent = 0;
+    $indent_first = 0;
+    $indent_char = ' ';
+    $wrap = 80;
+    $wrap_char = "\n";
+    $wrap_cut = false;
+    $assign = null;
+
+    foreach ($params as $_key => $_val) {
+        switch ($_key) {
+            case 'style':
+            case 'indent_char':
+            case 'wrap_char':
+            case 'assign':
+                $$_key = (string) $_val;
+                break;
+
+            case 'indent':
+            case 'indent_first':
+            case 'wrap':
+                $$_key = (int) $_val;
+                break;
+
+            case 'wrap_cut':
+                $$_key = (bool) $_val;
+                break;
+
+            default:
+                trigger_error("textformat: unknown attribute '$_key'");
+        }
+    }
+
+    if ($style == 'email') {
+        $wrap = 72;
+    }
+    // split into paragraphs
+    $_paragraphs = preg_split('![\r\n]{2}!', $content);
+
+    foreach ($_paragraphs as &$_paragraph) {
+        if (!$_paragraph) {
+            continue;
+        }
+        // convert mult. spaces & special chars to single space
+        $_paragraph =
+            preg_replace(array('!\s+!' . Smarty::$_UTF8_MODIFIER,
+                               '!(^\s+)|(\s+$)!' . Smarty::$_UTF8_MODIFIER),
+                         array(' ',
+                               ''), $_paragraph);
+        // indent first line
+        if ($indent_first > 0) {
+            $_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph;
+        }
+        // wordwrap sentences
+        if (Smarty::$_MBSTRING) {
+            $_paragraph = smarty_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut);
+        } else {
+            $_paragraph = wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut);
+        }
+        // indent lines
+        if ($indent > 0) {
+            $_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph);
+        }
+    }
+    $_output = implode($wrap_char . $wrap_char, $_paragraphs);
+
+    if ($assign) {
+        $template->assign($assign, $_output);
+    } else {
+        return $_output;
+    }
+}

+ 73 - 0
Mall/Framework/Smarty/libs/plugins/function.counter.php

@@ -0,0 +1,73 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {counter} function plugin
+ * Type:     function<br>
+ * Name:     counter<br>
+ * Purpose:  print out a counter value
+ *
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @link   http://www.smarty.net/manual/en/language.function.counter.php {counter}
+ *         (Smarty online manual)
+ *
+ * @param array                    $params   parameters
+ * @param Smarty_Internal_Template $template template object
+ *
+ * @return string|null
+ */
+function smarty_function_counter($params, $template)
+{
+    static $counters = array();
+
+    $name = (isset($params[ 'name' ])) ? $params[ 'name' ] : 'default';
+    if (!isset($counters[ $name ])) {
+        $counters[ $name ] = array('start' => 1, 'skip' => 1, 'direction' => 'up', 'count' => 1);
+    }
+    $counter =& $counters[ $name ];
+
+    if (isset($params[ 'start' ])) {
+        $counter[ 'start' ] = $counter[ 'count' ] = (int) $params[ 'start' ];
+    }
+
+    if (!empty($params[ 'assign' ])) {
+        $counter[ 'assign' ] = $params[ 'assign' ];
+    }
+
+    if (isset($counter[ 'assign' ])) {
+        $template->assign($counter[ 'assign' ], $counter[ 'count' ]);
+    }
+
+    if (isset($params[ 'print' ])) {
+        $print = (bool) $params[ 'print' ];
+    } else {
+        $print = empty($counter[ 'assign' ]);
+    }
+
+    if ($print) {
+        $retval = $counter[ 'count' ];
+    } else {
+        $retval = null;
+    }
+
+    if (isset($params[ 'skip' ])) {
+        $counter[ 'skip' ] = $params[ 'skip' ];
+    }
+
+    if (isset($params[ 'direction' ])) {
+        $counter[ 'direction' ] = $params[ 'direction' ];
+    }
+
+    if ($counter[ 'direction' ] == "down") {
+        $counter[ 'count' ] -= $counter[ 'skip' ];
+    } else {
+        $counter[ 'count' ] += $counter[ 'skip' ];
+    }
+
+    return $retval;
+}

+ 105 - 0
Mall/Framework/Smarty/libs/plugins/function.cycle.php

@@ -0,0 +1,105 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {cycle} function plugin
+ * Type:     function<br>
+ * Name:     cycle<br>
+ * Date:     May 3, 2002<br>
+ * Purpose:  cycle through given values<br>
+ * Params:
+ * <pre>
+ * - name      - name of cycle (optional)
+ * - values    - comma separated list of values to cycle, or an array of values to cycle
+ *               (this can be left out for subsequent calls)
+ * - reset     - boolean - resets given var to true
+ * - print     - boolean - print var or not. default is true
+ * - advance   - boolean - whether or not to advance the cycle
+ * - delimiter - the value delimiter, default is ","
+ * - assign    - boolean, assigns to template var instead of printed.
+ * </pre>
+ * Examples:<br>
+ * <pre>
+ * {cycle values="#eeeeee,#d0d0d0d"}
+ * {cycle name=row values="one,two,three" reset=true}
+ * {cycle name=row}
+ * </pre>
+ *
+ * @link     http://www.smarty.net/manual/en/language.function.cycle.php {cycle}
+ *           (Smarty online manual)
+ * @author   Monte Ohrt <monte at ohrt dot com>
+ * @author   credit to Mark Priatel <mpriatel@rogers.com>
+ * @author   credit to Gerard <gerard@interfold.com>
+ * @author   credit to Jason Sweat <jsweat_php@yahoo.com>
+ * @version  1.3
+ *
+ * @param array                    $params   parameters
+ * @param Smarty_Internal_Template $template template object
+ *
+ * @return string|null
+ */
+
+function smarty_function_cycle($params, $template)
+{
+    static $cycle_vars;
+
+    $name = (empty($params[ 'name' ])) ? 'default' : $params[ 'name' ];
+    $print = (isset($params[ 'print' ])) ? (bool) $params[ 'print' ] : true;
+    $advance = (isset($params[ 'advance' ])) ? (bool) $params[ 'advance' ] : true;
+    $reset = (isset($params[ 'reset' ])) ? (bool) $params[ 'reset' ] : false;
+
+    if (!isset($params[ 'values' ])) {
+        if (!isset($cycle_vars[ $name ][ 'values' ])) {
+            trigger_error("cycle: missing 'values' parameter");
+
+            return;
+        }
+    } else {
+        if (isset($cycle_vars[ $name ][ 'values' ]) && $cycle_vars[ $name ][ 'values' ] != $params[ 'values' ]) {
+            $cycle_vars[ $name ][ 'index' ] = 0;
+        }
+        $cycle_vars[ $name ][ 'values' ] = $params[ 'values' ];
+    }
+
+    if (isset($params[ 'delimiter' ])) {
+        $cycle_vars[ $name ][ 'delimiter' ] = $params[ 'delimiter' ];
+    } elseif (!isset($cycle_vars[ $name ][ 'delimiter' ])) {
+        $cycle_vars[ $name ][ 'delimiter' ] = ',';
+    }
+
+    if (is_array($cycle_vars[ $name ][ 'values' ])) {
+        $cycle_array = $cycle_vars[ $name ][ 'values' ];
+    } else {
+        $cycle_array = explode($cycle_vars[ $name ][ 'delimiter' ], $cycle_vars[ $name ][ 'values' ]);
+    }
+
+    if (!isset($cycle_vars[ $name ][ 'index' ]) || $reset) {
+        $cycle_vars[ $name ][ 'index' ] = 0;
+    }
+
+    if (isset($params[ 'assign' ])) {
+        $print = false;
+        $template->assign($params[ 'assign' ], $cycle_array[ $cycle_vars[ $name ][ 'index' ] ]);
+    }
+
+    if ($print) {
+        $retval = $cycle_array[ $cycle_vars[ $name ][ 'index' ] ];
+    } else {
+        $retval = null;
+    }
+
+    if ($advance) {
+        if ($cycle_vars[ $name ][ 'index' ] >= count($cycle_array) - 1) {
+            $cycle_vars[ $name ][ 'index' ] = 0;
+        } else {
+            $cycle_vars[ $name ][ 'index' ] ++;
+        }
+    }
+
+    return $retval;
+}

+ 221 - 0
Mall/Framework/Smarty/libs/plugins/function.fetch.php

@@ -0,0 +1,221 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {fetch} plugin
+ * Type:     function<br>
+ * Name:     fetch<br>
+ * Purpose:  fetch file, web or ftp data and display results
+ *
+ * @link   http://www.smarty.net/manual/en/language.function.fetch.php {fetch}
+ *         (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param array                    $params   parameters
+ * @param Smarty_Internal_Template $template template object
+ *
+ * @throws SmartyException
+ * @return string|null if the assign parameter is passed, Smarty assigns the result to a template variable
+ */
+function smarty_function_fetch($params, $template)
+{
+    if (empty($params[ 'file' ])) {
+        trigger_error("[plugin] fetch parameter 'file' cannot be empty", E_USER_NOTICE);
+
+        return;
+    }
+
+    // strip file protocol
+    if (stripos($params[ 'file' ], 'file://') === 0) {
+        $params[ 'file' ] = substr($params[ 'file' ], 7);
+    }
+
+    $protocol = strpos($params[ 'file' ], '://');
+    if ($protocol !== false) {
+        $protocol = strtolower(substr($params[ 'file' ], 0, $protocol));
+    }
+
+    if (isset($template->smarty->security_policy)) {
+        if ($protocol) {
+            // remote resource (or php stream, …)
+            if (!$template->smarty->security_policy->isTrustedUri($params[ 'file' ])) {
+                return;
+            }
+        } else {
+            // local file
+            if (!$template->smarty->security_policy->isTrustedResourceDir($params[ 'file' ])) {
+                return;
+            }
+        }
+    }
+
+    $content = '';
+    if ($protocol == 'http') {
+        // http fetch
+        if ($uri_parts = parse_url($params[ 'file' ])) {
+            // set defaults
+            $host = $server_name = $uri_parts[ 'host' ];
+            $timeout = 30;
+            $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*";
+            $agent = "Smarty Template Engine " . Smarty::SMARTY_VERSION;
+            $referer = "";
+            $uri = !empty($uri_parts[ 'path' ]) ? $uri_parts[ 'path' ] : '/';
+            $uri .= !empty($uri_parts[ 'query' ]) ? '?' . $uri_parts[ 'query' ] : '';
+            $_is_proxy = false;
+            if (empty($uri_parts[ 'port' ])) {
+                $port = 80;
+            } else {
+                $port = $uri_parts[ 'port' ];
+            }
+            if (!empty($uri_parts[ 'user' ])) {
+                $user = $uri_parts[ 'user' ];
+            }
+            if (!empty($uri_parts[ 'pass' ])) {
+                $pass = $uri_parts[ 'pass' ];
+            }
+            // loop through parameters, setup headers
+            foreach ($params as $param_key => $param_value) {
+                switch ($param_key) {
+                    case "file":
+                    case "assign":
+                    case "assign_headers":
+                        break;
+                    case "user":
+                        if (!empty($param_value)) {
+                            $user = $param_value;
+                        }
+                        break;
+                    case "pass":
+                        if (!empty($param_value)) {
+                            $pass = $param_value;
+                        }
+                        break;
+                    case "accept":
+                        if (!empty($param_value)) {
+                            $accept = $param_value;
+                        }
+                        break;
+                    case "header":
+                        if (!empty($param_value)) {
+                            if (!preg_match('![\w\d-]+: .+!', $param_value)) {
+                                trigger_error("[plugin] invalid header format '" . $param_value . "'", E_USER_NOTICE);
+
+                                return;
+                            } else {
+                                $extra_headers[] = $param_value;
+                            }
+                        }
+                        break;
+                    case "proxy_host":
+                        if (!empty($param_value)) {
+                            $proxy_host = $param_value;
+                        }
+                        break;
+                    case "proxy_port":
+                        if (!preg_match('!\D!', $param_value)) {
+                            $proxy_port = (int) $param_value;
+                        } else {
+                            trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE);
+
+                            return;
+                        }
+                        break;
+                    case "agent":
+                        if (!empty($param_value)) {
+                            $agent = $param_value;
+                        }
+                        break;
+                    case "referer":
+                        if (!empty($param_value)) {
+                            $referer = $param_value;
+                        }
+                        break;
+                    case "timeout":
+                        if (!preg_match('!\D!', $param_value)) {
+                            $timeout = (int) $param_value;
+                        } else {
+                            trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE);
+
+                            return;
+                        }
+                        break;
+                    default:
+                        trigger_error("[plugin] unrecognized attribute '" . $param_key . "'", E_USER_NOTICE);
+
+                        return;
+                }
+            }
+            if (!empty($proxy_host) && !empty($proxy_port)) {
+                $_is_proxy = true;
+                $fp = fsockopen($proxy_host, $proxy_port, $errno, $errstr, $timeout);
+            } else {
+                $fp = fsockopen($server_name, $port, $errno, $errstr, $timeout);
+            }
+
+            if (!$fp) {
+                trigger_error("[plugin] unable to fetch: $errstr ($errno)", E_USER_NOTICE);
+
+                return;
+            } else {
+                if ($_is_proxy) {
+                    fputs($fp, 'GET ' . $params[ 'file' ] . " HTTP/1.0\r\n");
+                } else {
+                    fputs($fp, "GET $uri HTTP/1.0\r\n");
+                }
+                if (!empty($host)) {
+                    fputs($fp, "Host: $host\r\n");
+                }
+                if (!empty($accept)) {
+                    fputs($fp, "Accept: $accept\r\n");
+                }
+                if (!empty($agent)) {
+                    fputs($fp, "User-Agent: $agent\r\n");
+                }
+                if (!empty($referer)) {
+                    fputs($fp, "Referer: $referer\r\n");
+                }
+                if (isset($extra_headers) && is_array($extra_headers)) {
+                    foreach ($extra_headers as $curr_header) {
+                        fputs($fp, $curr_header . "\r\n");
+                    }
+                }
+                if (!empty($user) && !empty($pass)) {
+                    fputs($fp, "Authorization: BASIC " . base64_encode("$user:$pass") . "\r\n");
+                }
+
+                fputs($fp, "\r\n");
+                while (!feof($fp)) {
+                    $content .= fgets($fp, 4096);
+                }
+                fclose($fp);
+                $csplit = preg_split("!\r\n\r\n!", $content, 2);
+
+                $content = $csplit[ 1 ];
+
+                if (!empty($params[ 'assign_headers' ])) {
+                    $template->assign($params[ 'assign_headers' ], preg_split("!\r\n!", $csplit[ 0 ]));
+                }
+            }
+        } else {
+            trigger_error("[plugin fetch] unable to parse URL, check syntax", E_USER_NOTICE);
+
+            return;
+        }
+    } else {
+        $content = @file_get_contents($params[ 'file' ]);
+        if ($content === false) {
+            throw new SmartyException("{fetch} cannot read resource '" . $params[ 'file' ] . "'");
+        }
+    }
+
+    if (!empty($params[ 'assign' ])) {
+        $template->assign($params[ 'assign' ], $content);
+    } else {
+        return $content;
+    }
+}

+ 252 - 0
Mall/Framework/Smarty/libs/plugins/function.html_checkboxes.php

@@ -0,0 +1,252 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {html_checkboxes} function plugin
+ * File:       function.html_checkboxes.php<br>
+ * Type:       function<br>
+ * Name:       html_checkboxes<br>
+ * Date:       24.Feb.2003<br>
+ * Purpose:    Prints out a list of checkbox input types<br>
+ * Examples:
+ * <pre>
+ * {html_checkboxes values=$ids output=$names}
+ * {html_checkboxes values=$ids name='box' separator='<br>' output=$names}
+ * {html_checkboxes values=$ids checked=$checked separator='<br>' output=$names}
+ * </pre>
+ * Params:
+ * <pre>
+ * - name       (optional) - string default "checkbox"
+ * - values     (required) - array
+ * - options    (optional) - associative array
+ * - checked    (optional) - array default not set
+ * - separator  (optional) - ie <br> or &nbsp;
+ * - output     (optional) - the output next to each checkbox
+ * - assign     (optional) - assign the output as an array to this variable
+ * - escape     (optional) - escape the content (not value), defaults to true
+ * </pre>
+ *
+ * @link       http://www.smarty.net/manual/en/language.function.html.checkboxes.php {html_checkboxes}
+ *             (Smarty online manual)
+ * @author     Christopher Kvarme <christopher.kvarme@flashjab.com>
+ * @author     credits to Monte Ohrt <monte at ohrt dot com>
+ * @version    1.0
+ *
+ * @param array  $params   parameters
+ * @param object $template template object
+ *
+ * @return string
+ * @uses       smarty_function_escape_special_chars()
+ */
+function smarty_function_html_checkboxes($params, $template)
+{
+    if (!isset($template->smarty->_cache[ '_required_sesc' ])) {
+        require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php');
+        $template->smarty->_cache[ '_required_sesc' ] = true;
+    }
+
+    $name = 'checkbox';
+    $values = null;
+    $options = null;
+    $selected = array();
+    $separator = '';
+    $escape = true;
+    $labels = true;
+    $label_ids = false;
+    $output = null;
+
+    $extra = '';
+
+    foreach ($params as $_key => $_val) {
+        switch ($_key) {
+            case 'name':
+            case 'separator':
+                $$_key = (string) $_val;
+                break;
+
+            case 'escape':
+            case 'labels':
+            case 'label_ids':
+                $$_key = (bool) $_val;
+                break;
+
+            case 'options':
+                $$_key = (array) $_val;
+                break;
+
+            case 'values':
+            case 'output':
+                $$_key = array_values((array) $_val);
+                break;
+
+            case 'checked':
+            case 'selected':
+                if (is_array($_val)) {
+                    $selected = array();
+                    foreach ($_val as $_sel) {
+                        if (is_object($_sel)) {
+                            if (method_exists($_sel, "__toString")) {
+                                $_sel = smarty_function_escape_special_chars((string) $_sel->__toString());
+                            } else {
+                                trigger_error("html_checkboxes: selected attribute contains an object of class '" .
+                                              get_class($_sel) . "' without __toString() method", E_USER_NOTICE);
+                                continue;
+                            }
+                        } else {
+                            $_sel = smarty_function_escape_special_chars((string) $_sel);
+                        }
+                        $selected[ $_sel ] = true;
+                    }
+                } elseif (is_object($_val)) {
+                    if (method_exists($_val, "__toString")) {
+                        $selected = smarty_function_escape_special_chars((string) $_val->__toString());
+                    } else {
+                        trigger_error("html_checkboxes: selected attribute is an object of class '" . get_class($_val) .
+                                      "' without __toString() method", E_USER_NOTICE);
+                    }
+                } else {
+                    $selected = smarty_function_escape_special_chars((string) $_val);
+                }
+                break;
+
+            case 'checkboxes':
+                trigger_error('html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead',
+                              E_USER_WARNING);
+                $options = (array) $_val;
+                break;
+
+            case 'assign':
+                break;
+
+            case 'strict':
+                break;
+
+            case 'disabled':
+            case 'readonly':
+                if (!empty($params[ 'strict' ])) {
+                    if (!is_scalar($_val)) {
+                        trigger_error("html_options: $_key attribute must be a scalar, only boolean true or string '$_key' will actually add the attribute",
+                                      E_USER_NOTICE);
+                    }
+
+                    if ($_val === true || $_val === $_key) {
+                        $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"';
+                    }
+
+                    break;
+                }
+            // omit break; to fall through!
+
+            default:
+                if (!is_array($_val)) {
+                    $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"';
+                } else {
+                    trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                }
+                break;
+        }
+    }
+
+    if (!isset($options) && !isset($values)) {
+        return '';
+    } /* raise error here? */
+
+    $_html_result = array();
+
+    if (isset($options)) {
+        foreach ($options as $_key => $_val) {
+            $_html_result[] =
+                smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels,
+                                                       $label_ids, $escape);
+        }
+    } else {
+        foreach ($values as $_i => $_key) {
+            $_val = isset($output[ $_i ]) ? $output[ $_i ] : '';
+            $_html_result[] =
+                smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels,
+                                                       $label_ids, $escape);
+        }
+    }
+
+    if (!empty($params[ 'assign' ])) {
+        $template->assign($params[ 'assign' ], $_html_result);
+    } else {
+        return implode("\n", $_html_result);
+    }
+}
+
+function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels,
+                                                $label_ids, $escape = true)
+{
+    $_output = '';
+
+    if (is_object($value)) {
+        if (method_exists($value, "__toString")) {
+            $value = (string) $value->__toString();
+        } else {
+            trigger_error("html_options: value is an object of class '" . get_class($value) .
+                          "' without __toString() method", E_USER_NOTICE);
+
+            return '';
+        }
+    } else {
+        $value = (string) $value;
+    }
+
+    if (is_object($output)) {
+        if (method_exists($output, "__toString")) {
+            $output = (string) $output->__toString();
+        } else {
+            trigger_error("html_options: output is an object of class '" . get_class($output) .
+                          "' without __toString() method", E_USER_NOTICE);
+
+            return '';
+        }
+    } else {
+        $output = (string) $output;
+    }
+
+    if ($labels) {
+        if ($label_ids) {
+            $_id = smarty_function_escape_special_chars(preg_replace('![^\w\-\.]!' . Smarty::$_UTF8_MODIFIER, '_',
+                                                                     $name . '_' . $value));
+            $_output .= '<label for="' . $_id . '">';
+        } else {
+            $_output .= '<label>';
+        }
+    }
+
+    $name = smarty_function_escape_special_chars($name);
+    $value = smarty_function_escape_special_chars($value);
+    if ($escape) {
+        $output = smarty_function_escape_special_chars($output);
+    }
+
+    $_output .= '<input type="checkbox" name="' . $name . '[]" value="' . $value . '"';
+
+    if ($labels && $label_ids) {
+        $_output .= ' id="' . $_id . '"';
+    }
+
+    if (is_array($selected)) {
+        if (isset($selected[ $value ])) {
+            $_output .= ' checked="checked"';
+        }
+    } elseif ($value === $selected) {
+        $_output .= ' checked="checked"';
+    }
+
+    $_output .= $extra . ' />' . $output;
+    if ($labels) {
+        $_output .= '</label>';
+    }
+
+    $_output .= $separator;
+
+    return $_output;
+}

+ 167 - 0
Mall/Framework/Smarty/libs/plugins/function.html_image.php

@@ -0,0 +1,167 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {html_image} function plugin
+ * Type:     function<br>
+ * Name:     html_image<br>
+ * Date:     Feb 24, 2003<br>
+ * Purpose:  format HTML tags for the image<br>
+ * Examples: {html_image file="/images/masthead.gif"}<br>
+ * Output:   <img src="/images/masthead.gif" width=400 height=23><br>
+ * Params:
+ * <pre>
+ * - file        - (required) - file (and path) of image
+ * - height      - (optional) - image height (default actual height)
+ * - width       - (optional) - image width (default actual width)
+ * - basedir     - (optional) - base directory for absolute paths, default is environment variable DOCUMENT_ROOT
+ * - path_prefix - prefix for path output (optional, default empty)
+ * </pre>
+ *
+ * @link    http://www.smarty.net/manual/en/language.function.html.image.php {html_image}
+ *          (Smarty online manual)
+ * @author  Monte Ohrt <monte at ohrt dot com>
+ * @author  credits to Duda <duda@big.hu>
+ * @version 1.0
+ *
+ * @param array                    $params   parameters
+ * @param Smarty_Internal_Template $template template object
+ *
+ * @throws SmartyException
+ * @return string
+ * @uses    smarty_function_escape_special_chars()
+ */
+function smarty_function_html_image($params, $template)
+{
+    if (!isset($template->smarty->_cache[ '_required_sesc' ])) {
+        require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php');
+        $template->smarty->_cache[ '_required_sesc' ] = true;
+    }
+
+    $alt = '';
+    $file = '';
+    $height = '';
+    $width = '';
+    $extra = '';
+    $prefix = '';
+    $suffix = '';
+    $path_prefix = '';
+    $basedir = isset($_SERVER[ 'DOCUMENT_ROOT' ]) ? $_SERVER[ 'DOCUMENT_ROOT' ] : '';
+    foreach ($params as $_key => $_val) {
+        switch ($_key) {
+            case 'file':
+            case 'height':
+            case 'width':
+            case 'dpi':
+            case 'path_prefix':
+            case 'basedir':
+                $$_key = $_val;
+                break;
+
+            case 'alt':
+                if (!is_array($_val)) {
+                    $$_key = smarty_function_escape_special_chars($_val);
+                } else {
+                    throw new SmartyException ("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                }
+                break;
+
+            case 'link':
+            case 'href':
+                $prefix = '<a href="' . $_val . '">';
+                $suffix = '</a>';
+                break;
+
+            default:
+                if (!is_array($_val)) {
+                    $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"';
+                } else {
+                    throw new SmartyException ("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                }
+                break;
+        }
+    }
+
+    if (empty($file)) {
+        trigger_error("html_image: missing 'file' parameter", E_USER_NOTICE);
+
+        return;
+    }
+
+    if ($file[ 0 ] == '/') {
+        $_image_path = $basedir . $file;
+    } else {
+        $_image_path = $file;
+    }
+
+    // strip file protocol
+    if (stripos($params[ 'file' ], 'file://') === 0) {
+        $params[ 'file' ] = substr($params[ 'file' ], 7);
+    }
+
+    $protocol = strpos($params[ 'file' ], '://');
+    if ($protocol !== false) {
+        $protocol = strtolower(substr($params[ 'file' ], 0, $protocol));
+    }
+
+    if (isset($template->smarty->security_policy)) {
+        if ($protocol) {
+            // remote resource (or php stream, …)
+            if (!$template->smarty->security_policy->isTrustedUri($params[ 'file' ])) {
+                return;
+            }
+        } else {
+            // local file
+            if (!$template->smarty->security_policy->isTrustedResourceDir($_image_path)) {
+                return;
+            }
+        }
+    }
+
+    if (!isset($params[ 'width' ]) || !isset($params[ 'height' ])) {
+        // FIXME: (rodneyrehm) getimagesize() loads the complete file off a remote resource, use custom [jpg,png,gif]header reader!
+        if (!$_image_data = @getimagesize($_image_path)) {
+            if (!file_exists($_image_path)) {
+                trigger_error("html_image: unable to find '$_image_path'", E_USER_NOTICE);
+
+                return;
+            } elseif (!is_readable($_image_path)) {
+                trigger_error("html_image: unable to read '$_image_path'", E_USER_NOTICE);
+
+                return;
+            } else {
+                trigger_error("html_image: '$_image_path' is not a valid image file", E_USER_NOTICE);
+
+                return;
+            }
+        }
+
+        if (!isset($params[ 'width' ])) {
+            $width = $_image_data[ 0 ];
+        }
+        if (!isset($params[ 'height' ])) {
+            $height = $_image_data[ 1 ];
+        }
+    }
+
+    if (isset($params[ 'dpi' ])) {
+        if (strstr($_SERVER[ 'HTTP_USER_AGENT' ], 'Mac')) {
+            // FIXME: (rodneyrehm) wrong dpi assumption
+            // don't know who thought this up… even if it was true in 1998, it's definitely wrong in 2011.
+            $dpi_default = 72;
+        } else {
+            $dpi_default = 96;
+        }
+        $_resize = $dpi_default / $params[ 'dpi' ];
+        $width = round($width * $_resize);
+        $height = round($height * $_resize);
+    }
+
+    return $prefix . '<img src="' . $path_prefix . $file . '" alt="' . $alt . '" width="' . $width . '" height="' .
+           $height . '"' . $extra . ' />' . $suffix;
+}

+ 209 - 0
Mall/Framework/Smarty/libs/plugins/function.html_options.php

@@ -0,0 +1,209 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {html_options} function plugin
+ * Type:     function<br>
+ * Name:     html_options<br>
+ * Purpose:  Prints the list of <option> tags generated from
+ *           the passed parameters<br>
+ * Params:
+ * <pre>
+ * - name       (optional) - string default "select"
+ * - values     (required) - if no options supplied) - array
+ * - options    (required) - if no values supplied) - associative array
+ * - selected   (optional) - string default not set
+ * - output     (required) - if not options supplied) - array
+ * - id         (optional) - string default not set
+ * - class      (optional) - string default not set
+ * </pre>
+ *
+ * @link     http://www.smarty.net/manual/en/language.function.html.options.php {html_image}
+ *           (Smarty online manual)
+ * @author   Monte Ohrt <monte at ohrt dot com>
+ * @author   Ralf Strehle (minor optimization) <ralf dot strehle at yahoo dot de>
+ *
+ * @param array                     $params parameters
+ *
+ * @param \Smarty_Internal_Template $template
+ *
+ * @return string
+ * @uses     smarty_function_escape_special_chars()
+ */
+function smarty_function_html_options($params, Smarty_Internal_Template $template)
+{
+    if (!isset($template->smarty->_cache[ '_required_sesc' ])) {
+        require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php');
+        $template->smarty->_cache[ '_required_sesc' ] = true;
+    }
+
+    $name = null;
+    $values = null;
+    $options = null;
+    $selected = null;
+    $output = null;
+    $id = null;
+    $class = null;
+
+    $extra = '';
+
+    foreach ($params as $_key => $_val) {
+        switch ($_key) {
+            case 'name':
+            case 'class':
+            case 'id':
+                $$_key = (string) $_val;
+                break;
+
+            case 'options':
+                $options = (array) $_val;
+                break;
+
+            case 'values':
+            case 'output':
+                $$_key = array_values((array) $_val);
+                break;
+
+            case 'selected':
+                if (is_array($_val)) {
+                    $selected = array();
+                    foreach ($_val as $_sel) {
+                        if (is_object($_sel)) {
+                            if (method_exists($_sel, "__toString")) {
+                                $_sel = smarty_function_escape_special_chars((string) $_sel->__toString());
+                            } else {
+                                trigger_error("html_options: selected attribute contains an object of class '" .
+                                              get_class($_sel) . "' without __toString() method", E_USER_NOTICE);
+                                continue;
+                            }
+                        } else {
+                            $_sel = smarty_function_escape_special_chars((string) $_sel);
+                        }
+                        $selected[ $_sel ] = true;
+                    }
+                } elseif (is_object($_val)) {
+                    if (method_exists($_val, "__toString")) {
+                        $selected = smarty_function_escape_special_chars((string) $_val->__toString());
+                    } else {
+                        trigger_error("html_options: selected attribute is an object of class '" . get_class($_val) .
+                                      "' without __toString() method", E_USER_NOTICE);
+                    }
+                } else {
+                    $selected = smarty_function_escape_special_chars((string) $_val);
+                }
+                break;
+
+            case 'strict':
+                break;
+
+            case 'disabled':
+            case 'readonly':
+                if (!empty($params[ 'strict' ])) {
+                    if (!is_scalar($_val)) {
+                        trigger_error("html_options: $_key attribute must be a scalar, only boolean true or string '$_key' will actually add the attribute",
+                                      E_USER_NOTICE);
+                    }
+
+                    if ($_val === true || $_val === $_key) {
+                        $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"';
+                    }
+
+                    break;
+                }
+            // omit break; to fall through!
+
+            default:
+                if (!is_array($_val)) {
+                    $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"';
+                } else {
+                    trigger_error("html_options: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                }
+                break;
+        }
+    }
+
+    if (!isset($options) && !isset($values)) {
+        /* raise error here? */
+
+        return '';
+    }
+
+    $_html_result = '';
+    $_idx = 0;
+
+    if (isset($options)) {
+        foreach ($options as $_key => $_val) {
+            $_html_result .= smarty_function_html_options_optoutput($_key, $_val, $selected, $id, $class, $_idx);
+        }
+    } else {
+        foreach ($values as $_i => $_key) {
+            $_val = isset($output[ $_i ]) ? $output[ $_i ] : '';
+            $_html_result .= smarty_function_html_options_optoutput($_key, $_val, $selected, $id, $class, $_idx);
+        }
+    }
+
+    if (!empty($name)) {
+        $_html_class = !empty($class) ? ' class="' . $class . '"' : '';
+        $_html_id = !empty($id) ? ' id="' . $id . '"' : '';
+        $_html_result =
+            '<select name="' . $name . '"' . $_html_class . $_html_id . $extra . '>' . "\n" . $_html_result .
+            '</select>' . "\n";
+    }
+
+    return $_html_result;
+}
+
+function smarty_function_html_options_optoutput($key, $value, $selected, $id, $class, &$idx)
+{
+    if (!is_array($value)) {
+        $_key = smarty_function_escape_special_chars($key);
+        $_html_result = '<option value="' . $_key . '"';
+        if (is_array($selected)) {
+            if (isset($selected[ $_key ])) {
+                $_html_result .= ' selected="selected"';
+            }
+        } elseif ($_key === $selected) {
+            $_html_result .= ' selected="selected"';
+        }
+        $_html_class = !empty($class) ? ' class="' . $class . ' option"' : '';
+        $_html_id = !empty($id) ? ' id="' . $id . '-' . $idx . '"' : '';
+        if (is_object($value)) {
+            if (method_exists($value, "__toString")) {
+                $value = smarty_function_escape_special_chars((string) $value->__toString());
+            } else {
+                trigger_error("html_options: value is an object of class '" . get_class($value) .
+                              "' without __toString() method", E_USER_NOTICE);
+
+                return '';
+            }
+        } else {
+            $value = smarty_function_escape_special_chars((string) $value);
+        }
+        $_html_result .= $_html_class . $_html_id . '>' . $value . '</option>' . "\n";
+        $idx ++;
+    } else {
+        $_idx = 0;
+        $_html_result =
+            smarty_function_html_options_optgroup($key, $value, $selected, !empty($id) ? ($id . '-' . $idx) : null,
+                                                  $class, $_idx);
+        $idx ++;
+    }
+
+    return $_html_result;
+}
+
+function smarty_function_html_options_optgroup($key, $values, $selected, $id, $class, &$idx)
+{
+    $optgroup_html = '<optgroup label="' . smarty_function_escape_special_chars($key) . '">' . "\n";
+    foreach ($values as $key => $value) {
+        $optgroup_html .= smarty_function_html_options_optoutput($key, $value, $selected, $id, $class, $idx);
+    }
+    $optgroup_html .= "</optgroup>\n";
+
+    return $optgroup_html;
+}

+ 235 - 0
Mall/Framework/Smarty/libs/plugins/function.html_radios.php

@@ -0,0 +1,235 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {html_radios} function plugin
+ * File:       function.html_radios.php<br>
+ * Type:       function<br>
+ * Name:       html_radios<br>
+ * Date:       24.Feb.2003<br>
+ * Purpose:    Prints out a list of radio input types<br>
+ * Params:
+ * <pre>
+ * - name       (optional) - string default "radio"
+ * - values     (required) - array
+ * - options    (required) - associative array
+ * - checked    (optional) - array default not set
+ * - separator  (optional) - ie <br> or &nbsp;
+ * - output     (optional) - the output next to each radio button
+ * - assign     (optional) - assign the output as an array to this variable
+ * - escape     (optional) - escape the content (not value), defaults to true
+ * </pre>
+ * Examples:
+ * <pre>
+ * {html_radios values=$ids output=$names}
+ * {html_radios values=$ids name='box' separator='<br>' output=$names}
+ * {html_radios values=$ids checked=$checked separator='<br>' output=$names}
+ * </pre>
+ *
+ * @link    http://smarty.php.net/manual/en/language.function.html.radios.php {html_radios}
+ *          (Smarty online manual)
+ * @author  Christopher Kvarme <christopher.kvarme@flashjab.com>
+ * @author  credits to Monte Ohrt <monte at ohrt dot com>
+ * @version 1.0
+ *
+ * @param array                    $params   parameters
+ * @param Smarty_Internal_Template $template template object
+ *
+ * @return string
+ * @uses    smarty_function_escape_special_chars()
+ */
+function smarty_function_html_radios($params, $template)
+{
+    if (!isset($template->smarty->_cache[ '_required_sesc' ])) {
+        require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php');
+        $template->smarty->_cache[ '_required_sesc' ] = true;
+    }
+
+    $name = 'radio';
+    $values = null;
+    $options = null;
+    $selected = null;
+    $separator = '';
+    $escape = true;
+    $labels = true;
+    $label_ids = false;
+    $output = null;
+    $extra = '';
+
+    foreach ($params as $_key => $_val) {
+        switch ($_key) {
+            case 'name':
+            case 'separator':
+                $$_key = (string) $_val;
+                break;
+
+            case 'checked':
+            case 'selected':
+                if (is_array($_val)) {
+                    trigger_error('html_radios: the "' . $_key . '" attribute cannot be an array', E_USER_WARNING);
+                } elseif (is_object($_val)) {
+                    if (method_exists($_val, "__toString")) {
+                        $selected = smarty_function_escape_special_chars((string) $_val->__toString());
+                    } else {
+                        trigger_error("html_radios: selected attribute is an object of class '" . get_class($_val) .
+                                      "' without __toString() method", E_USER_NOTICE);
+                    }
+                } else {
+                    $selected = (string) $_val;
+                }
+                break;
+
+            case 'escape':
+            case 'labels':
+            case 'label_ids':
+                $$_key = (bool) $_val;
+                break;
+
+            case 'options':
+                $$_key = (array) $_val;
+                break;
+
+            case 'values':
+            case 'output':
+                $$_key = array_values((array) $_val);
+                break;
+
+            case 'radios':
+                trigger_error('html_radios: the use of the "radios" attribute is deprecated, use "options" instead',
+                              E_USER_WARNING);
+                $options = (array) $_val;
+                break;
+
+            case 'assign':
+                break;
+
+            case 'strict':
+                break;
+
+            case 'disabled':
+            case 'readonly':
+                if (!empty($params[ 'strict' ])) {
+                    if (!is_scalar($_val)) {
+                        trigger_error("html_options: $_key attribute must be a scalar, only boolean true or string '$_key' will actually add the attribute",
+                                      E_USER_NOTICE);
+                    }
+
+                    if ($_val === true || $_val === $_key) {
+                        $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"';
+                    }
+
+                    break;
+                }
+            // omit break; to fall through!
+
+            default:
+                if (!is_array($_val)) {
+                    $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"';
+                } else {
+                    trigger_error("html_radios: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                }
+                break;
+        }
+    }
+
+    if (!isset($options) && !isset($values)) {
+        /* raise error here? */
+
+        return '';
+    }
+
+    $_html_result = array();
+
+    if (isset($options)) {
+        foreach ($options as $_key => $_val) {
+            $_html_result[] =
+                smarty_function_html_radios_output($name, $_key, $_val, $selected, $extra, $separator, $labels,
+                                                   $label_ids, $escape);
+        }
+    } else {
+        foreach ($values as $_i => $_key) {
+            $_val = isset($output[ $_i ]) ? $output[ $_i ] : '';
+            $_html_result[] =
+                smarty_function_html_radios_output($name, $_key, $_val, $selected, $extra, $separator, $labels,
+                                                   $label_ids, $escape);
+        }
+    }
+
+    if (!empty($params[ 'assign' ])) {
+        $template->assign($params[ 'assign' ], $_html_result);
+    } else {
+        return implode("\n", $_html_result);
+    }
+}
+
+function smarty_function_html_radios_output($name, $value, $output, $selected, $extra, $separator, $labels, $label_ids,
+                                            $escape)
+{
+    $_output = '';
+
+    if (is_object($value)) {
+        if (method_exists($value, "__toString")) {
+            $value = (string) $value->__toString();
+        } else {
+            trigger_error("html_options: value is an object of class '" . get_class($value) .
+                          "' without __toString() method", E_USER_NOTICE);
+
+            return '';
+        }
+    } else {
+        $value = (string) $value;
+    }
+
+    if (is_object($output)) {
+        if (method_exists($output, "__toString")) {
+            $output = (string) $output->__toString();
+        } else {
+            trigger_error("html_options: output is an object of class '" . get_class($output) .
+                          "' without __toString() method", E_USER_NOTICE);
+
+            return '';
+        }
+    } else {
+        $output = (string) $output;
+    }
+
+    if ($labels) {
+        if ($label_ids) {
+            $_id = smarty_function_escape_special_chars(preg_replace('![^\w\-\.]!' . Smarty::$_UTF8_MODIFIER, '_',
+                                                                     $name . '_' . $value));
+            $_output .= '<label for="' . $_id . '">';
+        } else {
+            $_output .= '<label>';
+        }
+    }
+
+    $name = smarty_function_escape_special_chars($name);
+    $value = smarty_function_escape_special_chars($value);
+    if ($escape) {
+        $output = smarty_function_escape_special_chars($output);
+    }
+
+    $_output .= '<input type="radio" name="' . $name . '" value="' . $value . '"';
+
+    if ($labels && $label_ids) {
+        $_output .= ' id="' . $_id . '"';
+    }
+
+    if ($value === $selected) {
+        $_output .= ' checked="checked"';
+    }
+
+    $_output .= $extra . ' />' . $output;
+    if ($labels) {
+        $_output .= '</label>';
+    }
+
+    $_output .= $separator;
+
+    return $_output;
+}

+ 397 - 0
Mall/Framework/Smarty/libs/plugins/function.html_select_date.php

@@ -0,0 +1,397 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {html_select_date} plugin
+ * Type:     function<br>
+ * Name:     html_select_date<br>
+ * Purpose:  Prints the dropdowns for date selection.
+ * ChangeLog:
+ * <pre>
+ *            - 1.0 initial release
+ *            - 1.1 added support for +/- N syntax for begin
+ *              and end year values. (Monte)
+ *            - 1.2 added support for yyyy-mm-dd syntax for
+ *              time value. (Jan Rosier)
+ *            - 1.3 added support for choosing format for
+ *              month values (Gary Loescher)
+ *            - 1.3.1 added support for choosing format for
+ *              day values (Marcus Bointon)
+ *            - 1.3.2 support negative timestamps, force year
+ *              dropdown to include given date unless explicitly set (Monte)
+ *            - 1.3.4 fix behaviour of 0000-00-00 00:00:00 dates to match that
+ *              of 0000-00-00 dates (cybot, boots)
+ *            - 2.0 complete rewrite for performance,
+ *              added attributes month_names, *_id
+ * </pre>
+ *
+ * @link     http://www.smarty.net/manual/en/language.function.html.select.date.php {html_select_date}
+ *           (Smarty online manual)
+ * @version  2.0
+ * @author   Andrei Zmievski
+ * @author   Monte Ohrt <monte at ohrt dot com>
+ * @author   Rodney Rehm
+ *
+ * @param array                     $params parameters
+ *
+ * @param \Smarty_Internal_Template $template
+ *
+ * @return string
+ */
+function smarty_function_html_select_date($params, Smarty_Internal_Template $template)
+{
+    if (!isset($template->smarty->_cache[ '_required_sesc' ])) {
+        require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php');
+        $template->smarty->_cache[ '_required_sesc' ] = true;
+    }
+    if (!isset($template->smarty->_cache[ '_required_smt' ])) {
+        require_once(SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php');
+        $template->smarty->_cache[ '_required_smt' ] = true;
+    }
+    // generate timestamps used for month names only
+    static $_month_timestamps = null;
+    static $_current_year = null;
+    if ($_month_timestamps === null) {
+        $_current_year = date('Y');
+        $_month_timestamps = array();
+        for ($i = 1; $i <= 12; $i ++) {
+            $_month_timestamps[ $i ] = mktime(0, 0, 0, $i, 1, 2000);
+        }
+    }
+
+    /* Default values. */
+    $prefix = "Date_";
+    $start_year = null;
+    $end_year = null;
+    $display_days = true;
+    $display_months = true;
+    $display_years = true;
+    $month_format = "%B";
+    /* Write months as numbers by default  GL */
+    $month_value_format = "%m";
+    $day_format = "%02d";
+    /* Write day values using this format MB */
+    $day_value_format = "%d";
+    $year_as_text = false;
+    /* Display years in reverse order? Ie. 2000,1999,.... */
+    $reverse_years = false;
+    /* Should the select boxes be part of an array when returned from PHP?
+       e.g. setting it to "birthday", would create "birthday[Day]",
+       "birthday[Month]" & "birthday[Year]". Can be combined with prefix */
+    $field_array = null;
+    /* <select size>'s of the different <select> tags.
+       If not set, uses default dropdown. */
+    $day_size = null;
+    $month_size = null;
+    $year_size = null;
+    /* Unparsed attributes common to *ALL* the <select>/<input> tags.
+       An example might be in the template: all_extra ='class ="foo"'. */
+    $all_extra = null;
+    /* Separate attributes for the tags. */
+    $day_extra = null;
+    $month_extra = null;
+    $year_extra = null;
+    /* Order in which to display the fields.
+       "D" -> day, "M" -> month, "Y" -> year. */
+    $field_order = 'MDY';
+    /* String printed between the different fields. */
+    $field_separator = "\n";
+    $option_separator = "\n";
+    $time = null;
+    // $all_empty = null;
+    // $day_empty = null;
+    // $month_empty = null;
+    // $year_empty = null;
+    $extra_attrs = '';
+    $all_id = null;
+    $day_id = null;
+    $month_id = null;
+    $year_id = null;
+
+    foreach ($params as $_key => $_value) {
+        switch ($_key) {
+            case 'time':
+                if (!is_array($_value) && $_value !== null) {
+                    $time = smarty_make_timestamp($_value);
+                }
+                break;
+
+            case 'month_names':
+                if (is_array($_value) && count($_value) == 12) {
+                    $$_key = $_value;
+                } else {
+                    trigger_error("html_select_date: month_names must be an array of 12 strings", E_USER_NOTICE);
+                }
+                break;
+
+            case 'prefix':
+            case 'field_array':
+            case 'start_year':
+            case 'end_year':
+            case 'day_format':
+            case 'day_value_format':
+            case 'month_format':
+            case 'month_value_format':
+            case 'day_size':
+            case 'month_size':
+            case 'year_size':
+            case 'all_extra':
+            case 'day_extra':
+            case 'month_extra':
+            case 'year_extra':
+            case 'field_order':
+            case 'field_separator':
+            case 'option_separator':
+            case 'all_empty':
+            case 'month_empty':
+            case 'day_empty':
+            case 'year_empty':
+            case 'all_id':
+            case 'month_id':
+            case 'day_id':
+            case 'year_id':
+                $$_key = (string) $_value;
+                break;
+
+            case 'display_days':
+            case 'display_months':
+            case 'display_years':
+            case 'year_as_text':
+            case 'reverse_years':
+                $$_key = (bool) $_value;
+                break;
+
+            default:
+                if (!is_array($_value)) {
+                    $extra_attrs .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_value) . '"';
+                } else {
+                    trigger_error("html_select_date: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                }
+                break;
+        }
+    }
+
+    // Note: date() is faster than strftime()
+    // Note: explode(date()) is faster than date() date() date()
+    if (isset($params[ 'time' ]) && is_array($params[ 'time' ])) {
+        if (isset($params[ 'time' ][ $prefix . 'Year' ])) {
+            // $_REQUEST[$field_array] given
+            foreach (array('Y' => 'Year',
+                           'm' => 'Month',
+                           'd' => 'Day') as $_elementKey => $_elementName) {
+                $_variableName = '_' . strtolower($_elementName);
+                $$_variableName =
+                    isset($params[ 'time' ][ $prefix . $_elementName ]) ? $params[ 'time' ][ $prefix . $_elementName ] :
+                        date($_elementKey);
+            }
+        } elseif (isset($params[ 'time' ][ $field_array ][ $prefix . 'Year' ])) {
+            // $_REQUEST given
+            foreach (array('Y' => 'Year',
+                           'm' => 'Month',
+                           'd' => 'Day') as $_elementKey => $_elementName) {
+                $_variableName = '_' . strtolower($_elementName);
+                $$_variableName = isset($params[ 'time' ][ $field_array ][ $prefix . $_elementName ]) ?
+                    $params[ 'time' ][ $field_array ][ $prefix . $_elementName ] : date($_elementKey);
+            }
+        } else {
+            // no date found, use NOW
+            list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d'));
+        }
+    } elseif ($time === null) {
+        if (array_key_exists('time', $params)) {
+            $_year = $_month = $_day = $time = null;
+        } else {
+            list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d'));
+        }
+    } else {
+        list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d', $time));
+    }
+
+    // make syntax "+N" or "-N" work with $start_year and $end_year
+    // Note preg_match('!^(\+|\-)\s*(\d+)$!', $end_year, $match) is slower than trim+substr
+    foreach (array('start',
+                   'end') as $key) {
+        $key .= '_year';
+        $t = $$key;
+        if ($t === null) {
+            $$key = (int) $_current_year;
+        } elseif ($t[ 0 ] == '+') {
+            $$key = (int) ($_current_year + (int) trim(substr($t, 1)));
+        } elseif ($t[ 0 ] == '-') {
+            $$key = (int) ($_current_year - (int) trim(substr($t, 1)));
+        } else {
+            $$key = (int) $$key;
+        }
+    }
+
+    // flip for ascending or descending
+    if (($start_year > $end_year && !$reverse_years) || ($start_year < $end_year && $reverse_years)) {
+        $t = $end_year;
+        $end_year = $start_year;
+        $start_year = $t;
+    }
+
+    // generate year <select> or <input>
+    if ($display_years) {
+        $_extra = '';
+        $_name = $field_array ? ($field_array . '[' . $prefix . 'Year]') : ($prefix . 'Year');
+        if ($all_extra) {
+            $_extra .= ' ' . $all_extra;
+        }
+        if ($year_extra) {
+            $_extra .= ' ' . $year_extra;
+        }
+
+        if ($year_as_text) {
+            $_html_years =
+                '<input type="text" name="' . $_name . '" value="' . $_year . '" size="4" maxlength="4"' . $_extra .
+                $extra_attrs . ' />';
+        } else {
+            $_html_years = '<select name="' . $_name . '"';
+            if ($year_id !== null || $all_id !== null) {
+                $_html_years .= ' id="' . smarty_function_escape_special_chars($year_id !== null ?
+                                                                                   ($year_id ? $year_id : $_name) :
+                                                                                   ($all_id ? ($all_id . $_name) :
+                                                                                       $_name)) . '"';
+            }
+            if ($year_size) {
+                $_html_years .= ' size="' . $year_size . '"';
+            }
+            $_html_years .= $_extra . $extra_attrs . '>' . $option_separator;
+
+            if (isset($year_empty) || isset($all_empty)) {
+                $_html_years .= '<option value="">' . (isset($year_empty) ? $year_empty : $all_empty) . '</option>' .
+                                $option_separator;
+            }
+
+            $op = $start_year > $end_year ? - 1 : 1;
+            for ($i = $start_year; $op > 0 ? $i <= $end_year : $i >= $end_year; $i += $op) {
+                $_html_years .= '<option value="' . $i . '"' . ($_year == $i ? ' selected="selected"' : '') . '>' . $i .
+                                '</option>' . $option_separator;
+            }
+
+            $_html_years .= '</select>';
+        }
+    }
+
+    // generate month <select> or <input>
+    if ($display_months) {
+        $_extra = '';
+        $_name = $field_array ? ($field_array . '[' . $prefix . 'Month]') : ($prefix . 'Month');
+        if ($all_extra) {
+            $_extra .= ' ' . $all_extra;
+        }
+        if ($month_extra) {
+            $_extra .= ' ' . $month_extra;
+        }
+
+        $_html_months = '<select name="' . $_name . '"';
+        if ($month_id !== null || $all_id !== null) {
+            $_html_months .= ' id="' . smarty_function_escape_special_chars($month_id !== null ?
+                                                                                ($month_id ? $month_id : $_name) :
+                                                                                ($all_id ? ($all_id . $_name) :
+                                                                                    $_name)) . '"';
+        }
+        if ($month_size) {
+            $_html_months .= ' size="' . $month_size . '"';
+        }
+        $_html_months .= $_extra . $extra_attrs . '>' . $option_separator;
+
+        if (isset($month_empty) || isset($all_empty)) {
+            $_html_months .= '<option value="">' . (isset($month_empty) ? $month_empty : $all_empty) . '</option>' .
+                             $option_separator;
+        }
+
+        for ($i = 1; $i <= 12; $i ++) {
+            $_val = sprintf('%02d', $i);
+            $_text = isset($month_names) ? smarty_function_escape_special_chars($month_names[ $i ]) :
+                ($month_format == "%m" ? $_val : strftime($month_format, $_month_timestamps[ $i ]));
+            $_value = $month_value_format == "%m" ? $_val : strftime($month_value_format, $_month_timestamps[ $i ]);
+            $_html_months .= '<option value="' . $_value . '"' . ($_val == $_month ? ' selected="selected"' : '') .
+                             '>' . $_text . '</option>' . $option_separator;
+        }
+
+        $_html_months .= '</select>';
+    }
+
+    // generate day <select> or <input>
+    if ($display_days) {
+        $_extra = '';
+        $_name = $field_array ? ($field_array . '[' . $prefix . 'Day]') : ($prefix . 'Day');
+        if ($all_extra) {
+            $_extra .= ' ' . $all_extra;
+        }
+        if ($day_extra) {
+            $_extra .= ' ' . $day_extra;
+        }
+
+        $_html_days = '<select name="' . $_name . '"';
+        if ($day_id !== null || $all_id !== null) {
+            $_html_days .= ' id="' .
+                           smarty_function_escape_special_chars($day_id !== null ? ($day_id ? $day_id : $_name) :
+                                                                    ($all_id ? ($all_id . $_name) : $_name)) . '"';
+        }
+        if ($day_size) {
+            $_html_days .= ' size="' . $day_size . '"';
+        }
+        $_html_days .= $_extra . $extra_attrs . '>' . $option_separator;
+
+        if (isset($day_empty) || isset($all_empty)) {
+            $_html_days .= '<option value="">' . (isset($day_empty) ? $day_empty : $all_empty) . '</option>' .
+                           $option_separator;
+        }
+
+        for ($i = 1; $i <= 31; $i ++) {
+            $_val = sprintf('%02d', $i);
+            $_text = $day_format == '%02d' ? $_val : sprintf($day_format, $i);
+            $_value = $day_value_format == '%02d' ? $_val : sprintf($day_value_format, $i);
+            $_html_days .= '<option value="' . $_value . '"' . ($_val == $_day ? ' selected="selected"' : '') . '>' .
+                           $_text . '</option>' . $option_separator;
+        }
+
+        $_html_days .= '</select>';
+    }
+
+    // order the fields for output
+    $_html = '';
+    for ($i = 0; $i <= 2; $i ++) {
+        switch ($field_order[ $i ]) {
+            case 'Y':
+            case 'y':
+                if (isset($_html_years)) {
+                    if ($_html) {
+                        $_html .= $field_separator;
+                    }
+                    $_html .= $_html_years;
+                }
+                break;
+
+            case 'm':
+            case 'M':
+                if (isset($_html_months)) {
+                    if ($_html) {
+                        $_html .= $field_separator;
+                    }
+                    $_html .= $_html_months;
+                }
+                break;
+
+            case 'd':
+            case 'D':
+                if (isset($_html_days)) {
+                    if ($_html) {
+                        $_html .= $field_separator;
+                    }
+                    $_html .= $_html_days;
+                }
+                break;
+        }
+    }
+
+    return $_html;
+}

+ 374 - 0
Mall/Framework/Smarty/libs/plugins/function.html_select_time.php

@@ -0,0 +1,374 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {html_select_time} function plugin
+ * Type:     function<br>
+ * Name:     html_select_time<br>
+ * Purpose:  Prints the dropdowns for time selection
+ *
+ * @link     http://www.smarty.net/manual/en/language.function.html.select.time.php {html_select_time}
+ *           (Smarty online manual)
+ * @author   Roberto Berto <roberto@berto.net>
+ * @author   Monte Ohrt <monte AT ohrt DOT com>
+ *
+ * @param array                     $params parameters
+ *
+ * @param \Smarty_Internal_Template $template
+ *
+ * @return string
+ * @uses     smarty_make_timestamp()
+ */
+function smarty_function_html_select_time($params, Smarty_Internal_Template $template)
+{
+    if (!isset($template->smarty->_cache[ '_required_sesc' ])) {
+        require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php');
+        $template->smarty->_cache[ '_required_sesc' ] = true;
+    }
+    if (!isset($template->smarty->_cache[ '_required_smt' ])) {
+        require_once(SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php');
+        $template->smarty->_cache[ '_required_smt' ] = true;
+    }
+    $prefix = "Time_";
+    $field_array = null;
+    $field_separator = "\n";
+    $option_separator = "\n";
+    $time = null;
+
+    $display_hours = true;
+    $display_minutes = true;
+    $display_seconds = true;
+    $display_meridian = true;
+
+    $hour_format = '%02d';
+    $hour_value_format = '%02d';
+    $minute_format = '%02d';
+    $minute_value_format = '%02d';
+    $second_format = '%02d';
+    $second_value_format = '%02d';
+
+    $hour_size = null;
+    $minute_size = null;
+    $second_size = null;
+    $meridian_size = null;
+
+    $all_empty = null;
+    $hour_empty = null;
+    $minute_empty = null;
+    $second_empty = null;
+    $meridian_empty = null;
+
+    $all_id = null;
+    $hour_id = null;
+    $minute_id = null;
+    $second_id = null;
+    $meridian_id = null;
+
+    $use_24_hours = true;
+    $minute_interval = 1;
+    $second_interval = 1;
+
+    $extra_attrs = '';
+    $all_extra = null;
+    $hour_extra = null;
+    $minute_extra = null;
+    $second_extra = null;
+    $meridian_extra = null;
+
+    foreach ($params as $_key => $_value) {
+        switch ($_key) {
+            case 'time':
+                if (!is_array($_value) && $_value !== null) {
+                    $time = smarty_make_timestamp($_value);
+                }
+                break;
+
+            case 'prefix':
+            case 'field_array':
+
+            case 'field_separator':
+            case 'option_separator':
+
+            case 'all_extra':
+            case 'hour_extra':
+            case 'minute_extra':
+            case 'second_extra':
+            case 'meridian_extra':
+
+            case 'all_empty':
+            case 'hour_empty':
+            case 'minute_empty':
+            case 'second_empty':
+            case 'meridian_empty':
+
+            case 'all_id':
+            case 'hour_id':
+            case 'minute_id':
+            case 'second_id':
+            case 'meridian_id':
+
+            case 'hour_format':
+            case 'hour_value_format':
+            case 'minute_format':
+            case 'minute_value_format':
+            case 'second_format':
+            case 'second_value_format':
+                $$_key = (string) $_value;
+                break;
+
+            case 'display_hours':
+            case 'display_minutes':
+            case 'display_seconds':
+            case 'display_meridian':
+            case 'use_24_hours':
+                $$_key = (bool) $_value;
+                break;
+
+            case 'minute_interval':
+            case 'second_interval':
+
+            case 'hour_size':
+            case 'minute_size':
+            case 'second_size':
+            case 'meridian_size':
+                $$_key = (int) $_value;
+                break;
+
+            default:
+                if (!is_array($_value)) {
+                    $extra_attrs .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_value) . '"';
+                } else {
+                    trigger_error("html_select_date: extra attribute '$_key' cannot be an array", E_USER_NOTICE);
+                }
+                break;
+        }
+    }
+
+    if (isset($params[ 'time' ]) && is_array($params[ 'time' ])) {
+        if (isset($params[ 'time' ][ $prefix . 'Hour' ])) {
+            // $_REQUEST[$field_array] given
+            foreach (array('H' => 'Hour',
+                           'i' => 'Minute',
+                           's' => 'Second') as $_elementKey => $_elementName) {
+                $_variableName = '_' . strtolower($_elementName);
+                $$_variableName =
+                    isset($params[ 'time' ][ $prefix . $_elementName ]) ? $params[ 'time' ][ $prefix . $_elementName ] :
+                        date($_elementKey);
+            }
+            $_meridian =
+                isset($params[ 'time' ][ $prefix . 'Meridian' ]) ? (' ' . $params[ 'time' ][ $prefix . 'Meridian' ]) :
+                    '';
+            $time = strtotime($_hour . ':' . $_minute . ':' . $_second . $_meridian);
+            list($_hour, $_minute, $_second) = $time = explode('-', date('H-i-s', $time));
+        } elseif (isset($params[ 'time' ][ $field_array ][ $prefix . 'Hour' ])) {
+            // $_REQUEST given
+            foreach (array('H' => 'Hour',
+                           'i' => 'Minute',
+                           's' => 'Second') as $_elementKey => $_elementName) {
+                $_variableName = '_' . strtolower($_elementName);
+                $$_variableName = isset($params[ 'time' ][ $field_array ][ $prefix . $_elementName ]) ?
+                    $params[ 'time' ][ $field_array ][ $prefix . $_elementName ] : date($_elementKey);
+            }
+            $_meridian = isset($params[ 'time' ][ $field_array ][ $prefix . 'Meridian' ]) ?
+                (' ' . $params[ 'time' ][ $field_array ][ $prefix . 'Meridian' ]) : '';
+            $time = strtotime($_hour . ':' . $_minute . ':' . $_second . $_meridian);
+            list($_hour, $_minute, $_second) = $time = explode('-', date('H-i-s', $time));
+        } else {
+            // no date found, use NOW
+            list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d'));
+        }
+    } elseif ($time === null) {
+        if (array_key_exists('time', $params)) {
+            $_hour = $_minute = $_second = $time = null;
+        } else {
+            list($_hour, $_minute, $_second) = $time = explode('-', date('H-i-s'));
+        }
+    } else {
+        list($_hour, $_minute, $_second) = $time = explode('-', date('H-i-s', $time));
+    }
+
+    // generate hour <select>
+    if ($display_hours) {
+        $_html_hours = '';
+        $_extra = '';
+        $_name = $field_array ? ($field_array . '[' . $prefix . 'Hour]') : ($prefix . 'Hour');
+        if ($all_extra) {
+            $_extra .= ' ' . $all_extra;
+        }
+        if ($hour_extra) {
+            $_extra .= ' ' . $hour_extra;
+        }
+
+        $_html_hours = '<select name="' . $_name . '"';
+        if ($hour_id !== null || $all_id !== null) {
+            $_html_hours .= ' id="' .
+                            smarty_function_escape_special_chars($hour_id !== null ? ($hour_id ? $hour_id : $_name) :
+                                                                     ($all_id ? ($all_id . $_name) : $_name)) . '"';
+        }
+        if ($hour_size) {
+            $_html_hours .= ' size="' . $hour_size . '"';
+        }
+        $_html_hours .= $_extra . $extra_attrs . '>' . $option_separator;
+
+        if (isset($hour_empty) || isset($all_empty)) {
+            $_html_hours .= '<option value="">' . (isset($hour_empty) ? $hour_empty : $all_empty) . '</option>' .
+                            $option_separator;
+        }
+
+        $start = $use_24_hours ? 0 : 1;
+        $end = $use_24_hours ? 23 : 12;
+        for ($i = $start; $i <= $end; $i ++) {
+            $_val = sprintf('%02d', $i);
+            $_text = $hour_format == '%02d' ? $_val : sprintf($hour_format, $i);
+            $_value = $hour_value_format == '%02d' ? $_val : sprintf($hour_value_format, $i);
+
+            if (!$use_24_hours) {
+                $_hour12 = $_hour == 0 ? 12 : ($_hour <= 12 ? $_hour : $_hour - 12);
+            }
+
+            $selected = $_hour !== null ? ($use_24_hours ? $_hour == $_val : $_hour12 == $_val) : null;
+            $_html_hours .= '<option value="' . $_value . '"' . ($selected ? ' selected="selected"' : '') . '>' .
+                            $_text . '</option>' . $option_separator;
+        }
+
+        $_html_hours .= '</select>';
+    }
+
+    // generate minute <select>
+    if ($display_minutes) {
+        $_html_minutes = '';
+        $_extra = '';
+        $_name = $field_array ? ($field_array . '[' . $prefix . 'Minute]') : ($prefix . 'Minute');
+        if ($all_extra) {
+            $_extra .= ' ' . $all_extra;
+        }
+        if ($minute_extra) {
+            $_extra .= ' ' . $minute_extra;
+        }
+
+        $_html_minutes = '<select name="' . $_name . '"';
+        if ($minute_id !== null || $all_id !== null) {
+            $_html_minutes .= ' id="' . smarty_function_escape_special_chars($minute_id !== null ?
+                                                                                 ($minute_id ? $minute_id : $_name) :
+                                                                                 ($all_id ? ($all_id . $_name) :
+                                                                                     $_name)) . '"';
+        }
+        if ($minute_size) {
+            $_html_minutes .= ' size="' . $minute_size . '"';
+        }
+        $_html_minutes .= $_extra . $extra_attrs . '>' . $option_separator;
+
+        if (isset($minute_empty) || isset($all_empty)) {
+            $_html_minutes .= '<option value="">' . (isset($minute_empty) ? $minute_empty : $all_empty) . '</option>' .
+                              $option_separator;
+        }
+
+        $selected = $_minute !== null ? ($_minute - $_minute % $minute_interval) : null;
+        for ($i = 0; $i <= 59; $i += $minute_interval) {
+            $_val = sprintf('%02d', $i);
+            $_text = $minute_format == '%02d' ? $_val : sprintf($minute_format, $i);
+            $_value = $minute_value_format == '%02d' ? $_val : sprintf($minute_value_format, $i);
+            $_html_minutes .= '<option value="' . $_value . '"' . ($selected === $i ? ' selected="selected"' : '') .
+                              '>' . $_text . '</option>' . $option_separator;
+        }
+
+        $_html_minutes .= '</select>';
+    }
+
+    // generate second <select>
+    if ($display_seconds) {
+        $_html_seconds = '';
+        $_extra = '';
+        $_name = $field_array ? ($field_array . '[' . $prefix . 'Second]') : ($prefix . 'Second');
+        if ($all_extra) {
+            $_extra .= ' ' . $all_extra;
+        }
+        if ($second_extra) {
+            $_extra .= ' ' . $second_extra;
+        }
+
+        $_html_seconds = '<select name="' . $_name . '"';
+        if ($second_id !== null || $all_id !== null) {
+            $_html_seconds .= ' id="' . smarty_function_escape_special_chars($second_id !== null ?
+                                                                                 ($second_id ? $second_id : $_name) :
+                                                                                 ($all_id ? ($all_id . $_name) :
+                                                                                     $_name)) . '"';
+        }
+        if ($second_size) {
+            $_html_seconds .= ' size="' . $second_size . '"';
+        }
+        $_html_seconds .= $_extra . $extra_attrs . '>' . $option_separator;
+
+        if (isset($second_empty) || isset($all_empty)) {
+            $_html_seconds .= '<option value="">' . (isset($second_empty) ? $second_empty : $all_empty) . '</option>' .
+                              $option_separator;
+        }
+
+        $selected = $_second !== null ? ($_second - $_second % $second_interval) : null;
+        for ($i = 0; $i <= 59; $i += $second_interval) {
+            $_val = sprintf('%02d', $i);
+            $_text = $second_format == '%02d' ? $_val : sprintf($second_format, $i);
+            $_value = $second_value_format == '%02d' ? $_val : sprintf($second_value_format, $i);
+            $_html_seconds .= '<option value="' . $_value . '"' . ($selected === $i ? ' selected="selected"' : '') .
+                              '>' . $_text . '</option>' . $option_separator;
+        }
+
+        $_html_seconds .= '</select>';
+    }
+
+    // generate meridian <select>
+    if ($display_meridian && !$use_24_hours) {
+        $_html_meridian = '';
+        $_extra = '';
+        $_name = $field_array ? ($field_array . '[' . $prefix . 'Meridian]') : ($prefix . 'Meridian');
+        if ($all_extra) {
+            $_extra .= ' ' . $all_extra;
+        }
+        if ($meridian_extra) {
+            $_extra .= ' ' . $meridian_extra;
+        }
+
+        $_html_meridian = '<select name="' . $_name . '"';
+        if ($meridian_id !== null || $all_id !== null) {
+            $_html_meridian .= ' id="' . smarty_function_escape_special_chars($meridian_id !== null ?
+                                                                                  ($meridian_id ? $meridian_id :
+                                                                                      $_name) :
+                                                                                  ($all_id ? ($all_id . $_name) :
+                                                                                      $_name)) . '"';
+        }
+        if ($meridian_size) {
+            $_html_meridian .= ' size="' . $meridian_size . '"';
+        }
+        $_html_meridian .= $_extra . $extra_attrs . '>' . $option_separator;
+
+        if (isset($meridian_empty) || isset($all_empty)) {
+            $_html_meridian .= '<option value="">' . (isset($meridian_empty) ? $meridian_empty : $all_empty) .
+                               '</option>' . $option_separator;
+        }
+
+        $_html_meridian .= '<option value="am"' . ($_hour > 0 && $_hour < 12 ? ' selected="selected"' : '') .
+                           '>AM</option>' . $option_separator . '<option value="pm"' .
+                           ($_hour < 12 ? '' : ' selected="selected"') . '>PM</option>' . $option_separator .
+                           '</select>';
+    }
+
+    $_html = '';
+    foreach (array('_html_hours',
+                   '_html_minutes',
+                   '_html_seconds',
+                   '_html_meridian') as $k) {
+        if (isset($$k)) {
+            if ($_html) {
+                $_html .= $field_separator;
+            }
+            $_html .= $$k;
+        }
+    }
+
+    return $_html;
+}

+ 176 - 0
Mall/Framework/Smarty/libs/plugins/function.html_table.php

@@ -0,0 +1,176 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {html_table} function plugin
+ * Type:     function<br>
+ * Name:     html_table<br>
+ * Date:     Feb 17, 2003<br>
+ * Purpose:  make an html table from an array of data<br>
+ * Params:
+ * <pre>
+ * - loop       - array to loop through
+ * - cols       - number of columns, comma separated list of column names
+ *                or array of column names
+ * - rows       - number of rows
+ * - table_attr - table attributes
+ * - th_attr    - table heading attributes (arrays are cycled)
+ * - tr_attr    - table row attributes (arrays are cycled)
+ * - td_attr    - table cell attributes (arrays are cycled)
+ * - trailpad   - value to pad trailing cells with
+ * - caption    - text for caption element
+ * - vdir       - vertical direction (default: "down", means top-to-bottom)
+ * - hdir       - horizontal direction (default: "right", means left-to-right)
+ * - inner      - inner loop (default "cols": print $loop line by line,
+ *                $loop will be printed column by column otherwise)
+ * </pre>
+ * Examples:
+ * <pre>
+ * {table loop=$data}
+ * {table loop=$data cols=4 tr_attr='"bgcolor=red"'}
+ * {table loop=$data cols="first,second,third" tr_attr=$colors}
+ * </pre>
+ *
+ * @author   Monte Ohrt <monte at ohrt dot com>
+ * @author   credit to Messju Mohr <messju at lammfellpuschen dot de>
+ * @author   credit to boots <boots dot smarty at yahoo dot com>
+ * @version  1.1
+ * @link     http://www.smarty.net/manual/en/language.function.html.table.php {html_table}
+ *           (Smarty online manual)
+ *
+ * @param array $params parameters
+ *
+ * @return string
+ */
+function smarty_function_html_table($params)
+{
+    $table_attr = 'border="1"';
+    $tr_attr = '';
+    $th_attr = '';
+    $td_attr = '';
+    $cols = $cols_count = 3;
+    $rows = 3;
+    $trailpad = '&nbsp;';
+    $vdir = 'down';
+    $hdir = 'right';
+    $inner = 'cols';
+    $caption = '';
+    $loop = null;
+
+    if (!isset($params[ 'loop' ])) {
+        trigger_error("html_table: missing 'loop' parameter", E_USER_WARNING);
+
+        return;
+    }
+
+    foreach ($params as $_key => $_value) {
+        switch ($_key) {
+            case 'loop':
+                $$_key = (array) $_value;
+                break;
+
+            case 'cols':
+                if (is_array($_value) && !empty($_value)) {
+                    $cols = $_value;
+                    $cols_count = count($_value);
+                } elseif (!is_numeric($_value) && is_string($_value) && !empty($_value)) {
+                    $cols = explode(',', $_value);
+                    $cols_count = count($cols);
+                } elseif (!empty($_value)) {
+                    $cols_count = (int) $_value;
+                } else {
+                    $cols_count = $cols;
+                }
+                break;
+
+            case 'rows':
+                $$_key = (int) $_value;
+                break;
+
+            case 'table_attr':
+            case 'trailpad':
+            case 'hdir':
+            case 'vdir':
+            case 'inner':
+            case 'caption':
+                $$_key = (string) $_value;
+                break;
+
+            case 'tr_attr':
+            case 'td_attr':
+            case 'th_attr':
+                $$_key = $_value;
+                break;
+        }
+    }
+
+    $loop_count = count($loop);
+    if (empty($params[ 'rows' ])) {
+        /* no rows specified */
+        $rows = ceil($loop_count / $cols_count);
+    } elseif (empty($params[ 'cols' ])) {
+        if (!empty($params[ 'rows' ])) {
+            /* no cols specified, but rows */
+            $cols_count = ceil($loop_count / $rows);
+        }
+    }
+
+    $output = "<table $table_attr>\n";
+
+    if (!empty($caption)) {
+        $output .= '<caption>' . $caption . "</caption>\n";
+    }
+
+    if (is_array($cols)) {
+        $cols = ($hdir == 'right') ? $cols : array_reverse($cols);
+        $output .= "<thead><tr>\n";
+
+        for ($r = 0; $r < $cols_count; $r ++) {
+            $output .= '<th' . smarty_function_html_table_cycle('th', $th_attr, $r) . '>';
+            $output .= $cols[ $r ];
+            $output .= "</th>\n";
+        }
+        $output .= "</tr></thead>\n";
+    }
+
+    $output .= "<tbody>\n";
+    for ($r = 0; $r < $rows; $r ++) {
+        $output .= "<tr" . smarty_function_html_table_cycle('tr', $tr_attr, $r) . ">\n";
+        $rx = ($vdir == 'down') ? $r * $cols_count : ($rows - 1 - $r) * $cols_count;
+
+        for ($c = 0; $c < $cols_count; $c ++) {
+            $x = ($hdir == 'right') ? $rx + $c : $rx + $cols_count - 1 - $c;
+            if ($inner != 'cols') {
+                /* shuffle x to loop over rows*/
+                $x = floor($x / $cols_count) + ($x % $cols_count) * $rows;
+            }
+
+            if ($x < $loop_count) {
+                $output .= "<td" . smarty_function_html_table_cycle('td', $td_attr, $c) . ">" . $loop[ $x ] . "</td>\n";
+            } else {
+                $output .= "<td" . smarty_function_html_table_cycle('td', $td_attr, $c) . ">$trailpad</td>\n";
+            }
+        }
+        $output .= "</tr>\n";
+    }
+    $output .= "</tbody>\n";
+    $output .= "</table>\n";
+
+    return $output;
+}
+
+function smarty_function_html_table_cycle($name, $var, $no)
+{
+    if (!is_array($var)) {
+        $ret = $var;
+    } else {
+        $ret = $var[ $no % count($var) ];
+    }
+
+    return ($ret) ? ' ' . $ret : '';
+}

+ 153 - 0
Mall/Framework/Smarty/libs/plugins/function.mailto.php

@@ -0,0 +1,153 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {mailto} function plugin
+ * Type:     function<br>
+ * Name:     mailto<br>
+ * Date:     May 21, 2002
+ * Purpose:  automate mailto address link creation, and optionally encode them.<br>
+ * Params:
+ * <pre>
+ * - address    - (required) - e-mail address
+ * - text       - (optional) - text to display, default is address
+ * - encode     - (optional) - can be one of:
+ *                             * none : no encoding (default)
+ *                             * javascript : encode with javascript
+ *                             * javascript_charcode : encode with javascript charcode
+ *                             * hex : encode with hexadecimal (no javascript)
+ * - cc         - (optional) - address(es) to carbon copy
+ * - bcc        - (optional) - address(es) to blind carbon copy
+ * - subject    - (optional) - e-mail subject
+ * - newsgroups - (optional) - newsgroup(s) to post to
+ * - followupto - (optional) - address(es) to follow up to
+ * - extra      - (optional) - extra tags for the href link
+ * </pre>
+ * Examples:
+ * <pre>
+ * {mailto address="me@domain.com"}
+ * {mailto address="me@domain.com" encode="javascript"}
+ * {mailto address="me@domain.com" encode="hex"}
+ * {mailto address="me@domain.com" subject="Hello to you!"}
+ * {mailto address="me@domain.com" cc="you@domain.com,they@domain.com"}
+ * {mailto address="me@domain.com" extra='class="mailto"'}
+ * </pre>
+ *
+ * @link     http://www.smarty.net/manual/en/language.function.mailto.php {mailto}
+ *           (Smarty online manual)
+ * @version  1.2
+ * @author   Monte Ohrt <monte at ohrt dot com>
+ * @author   credits to Jason Sweat (added cc, bcc and subject functionality)
+ *
+ * @param array $params parameters
+ *
+ * @return string
+ */
+function smarty_function_mailto($params)
+{
+    static $_allowed_encoding =
+        array('javascript' => true, 'javascript_charcode' => true, 'hex' => true, 'none' => true);
+    $extra = '';
+
+    if (empty($params[ 'address' ])) {
+        trigger_error("mailto: missing 'address' parameter", E_USER_WARNING);
+
+        return;
+    } else {
+        $address = $params[ 'address' ];
+    }
+
+    $text = $address;
+    // netscape and mozilla do not decode %40 (@) in BCC field (bug?)
+    // so, don't encode it.
+    $search = array('%40', '%2C');
+    $replace = array('@', ',');
+    $mail_parms = array();
+    foreach ($params as $var => $value) {
+        switch ($var) {
+            case 'cc':
+            case 'bcc':
+            case 'followupto':
+                if (!empty($value)) {
+                    $mail_parms[] = $var . '=' . str_replace($search, $replace, rawurlencode($value));
+                }
+                break;
+
+            case 'subject':
+            case 'newsgroups':
+                $mail_parms[] = $var . '=' . rawurlencode($value);
+                break;
+
+            case 'extra':
+            case 'text':
+                $$var = $value;
+
+            default:
+        }
+    }
+
+    if ($mail_parms) {
+        $address .= '?' . join('&', $mail_parms);
+    }
+
+    $encode = (empty($params[ 'encode' ])) ? 'none' : $params[ 'encode' ];
+    if (!isset($_allowed_encoding[ $encode ])) {
+        trigger_error("mailto: 'encode' parameter must be none, javascript, javascript_charcode or hex",
+                      E_USER_WARNING);
+
+        return;
+    }
+    // FIXME: (rodneyrehm) document.write() excues me what? 1998 has passed!
+    if ($encode == 'javascript') {
+        $string = 'document.write(\'<a href="mailto:' . $address . '" ' . $extra . '>' . $text . '</a>\');';
+
+        $js_encode = '';
+        for ($x = 0, $_length = strlen($string); $x < $_length; $x ++) {
+            $js_encode .= '%' . bin2hex($string[ $x ]);
+        }
+
+        return '<script type="text/javascript">eval(unescape(\'' . $js_encode . '\'))</script>';
+    } elseif ($encode == 'javascript_charcode') {
+        $string = '<a href="mailto:' . $address . '" ' . $extra . '>' . $text . '</a>';
+
+        for ($x = 0, $y = strlen($string); $x < $y; $x ++) {
+            $ord[] = ord($string[ $x ]);
+        }
+
+        $_ret = "<script type=\"text/javascript\" language=\"javascript\">\n" . "{document.write(String.fromCharCode(" .
+                implode(',', $ord) . "))" . "}\n" . "</script>\n";
+
+        return $_ret;
+    } elseif ($encode == 'hex') {
+        preg_match('!^(.*)(\?.*)$!', $address, $match);
+        if (!empty($match[ 2 ])) {
+            trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript.", E_USER_WARNING);
+
+            return;
+        }
+        $address_encode = '';
+        for ($x = 0, $_length = strlen($address); $x < $_length; $x ++) {
+            if (preg_match('!\w!' . Smarty::$_UTF8_MODIFIER, $address[ $x ])) {
+                $address_encode .= '%' . bin2hex($address[ $x ]);
+            } else {
+                $address_encode .= $address[ $x ];
+            }
+        }
+        $text_encode = '';
+        for ($x = 0, $_length = strlen($text); $x < $_length; $x ++) {
+            $text_encode .= '&#x' . bin2hex($text[ $x ]) . ';';
+        }
+
+        $mailto = "&#109;&#97;&#105;&#108;&#116;&#111;&#58;";
+
+        return '<a href="' . $mailto . $address_encode . '" ' . $extra . '>' . $text_encode . '</a>';
+    } else {
+        // no encoding
+        return '<a href="mailto:' . $address . '" ' . $extra . '>' . $text . '</a>';
+    }
+}

+ 109 - 0
Mall/Framework/Smarty/libs/plugins/function.math.php

@@ -0,0 +1,109 @@
+<?php
+/**
+ * Smarty plugin
+ * This plugin is only for Smarty2 BC
+ *
+ * @package    Smarty
+ * @subpackage PluginsFunction
+ */
+
+/**
+ * Smarty {math} function plugin
+ * Type:     function<br>
+ * Name:     math<br>
+ * Purpose:  handle math computations in template
+ *
+ * @link     http://www.smarty.net/manual/en/language.function.math.php {math}
+ *           (Smarty online manual)
+ * @author   Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param array                    $params   parameters
+ * @param Smarty_Internal_Template $template template object
+ *
+ * @return string|null
+ */
+function smarty_function_math($params, $template)
+{
+    static $_allowed_funcs =
+        array('int' => true, 'abs' => true, 'ceil' => true, 'cos' => true, 'exp' => true, 'floor' => true,
+              'log' => true, 'log10' => true, 'max' => true, 'min' => true, 'pi' => true, 'pow' => true, 'rand' => true,
+              'round' => true, 'sin' => true, 'sqrt' => true, 'srand' => true, 'tan' => true);
+    // be sure equation parameter is present
+    if (empty($params[ 'equation' ])) {
+        trigger_error("math: missing equation parameter", E_USER_WARNING);
+
+        return;
+    }
+
+    $equation = $params[ 'equation' ];
+
+    // make sure parenthesis are balanced
+    if (substr_count($equation, "(") != substr_count($equation, ")")) {
+        trigger_error("math: unbalanced parenthesis", E_USER_WARNING);
+
+        return;
+    }
+
+    // disallow backticks
+    if (strpos($equation, '`') !== false) {
+        trigger_error("math: backtick character not allowed in equation", E_USER_WARNING);
+
+        return;
+    }
+
+    // also disallow dollar signs
+    if (strpos($equation, '$') !== false) {
+        trigger_error("math: dollar signs not allowed in equation", E_USER_WARNING);
+
+        return;
+    }
+
+    foreach ($params as $key => $val) {
+        if ($key != "equation" && $key != "format" && $key != "assign") {
+            // make sure value is not empty
+            if (strlen($val) == 0) {
+                trigger_error("math: parameter '{$key}' is empty", E_USER_WARNING);
+
+                return;
+            }
+            if (!is_numeric($val)) {
+                trigger_error("math: parameter '{$key}' is not numeric", E_USER_WARNING);
+
+                return;
+            }
+        }
+    }
+
+    // match all vars in equation, make sure all are passed
+    preg_match_all('!(?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)!', $equation, $match);
+
+    foreach ($match[ 1 ] as $curr_var) {
+        if ($curr_var && !isset($params[ $curr_var ]) && !isset($_allowed_funcs[ $curr_var ])) {
+            trigger_error("math: function call '{$curr_var}' not allowed, or missing parameter '{$curr_var}'", E_USER_WARNING);
+
+            return;
+        }
+    }
+
+    foreach ($params as $key => $val) {
+        if ($key != "equation" && $key != "format" && $key != "assign") {
+            $equation = preg_replace("/\b$key\b/", " \$params['$key'] ", $equation);
+        }
+    }
+    $smarty_math_result = null;
+    eval("\$smarty_math_result = " . $equation . ";");
+
+    if (empty($params[ 'format' ])) {
+        if (empty($params[ 'assign' ])) {
+            return $smarty_math_result;
+        } else {
+            $template->assign($params[ 'assign' ], $smarty_math_result);
+        }
+    } else {
+        if (empty($params[ 'assign' ])) {
+            printf($params[ 'format' ], $smarty_math_result);
+        } else {
+            $template->assign($params[ 'assign' ], sprintf($params[ 'format' ], $smarty_math_result));
+        }
+    }
+}

+ 101 - 0
Mall/Framework/Smarty/libs/plugins/modifier.capitalize.php

@@ -0,0 +1,101 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifier
+ */
+
+/**
+ * Smarty capitalize modifier plugin
+ * Type:     modifier<br>
+ * Name:     capitalize<br>
+ * Purpose:  capitalize words in the string
+ * {@internal {$string|capitalize:true:true} is the fastest option for MBString enabled systems }}
+ *
+ * @param string  $string    string to capitalize
+ * @param boolean $uc_digits also capitalize "x123" to "X123"
+ * @param boolean $lc_rest   capitalize first letters, lowercase all following letters "aAa" to "Aaa"
+ *
+ * @return string capitalized string
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author Rodney Rehm
+ */
+function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false)
+{
+    if (Smarty::$_MBSTRING) {
+        if ($lc_rest) {
+            // uppercase (including hyphenated words)
+            $upper_string = mb_convert_case($string, MB_CASE_TITLE, Smarty::$_CHARSET);
+        } else {
+            // uppercase word breaks
+            $upper_string = preg_replace_callback("!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER,
+                                                  'smarty_mod_cap_mbconvert_cb', $string);
+        }
+        // check uc_digits case
+        if (!$uc_digits) {
+            if (preg_match_all("!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER, $string, $matches,
+                               PREG_OFFSET_CAPTURE)) {
+                foreach ($matches[ 1 ] as $match) {
+                    $upper_string =
+                        substr_replace($upper_string, mb_strtolower($match[ 0 ], Smarty::$_CHARSET), $match[ 1 ],
+                                       strlen($match[ 0 ]));
+                }
+            }
+        }
+        $upper_string =
+            preg_replace_callback("!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_mbconvert2_cb',
+                                  $upper_string);
+        return $upper_string;
+    }
+
+    // lowercase first
+    if ($lc_rest) {
+        $string = strtolower($string);
+    }
+    // uppercase (including hyphenated words)
+    $upper_string =
+        preg_replace_callback("!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_ucfirst_cb',
+                              $string);
+    // check uc_digits case
+    if (!$uc_digits) {
+        if (preg_match_all("!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER, $string, $matches,
+                           PREG_OFFSET_CAPTURE)) {
+            foreach ($matches[ 1 ] as $match) {
+                $upper_string =
+                    substr_replace($upper_string, strtolower($match[ 0 ]), $match[ 1 ], strlen($match[ 0 ]));
+            }
+        }
+    }
+    $upper_string = preg_replace_callback("!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_ucfirst2_cb',
+                                          $upper_string);
+    return $upper_string;
+}
+
+/* 
+ *
+ * Bug: create_function() use exhausts memory when used in long loops
+ * Fix: use declared functions for callbacks instead of using create_function()
+ * Note: This can be fixed using anonymous functions instead, but that requires PHP >= 5.3
+ *
+ * @author Kyle Renfrow
+ */
+function smarty_mod_cap_mbconvert_cb($matches)
+{
+    return stripslashes($matches[ 1 ]) . mb_convert_case(stripslashes($matches[ 2 ]), MB_CASE_UPPER, Smarty::$_CHARSET);
+}
+
+function smarty_mod_cap_mbconvert2_cb($matches)
+{
+    return stripslashes($matches[ 1 ]) . mb_convert_case(stripslashes($matches[ 3 ]), MB_CASE_UPPER, Smarty::$_CHARSET);
+}
+
+function smarty_mod_cap_ucfirst_cb($matches)
+{
+    return stripslashes($matches[ 1 ]) . ucfirst(stripslashes($matches[ 2 ]));
+}
+
+function smarty_mod_cap_ucfirst2_cb($matches)
+{
+    return stripslashes($matches[ 1 ]) . ucfirst(stripslashes($matches[ 3 ]));
+}

+ 83 - 0
Mall/Framework/Smarty/libs/plugins/modifier.date_format.php

@@ -0,0 +1,83 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifier
+ */
+
+/**
+ * Smarty date_format modifier plugin
+ * Type:     modifier<br>
+ * Name:     date_format<br>
+ * Purpose:  format datestamps via strftime<br>
+ * Input:<br>
+ *          - string: input date string
+ *          - format: strftime format for output
+ *          - default_date: default date if $string is empty
+ *
+ * @link   http://www.smarty.net/manual/en/language.modifier.date.format.php date_format (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param string $string       input date string
+ * @param string $format       strftime format for output
+ * @param string $default_date default date if $string is empty
+ * @param string $formatter    either 'strftime' or 'auto'
+ *
+ * @return string |void
+ * @uses   smarty_make_timestamp()
+ */
+function smarty_modifier_date_format($string, $format = null, $default_date = '', $formatter = 'auto')
+{
+    if ($format === null) {
+        $format = Smarty::$_DATE_FORMAT;
+    }
+    /**
+     * require_once the {@link shared.make_timestamp.php} plugin
+     */
+    static $is_loaded = false;
+    if (!$is_loaded) {
+        if (!is_callable('smarty_make_timestamp')) {
+            require_once(SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php');
+        }
+        $is_loaded = true;
+    }
+    if ($string != '' && $string != '0000-00-00' && $string != '0000-00-00 00:00:00') {
+        $timestamp = smarty_make_timestamp($string);
+    } elseif ($default_date != '') {
+        $timestamp = smarty_make_timestamp($default_date);
+    } else {
+        return;
+    }
+    if ($formatter == 'strftime' || ($formatter == 'auto' && strpos($format, '%') !== false)) {
+        if (Smarty::$_IS_WINDOWS) {
+            $_win_from = array('%D',
+                               '%h',
+                               '%n',
+                               '%r',
+                               '%R',
+                               '%t',
+                               '%T');
+            $_win_to = array('%m/%d/%y',
+                             '%b',
+                             "\n",
+                             '%I:%M:%S %p',
+                             '%H:%M',
+                             "\t",
+                             '%H:%M:%S');
+            if (strpos($format, '%e') !== false) {
+                $_win_from[] = '%e';
+                $_win_to[] = sprintf('%\' 2d', date('j', $timestamp));
+            }
+            if (strpos($format, '%l') !== false) {
+                $_win_from[] = '%l';
+                $_win_to[] = sprintf('%\' 2d', date('h', $timestamp));
+            }
+            $format = str_replace($_win_from, $_win_to, $format);
+        }
+
+        return strftime($format, $timestamp);
+    } else {
+        return date($format, $timestamp);
+    }
+}

+ 112 - 0
Mall/Framework/Smarty/libs/plugins/modifier.debug_print_var.php

@@ -0,0 +1,112 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage Debug
+ */
+
+/**
+ * Smarty debug_print_var modifier plugin
+ * Type:     modifier<br>
+ * Name:     debug_print_var<br>
+ * Purpose:  formats variable contents for display in the console
+ *
+ * @author Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param array|object $var     variable to be formatted
+ * @param int          $max     maximum recursion depth if $var is an array or object
+ * @param int          $length  maximum string length if $var is a string
+ * @param int          $depth   actual recursion depth
+ * @param array        $objects processed objects in actual depth to prevent recursive object processing
+ *
+ * @return string
+ */
+function smarty_modifier_debug_print_var($var, $max = 10, $length = 40, $depth = 0, $objects = array())
+{
+    $_replace = array("\n" => '\n', "\r" => '\r', "\t" => '\t');
+    switch (gettype($var)) {
+        case 'array' :
+            $results = '<b>Array (' . count($var) . ')</b>';
+            if ($depth == $max) {
+                break;
+            }
+            foreach ($var as $curr_key => $curr_val) {
+                $results .= '<br>' . str_repeat('&nbsp;', $depth * 2) . '<b>' . strtr($curr_key, $_replace) .
+                            '</b> =&gt; ' .
+                            smarty_modifier_debug_print_var($curr_val, $max, $length, ++ $depth, $objects);
+                $depth --;
+            }
+            break;
+
+        case 'object' :
+            $object_vars = get_object_vars($var);
+            $results = '<b>' . get_class($var) . ' Object (' . count($object_vars) . ')</b>';
+            if (in_array($var, $objects)) {
+                $results .= ' called recursive';
+                break;
+            }
+            if ($depth == $max) {
+                break;
+            }
+            $objects[] = $var;
+            foreach ($object_vars as $curr_key => $curr_val) {
+                $results .= '<br>' . str_repeat('&nbsp;', $depth * 2) . '<b> -&gt;' . strtr($curr_key, $_replace) .
+                            '</b> = ' . smarty_modifier_debug_print_var($curr_val, $max, $length, ++ $depth, $objects);
+                $depth --;
+            }
+            break;
+
+        case 'boolean' :
+        case 'NULL' :
+        case 'resource' :
+            if (true === $var) {
+                $results = 'true';
+            } elseif (false === $var) {
+                $results = 'false';
+            } elseif (null === $var) {
+                $results = 'null';
+            } else {
+                $results = htmlspecialchars((string) $var);
+            }
+            $results = '<i>' . $results . '</i>';
+            break;
+
+        case 'integer' :
+        case 'float' :
+            $results = htmlspecialchars((string) $var);
+            break;
+
+        case 'string' :
+            $results = strtr($var, $_replace);
+            if (Smarty::$_MBSTRING) {
+                if (mb_strlen($var, Smarty::$_CHARSET) > $length) {
+                    $results = mb_substr($var, 0, $length - 3, Smarty::$_CHARSET) . '...';
+                }
+            } else {
+                if (isset($var[ $length ])) {
+                    $results = substr($var, 0, $length - 3) . '...';
+                }
+            }
+
+            $results = htmlspecialchars('"' . $results . '"', ENT_QUOTES, Smarty::$_CHARSET);
+            break;
+
+        case 'unknown type' :
+        default :
+            $results = strtr((string) $var, $_replace);
+            if (Smarty::$_MBSTRING) {
+                if (mb_strlen($results, Smarty::$_CHARSET) > $length) {
+                    $results = mb_substr($results, 0, $length - 3, Smarty::$_CHARSET) . '...';
+                }
+            } else {
+                if (strlen($results) > $length) {
+                    $results = substr($results, 0, $length - 3) . '...';
+                }
+            }
+
+            $results = htmlspecialchars($results, ENT_QUOTES, Smarty::$_CHARSET);
+    }
+
+    return $results;
+}

+ 232 - 0
Mall/Framework/Smarty/libs/plugins/modifier.escape.php

@@ -0,0 +1,232 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifier
+ */
+
+/**
+ * Smarty escape modifier plugin
+ * Type:     modifier<br>
+ * Name:     escape<br>
+ * Purpose:  escape string for output
+ *
+ * @link   http://www.smarty.net/docs/en/language.modifier.escape
+ * @author Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param string  $string        input string
+ * @param string  $esc_type      escape type
+ * @param string  $char_set      character set, used for htmlspecialchars() or htmlentities()
+ * @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities()
+ *
+ * @return string escaped input string
+ */
+function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true)
+{
+    static $_double_encode = null;
+    static $is_loaded1 = false;
+    static $is_loaded2 = false;
+    if ($_double_encode === null) {
+        $_double_encode = version_compare(PHP_VERSION, '5.2.3', '>=');
+    }
+
+    if (!$char_set) {
+        $char_set = Smarty::$_CHARSET;
+    }
+
+    switch ($esc_type) {
+        case 'html':
+            if ($_double_encode) {
+                // php >=5.3.2 - go native
+                return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
+            } else {
+                if ($double_encode) {
+                    // php <5.2.3 - only handle double encoding
+                    return htmlspecialchars($string, ENT_QUOTES, $char_set);
+                } else {
+                    // php <5.2.3 - prevent double encoding
+                    $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
+                    $string = htmlspecialchars($string, ENT_QUOTES, $char_set);
+                    $string = str_replace(array('%%%SMARTY_START%%%',
+                                                '%%%SMARTY_END%%%'), array('&',
+                                                                           ';'), $string);
+
+                    return $string;
+                }
+            }
+
+        case 'htmlall':
+            if (Smarty::$_MBSTRING) {
+                // mb_convert_encoding ignores htmlspecialchars()
+                if ($_double_encode) {
+                    // php >=5.3.2 - go native
+                    $string = htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
+                } else {
+                    if ($double_encode) {
+                        // php <5.2.3 - only handle double encoding
+                        $string = htmlspecialchars($string, ENT_QUOTES, $char_set);
+                    } else {
+                        // php <5.2.3 - prevent double encoding
+                        $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
+                        $string = htmlspecialchars($string, ENT_QUOTES, $char_set);
+                        $string =
+                            str_replace(array('%%%SMARTY_START%%%',
+                                              '%%%SMARTY_END%%%'), array('&',
+                                                                         ';'), $string);
+
+                        return $string;
+                    }
+                }
+
+                // htmlentities() won't convert everything, so use mb_convert_encoding
+                return mb_convert_encoding($string, 'HTML-ENTITIES', $char_set);
+            }
+
+            // no MBString fallback
+            if ($_double_encode) {
+                return htmlentities($string, ENT_QUOTES, $char_set, $double_encode);
+            } else {
+                if ($double_encode) {
+                    return htmlentities($string, ENT_QUOTES, $char_set);
+                } else {
+                    $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
+                    $string = htmlentities($string, ENT_QUOTES, $char_set);
+                    $string = str_replace(array('%%%SMARTY_START%%%',
+                                                '%%%SMARTY_END%%%'), array('&',
+                                                                           ';'), $string);
+
+                    return $string;
+                }
+            }
+
+        case 'url':
+            return rawurlencode($string);
+
+        case 'urlpathinfo':
+            return str_replace('%2F', '/', rawurlencode($string));
+
+        case 'quotes':
+            // escape unescaped single quotes
+            return preg_replace("%(?<!\\\\)'%", "\\'", $string);
+
+        case 'hex':
+            // escape every byte into hex
+            // Note that the UTF-8 encoded character ä will be represented as %c3%a4
+            $return = '';
+            $_length = strlen($string);
+            for ($x = 0; $x < $_length; $x ++) {
+                $return .= '%' . bin2hex($string[ $x ]);
+            }
+
+            return $return;
+
+        case 'hexentity':
+            $return = '';
+            if (Smarty::$_MBSTRING) {
+                if (!$is_loaded1) {
+                    if (!is_callable('smarty_mb_to_unicode')) {
+                        require_once(SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php');
+                        $is_loaded1 = true;
+                    }
+                }
+                $return = '';
+                foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) {
+                    $return .= '&#x' . strtoupper(dechex($unicode)) . ';';
+                }
+
+                return $return;
+            }
+            // no MBString fallback
+            $_length = strlen($string);
+            for ($x = 0; $x < $_length; $x ++) {
+                $return .= '&#x' . bin2hex($string[ $x ]) . ';';
+            }
+
+            return $return;
+
+        case 'decentity':
+            $return = '';
+            if (Smarty::$_MBSTRING) {
+                if (!$is_loaded1) {
+                    if (!is_callable('smarty_mb_to_unicode')) {
+                        require_once(SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php');
+                    }
+                    $is_loaded1 = true;
+                }
+                $return = '';
+                foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) {
+                    $return .= '&#' . $unicode . ';';
+                }
+
+                return $return;
+            }
+            // no MBString fallback
+            $_length = strlen($string);
+            for ($x = 0; $x < $_length; $x ++) {
+                $return .= '&#' . ord($string[ $x ]) . ';';
+            }
+
+            return $return;
+
+        case 'javascript':
+            // escape quotes and backslashes, newlines, etc.
+            return strtr($string, array('\\' => '\\\\',
+                                        "'" => "\\'",
+                                        '"' => '\\"',
+                                        "\r" => '\\r',
+                                        "\n" => '\\n',
+                                        '</' => '<\/'));
+
+        case 'mail':
+            if (Smarty::$_MBSTRING) {
+                if (!is_callable('smarty_mb_str_replace')) {
+                    require_once(SMARTY_PLUGINS_DIR . 'shared.mb_str_replace.php');
+                }
+                return smarty_mb_str_replace(array('@',
+                                                   '.'), array(' [AT] ',
+                                                               ' [DOT] '), $string);
+            }
+            // no MBString fallback
+            return str_replace(array('@',
+                                     '.'), array(' [AT] ',
+                                                 ' [DOT] '), $string);
+
+        case 'nonstd':
+            // escape non-standard chars, such as ms document quotes
+            $return = '';
+            if (Smarty::$_MBSTRING) {
+                if (!$is_loaded1) {
+                    if (!is_callable('smarty_mb_to_unicode')) {
+                        require_once(SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php');
+                    }
+                    $is_loaded1 = true;
+                }
+                foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) {
+                    if ($unicode >= 126) {
+                        $return .= '&#' . $unicode . ';';
+                    } else {
+                        $return .= chr($unicode);
+                    }
+                }
+
+                return $return;
+            }
+
+            $_length = strlen($string);
+            for ($_i = 0; $_i < $_length; $_i ++) {
+                $_ord = ord(substr($string, $_i, 1));
+                // non-standard char, escape it
+                if ($_ord >= 126) {
+                    $return .= '&#' . $_ord . ';';
+                } else {
+                    $return .= substr($string, $_i, 1);
+                }
+            }
+
+            return $return;
+
+        default:
+            return $string;
+    }
+}

+ 58 - 0
Mall/Framework/Smarty/libs/plugins/modifier.regex_replace.php

@@ -0,0 +1,58 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifier
+ */
+
+/**
+ * Smarty regex_replace modifier plugin
+ * Type:     modifier<br>
+ * Name:     regex_replace<br>
+ * Purpose:  regular expression search/replace
+ *
+ * @link    http://smarty.php.net/manual/en/language.modifier.regex.replace.php
+ *          regex_replace (Smarty online manual)
+ * @author  Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param string       $string  input string
+ * @param string|array $search  regular expression(s) to search for
+ * @param string|array $replace string(s) that should be replaced
+ * @param int          $limit   the maximum number of replacements
+ *
+ * @return string
+ */
+function smarty_modifier_regex_replace($string, $search, $replace, $limit = - 1)
+{
+    if (is_array($search)) {
+        foreach ($search as $idx => $s) {
+            $search[ $idx ] = _smarty_regex_replace_check($s);
+        }
+    } else {
+        $search = _smarty_regex_replace_check($search);
+    }
+
+    return preg_replace($search, $replace, $string, $limit);
+}
+
+/**
+ * @param  string $search string(s) that should be replaced
+ *
+ * @return string
+ * @ignore
+ */
+function _smarty_regex_replace_check($search)
+{
+    // null-byte injection detection
+    // anything behind the first null-byte is ignored
+    if (($pos = strpos($search, "\0")) !== false) {
+        $search = substr($search, 0, $pos);
+    }
+    // remove eval-modifier from $search
+    if (preg_match('!([a-zA-Z\s]+)$!s', $search, $match) && (strpos($match[ 1 ], 'e') !== false)) {
+        $search = substr($search, 0, - strlen($match[ 1 ])) . preg_replace('![e\s]+!', '', $match[ 1 ]);
+    }
+
+    return $search;
+}

+ 39 - 0
Mall/Framework/Smarty/libs/plugins/modifier.replace.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifier
+ */
+
+/**
+ * Smarty replace modifier plugin
+ * Type:     modifier<br>
+ * Name:     replace<br>
+ * Purpose:  simple search/replace
+ *
+ * @link   http://smarty.php.net/manual/en/language.modifier.replace.php replace (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author Uwe Tews
+ *
+ * @param string $string  input string
+ * @param string $search  text to search for
+ * @param string $replace replacement text
+ *
+ * @return string
+ */
+function smarty_modifier_replace($string, $search, $replace)
+{
+    static $is_loaded = false;
+    if (Smarty::$_MBSTRING) {
+        if (!$is_loaded) {
+            if (!is_callable('smarty_mb_str_replace')) {
+                require_once(SMARTY_PLUGINS_DIR . 'shared.mb_str_replace.php');
+            }
+            $is_loaded = true;
+        }
+         return smarty_mb_str_replace($search, $replace, $string);
+    }
+
+    return str_replace($search, $replace, $string);
+}

+ 27 - 0
Mall/Framework/Smarty/libs/plugins/modifier.spacify.php

@@ -0,0 +1,27 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifier
+ */
+
+/**
+ * Smarty spacify modifier plugin
+ * Type:     modifier<br>
+ * Name:     spacify<br>
+ * Purpose:  add spaces between characters in a string
+ *
+ * @link   http://smarty.php.net/manual/en/language.modifier.spacify.php spacify (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param string $string       input string
+ * @param string $spacify_char string to insert between characters.
+ *
+ * @return string
+ */
+function smarty_modifier_spacify($string, $spacify_char = ' ')
+{
+    // well… what about charsets besides latin and UTF-8?
+    return implode($spacify_char, preg_split('//' . Smarty::$_UTF8_MODIFIER, $string, - 1, PREG_SPLIT_NO_EMPTY));
+}

+ 66 - 0
Mall/Framework/Smarty/libs/plugins/modifier.truncate.php

@@ -0,0 +1,66 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifier
+ */
+
+/**
+ * Smarty truncate modifier plugin
+ * Type:     modifier<br>
+ * Name:     truncate<br>
+ * Purpose:  Truncate a string to a certain length if necessary,
+ *               optionally splitting in the middle of a word, and
+ *               appending the $etc string or inserting $etc into the middle.
+ *
+ * @link   http://smarty.php.net/manual/en/language.modifier.truncate.php truncate (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param string  $string      input string
+ * @param integer $length      length of truncated text
+ * @param string  $etc         end string
+ * @param boolean $break_words truncate at word boundary
+ * @param boolean $middle      truncate in the middle of text
+ *
+ * @return string truncated string
+ */
+function smarty_modifier_truncate($string, $length = 80, $etc = '...', $break_words = false, $middle = false)
+{
+    if ($length == 0) {
+        return '';
+    }
+
+    if (Smarty::$_MBSTRING) {
+        if (mb_strlen($string, Smarty::$_CHARSET) > $length) {
+            $length -= min($length, mb_strlen($etc, Smarty::$_CHARSET));
+            if (!$break_words && !$middle) {
+                $string = preg_replace('/\s+?(\S+)?$/' . Smarty::$_UTF8_MODIFIER, '',
+                                       mb_substr($string, 0, $length + 1, Smarty::$_CHARSET));
+            }
+            if (!$middle) {
+                return mb_substr($string, 0, $length, Smarty::$_CHARSET) . $etc;
+            }
+
+            return mb_substr($string, 0, $length / 2, Smarty::$_CHARSET) . $etc .
+                   mb_substr($string, - $length / 2, $length, Smarty::$_CHARSET);
+        }
+
+        return $string;
+    }
+
+    // no MBString fallback
+    if (isset($string[ $length ])) {
+        $length -= min($length, strlen($etc));
+        if (!$break_words && !$middle) {
+            $string = preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, $length + 1));
+        }
+        if (!$middle) {
+            return substr($string, 0, $length) . $etc;
+        }
+
+        return substr($string, 0, $length / 2) . $etc . substr($string, - $length / 2);
+    }
+
+    return $string;
+}

+ 29 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.cat.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty cat modifier plugin
+ * Type:     modifier<br>
+ * Name:     cat<br>
+ * Date:     Feb 24, 2003<br>
+ * Purpose:  catenate a value to a variable<br>
+ * Input:    string to catenate<br>
+ * Example:  {$var|cat:"foo"}
+ *
+ * @link     http://smarty.php.net/manual/en/language.modifier.cat.php cat
+ *           (Smarty online manual)
+ * @author   Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_cat($params)
+{
+    return '(' . implode(').(', $params) . ')';
+}

+ 32 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.count_characters.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty count_characters modifier plugin
+ * Type:     modifier<br>
+ * Name:     count_characteres<br>
+ * Purpose:  count the number of characters in a text
+ *
+ * @link   http://www.smarty.net/manual/en/language.modifier.count.characters.php count_characters (Smarty online manual)
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_count_characters($params)
+{
+    if (!isset($params[ 1 ]) || $params[ 1 ] != 'true') {
+        return 'preg_match_all(\'/[^\s]/' . Smarty::$_UTF8_MODIFIER . '\',' . $params[ 0 ] . ', $tmp)';
+    }
+    if (Smarty::$_MBSTRING) {
+        return 'mb_strlen(' . $params[ 0 ] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')';
+    }
+    // no MBString fallback
+    return 'strlen(' . $params[ 0 ] . ')';
+}

+ 27 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.count_paragraphs.php

@@ -0,0 +1,27 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty count_paragraphs modifier plugin
+ * Type:     modifier<br>
+ * Name:     count_paragraphs<br>
+ * Purpose:  count the number of paragraphs in a text
+ *
+ * @link    http://www.smarty.net/manual/en/language.modifier.count.paragraphs.php
+ *          count_paragraphs (Smarty online manual)
+ * @author  Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_count_paragraphs($params)
+{
+    // count \r or \n characters
+    return '(preg_match_all(\'#[\r\n]+#\', ' . $params[ 0 ] . ', $tmp)+1)';
+}

+ 27 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.count_sentences.php

@@ -0,0 +1,27 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty count_sentences modifier plugin
+ * Type:     modifier<br>
+ * Name:     count_sentences
+ * Purpose:  count the number of sentences in a text
+ *
+ * @link    http://www.smarty.net/manual/en/language.modifier.count.paragraphs.php
+ *          count_sentences (Smarty online manual)
+ * @author  Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_count_sentences($params)
+{
+    // find periods, question marks, exclamation marks with a word before but not after.
+    return 'preg_match_all("#\w[\.\?\!](\W|$)#S' . Smarty::$_UTF8_MODIFIER . '", ' . $params[ 0 ] . ', $tmp)';
+}

+ 32 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.count_words.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty count_words modifier plugin
+ * Type:     modifier<br>
+ * Name:     count_words<br>
+ * Purpose:  count the number of words in a text
+ *
+ * @link   http://www.smarty.net/manual/en/language.modifier.count.words.php count_words (Smarty online manual)
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_count_words($params)
+{
+    if (Smarty::$_MBSTRING) {
+        // return 'preg_match_all(\'#[\w\pL]+#' . Smarty::$_UTF8_MODIFIER . '\', ' . $params[0] . ', $tmp)';
+        // expression taken from http://de.php.net/manual/en/function.str-word-count.php#85592
+        return 'preg_match_all(\'/\p{L}[\p{L}\p{Mn}\p{Pd}\\\'\x{2019}]*/' . Smarty::$_UTF8_MODIFIER . '\', ' .
+               $params[ 0 ] . ', $tmp)';
+    }
+    // no MBString fallback
+    return 'str_word_count(' . $params[ 0 ] . ')';
+}

+ 35 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.default.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty default modifier plugin
+ * Type:     modifier<br>
+ * Name:     default<br>
+ * Purpose:  designate default value for empty variables
+ *
+ * @link   http://www.smarty.net/manual/en/language.modifier.default.php default (Smarty online manual)
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_default($params)
+{
+    $output = $params[ 0 ];
+    if (!isset($params[ 1 ])) {
+        $params[ 1 ] = "''";
+    }
+
+    array_shift($params);
+    foreach ($params as $param) {
+        $output = '(($tmp = @' . $output . ')===null||$tmp===\'\' ? ' . $param . ' : $tmp)';
+    }
+
+    return $output;
+}

+ 119 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.escape.php

@@ -0,0 +1,119 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty escape modifier plugin
+ * Type:     modifier<br>
+ * Name:     escape<br>
+ * Purpose:  escape string for output
+ *
+ * @link   http://www.smarty.net/docsv2/en/language.modifier.escape count_characters (Smarty online manual)
+ * @author Rodney Rehm
+ *
+ * @param array $params parameters
+ * @param       $compiler
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_escape($params, $compiler)
+{
+    static $_double_encode = null;
+    static $is_loaded = false;
+    if (!$is_loaded) {
+        if (!is_callable('smarty_literal_compiler_param')) {
+            require_once(SMARTY_PLUGINS_DIR . 'shared.literal_compiler_param.php');
+        }
+        $is_loaded = true;
+    }
+    if ($_double_encode === null) {
+        $_double_encode = version_compare(PHP_VERSION, '5.2.3', '>=');
+    }
+
+    try {
+        $esc_type = smarty_literal_compiler_param($params, 1, 'html');
+        $char_set = smarty_literal_compiler_param($params, 2, Smarty::$_CHARSET);
+        $double_encode = smarty_literal_compiler_param($params, 3, true);
+
+        if (!$char_set) {
+            $char_set = Smarty::$_CHARSET;
+        }
+
+        switch ($esc_type) {
+            case 'html':
+                if ($_double_encode) {
+                    return 'htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
+                           var_export($double_encode, true) . ')';
+                } elseif ($double_encode) {
+                    return 'htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ')';
+                } else {
+                    // fall back to modifier.escape.php
+                }
+
+            case 'htmlall':
+                if (Smarty::$_MBSTRING) {
+                    if ($_double_encode) {
+                        // php >=5.2.3 - go native
+                        return 'mb_convert_encoding(htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' .
+                               var_export($char_set, true) . ', ' . var_export($double_encode, true) .
+                               '), "HTML-ENTITIES", ' . var_export($char_set, true) . ')';
+                    } elseif ($double_encode) {
+                        // php <5.2.3 - only handle double encoding
+                        return 'mb_convert_encoding(htmlspecialchars(' . $params[ 0 ] . ', ENT_QUOTES, ' .
+                               var_export($char_set, true) . '), "HTML-ENTITIES", ' . var_export($char_set, true) . ')';
+                    } else {
+                        // fall back to modifier.escape.php
+                    }
+                }
+
+                // no MBString fallback
+                if ($_double_encode) {
+                    // php >=5.2.3 - go native
+                    return 'htmlentities(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
+                           var_export($double_encode, true) . ')';
+                } elseif ($double_encode) {
+                    // php <5.2.3 - only handle double encoding
+                    return 'htmlentities(' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ')';
+                } else {
+                    // fall back to modifier.escape.php
+                }
+
+            case 'url':
+                return 'rawurlencode(' . $params[ 0 ] . ')';
+
+            case 'urlpathinfo':
+                return 'str_replace("%2F", "/", rawurlencode(' . $params[ 0 ] . '))';
+
+            case 'quotes':
+                // escape unescaped single quotes
+                return 'preg_replace("%(?<!\\\\\\\\)\'%", "\\\'",' . $params[ 0 ] . ')';
+
+            case 'javascript':
+                // escape quotes and backslashes, newlines, etc.
+                return 'strtr(' . $params[ 0 ] .
+                       ', array("\\\\" => "\\\\\\\\", "\'" => "\\\\\'", "\"" => "\\\\\"", "\\r" => "\\\\r", "\\n" => "\\\n", "</" => "<\/" ))';
+        }
+    }
+    catch (SmartyException $e) {
+        // pass through to regular plugin fallback
+    }
+
+    // could not optimize |escape call, so fallback to regular plugin
+    if ($compiler->template->caching && ($compiler->tag_nocache | $compiler->nocache)) {
+        $compiler->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ 'escape' ][ 'modifier' ][ 'file' ] =
+            SMARTY_PLUGINS_DIR . 'modifier.escape.php';
+        $compiler->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ 'escape' ][ 'modifier' ][ 'function' ] =
+            'smarty_modifier_escape';
+    } else {
+        $compiler->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ 'escape' ][ 'modifier' ][ 'file' ] =
+            SMARTY_PLUGINS_DIR . 'modifier.escape.php';
+        $compiler->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ 'escape' ][ 'modifier' ][ 'function' ] =
+            'smarty_modifier_escape';
+    }
+
+    return 'smarty_modifier_escape(' . join(', ', $params) . ')';
+}

+ 33 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.from_charset.php

@@ -0,0 +1,33 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty from_charset modifier plugin
+ * Type:     modifier<br>
+ * Name:     from_charset<br>
+ * Purpose:  convert character encoding from $charset to internal encoding
+ *
+ * @author Rodney Rehm
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_from_charset($params)
+{
+    if (!Smarty::$_MBSTRING) {
+        // FIXME: (rodneyrehm) shouldn't this throw an error?
+        return $params[ 0 ];
+    }
+
+    if (!isset($params[ 1 ])) {
+        $params[ 1 ] = '"ISO-8859-1"';
+    }
+
+    return 'mb_convert_encoding(' . $params[ 0 ] . ', "' . addslashes(Smarty::$_CHARSET) . '", ' . $params[ 1 ] . ')';
+}

+ 33 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.indent.php

@@ -0,0 +1,33 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty indent modifier plugin
+ * Type:     modifier<br>
+ * Name:     indent<br>
+ * Purpose:  indent lines of text
+ *
+ * @link   http://www.smarty.net/manual/en/language.modifier.indent.php indent (Smarty online manual)
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+
+function smarty_modifiercompiler_indent($params)
+{
+    if (!isset($params[ 1 ])) {
+        $params[ 1 ] = 4;
+    }
+    if (!isset($params[ 2 ])) {
+        $params[ 2 ] = "' '";
+    }
+
+    return 'preg_replace(\'!^!m\',str_repeat(' . $params[ 2 ] . ',' . $params[ 1 ] . '),' . $params[ 0 ] . ')';
+}

+ 31 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.lower.php

@@ -0,0 +1,31 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty lower modifier plugin
+ * Type:     modifier<br>
+ * Name:     lower<br>
+ * Purpose:  convert string to lowercase
+ *
+ * @link   http://www.smarty.net/manual/en/language.modifier.lower.php lower (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+
+function smarty_modifiercompiler_lower($params)
+{
+    if (Smarty::$_MBSTRING) {
+        return 'mb_strtolower(' . $params[ 0 ] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')';
+    }
+    // no MBString fallback
+    return 'strtolower(' . $params[ 0 ] . ')';
+}

+ 21 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.noprint.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty noprint modifier plugin
+ * Type:     modifier<br>
+ * Name:     noprint<br>
+ * Purpose:  return an empty string
+ *
+ * @author   Uwe Tews
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_noprint()
+{
+    return "''";
+}

+ 25 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.string_format.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty string_format modifier plugin
+ * Type:     modifier<br>
+ * Name:     string_format<br>
+ * Purpose:  format strings via sprintf
+ *
+ * @link   http://www.smarty.net/manual/en/language.modifier.string.format.php string_format (Smarty online manual)
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_string_format($params)
+{
+    return 'sprintf(' . $params[ 1 ] . ',' . $params[ 0 ] . ')';
+}

+ 33 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.strip.php

@@ -0,0 +1,33 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty strip modifier plugin
+ * Type:     modifier<br>
+ * Name:     strip<br>
+ * Purpose:  Replace all repeated spaces, newlines, tabs
+ *              with a single space or supplied replacement string.<br>
+ * Example:  {$var|strip} {$var|strip:"&nbsp;"}<br>
+ * Date:     September 25th, 2002
+ *
+ * @link   http://www.smarty.net/manual/en/language.modifier.strip.php strip (Smarty online manual)
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+
+function smarty_modifiercompiler_strip($params)
+{
+    if (!isset($params[ 1 ])) {
+        $params[ 1 ] = "' '";
+    }
+
+    return "preg_replace('!\s+!" . Smarty::$_UTF8_MODIFIER . "', {$params[1]},{$params[0]})";
+}

+ 29 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.strip_tags.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty strip_tags modifier plugin
+ * Type:     modifier<br>
+ * Name:     strip_tags<br>
+ * Purpose:  strip html tags from text
+ *
+ * @link   http://www.smarty.net/docs/en/language.modifier.strip.tags.tpl strip_tags (Smarty online manual)
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_strip_tags($params)
+{
+    if (!isset($params[ 1 ]) || $params[ 1 ] === true || trim($params[ 1 ], '"') == 'true') {
+        return "preg_replace('!<[^>]*?>!', ' ', {$params[0]})";
+    } else {
+        return 'strip_tags(' . $params[ 0 ] . ')';
+    }
+}

+ 33 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.to_charset.php

@@ -0,0 +1,33 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty to_charset modifier plugin
+ * Type:     modifier<br>
+ * Name:     to_charset<br>
+ * Purpose:  convert character encoding from internal encoding to $charset
+ *
+ * @author Rodney Rehm
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_to_charset($params)
+{
+    if (!Smarty::$_MBSTRING) {
+        // FIXME: (rodneyrehm) shouldn't this throw an error?
+        return $params[ 0 ];
+    }
+
+    if (!isset($params[ 1 ])) {
+        $params[ 1 ] = '"ISO-8859-1"';
+    }
+
+    return 'mb_convert_encoding(' . $params[ 0 ] . ', ' . $params[ 1 ] . ', "' . addslashes(Smarty::$_CHARSET) . '")';
+}

+ 50 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.unescape.php

@@ -0,0 +1,50 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty unescape modifier plugin
+ * Type:     modifier<br>
+ * Name:     unescape<br>
+ * Purpose:  unescape html entities
+ *
+ * @author Rodney Rehm
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_unescape($params)
+{
+    if (!isset($params[ 1 ])) {
+        $params[ 1 ] = 'html';
+    }
+    if (!isset($params[ 2 ])) {
+        $params[ 2 ] = '\'' . addslashes(Smarty::$_CHARSET) . '\'';
+    } else {
+        $params[ 2 ] = "'" . $params[ 2 ] . "'";
+    }
+
+    switch (trim($params[ 1 ], '"\'')) {
+        case 'entity':
+        case 'htmlall':
+            if (Smarty::$_MBSTRING) {
+                return 'mb_convert_encoding(' . $params[ 0 ] . ', ' . $params[ 2 ] . ', \'HTML-ENTITIES\')';
+            }
+
+            return 'html_entity_decode(' . $params[ 0 ] . ', ENT_NOQUOTES, ' . $params[ 2 ] . ')';
+
+        case 'html':
+            return 'htmlspecialchars_decode(' . $params[ 0 ] . ', ENT_QUOTES)';
+
+        case 'url':
+            return 'rawurldecode(' . $params[ 0 ] . ')';
+
+        default:
+            return $params[ 0 ];
+    }
+}

+ 29 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.upper.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty upper modifier plugin
+ * Type:     modifier<br>
+ * Name:     lower<br>
+ * Purpose:  convert string to uppercase
+ *
+ * @link   http://smarty.php.net/manual/en/language.modifier.upper.php lower (Smarty online manual)
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_upper($params)
+{
+    if (Smarty::$_MBSTRING) {
+        return 'mb_strtoupper(' . $params[ 0 ] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')';
+    }
+    // no MBString fallback
+    return 'strtoupper(' . $params[ 0 ] . ')';
+}

+ 51 - 0
Mall/Framework/Smarty/libs/plugins/modifiercompiler.wordwrap.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsModifierCompiler
+ */
+
+/**
+ * Smarty wordwrap modifier plugin
+ * Type:     modifier<br>
+ * Name:     wordwrap<br>
+ * Purpose:  wrap a string of text at a given length
+ *
+ * @link   http://smarty.php.net/manual/en/language.modifier.wordwrap.php wordwrap (Smarty online manual)
+ * @author Uwe Tews
+ *
+ * @param array $params parameters
+ * @param       $compiler
+ *
+ * @return string with compiled code
+ */
+function smarty_modifiercompiler_wordwrap($params, $compiler)
+{
+    if (!isset($params[ 1 ])) {
+        $params[ 1 ] = 80;
+    }
+    if (!isset($params[ 2 ])) {
+        $params[ 2 ] = '"\n"';
+    }
+    if (!isset($params[ 3 ])) {
+        $params[ 3 ] = 'false';
+    }
+    $function = 'wordwrap';
+    if (Smarty::$_MBSTRING) {
+        if ($compiler->template->caching && ($compiler->tag_nocache | $compiler->nocache)) {
+            $compiler->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ 'wordwrap' ][ 'modifier' ][ 'file' ] =
+                SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php';
+            $compiler->template->required_plugins[ 'nocache' ][ 'wordwrap' ][ 'modifier' ][ 'function' ] =
+                'smarty_mb_wordwrap';
+        } else {
+            $compiler->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ 'wordwrap' ][ 'modifier' ][ 'file' ] =
+                SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php';
+            $compiler->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ 'wordwrap' ][ 'modifier' ][ 'function' ] =
+                'smarty_mb_wordwrap';
+        }
+        $function = 'smarty_mb_wordwrap';
+    }
+
+    return $function . '(' . $params[ 0 ] . ',' . $params[ 1 ] . ',' . $params[ 2 ] . ',' . $params[ 3 ] . ')';
+}

+ 89 - 0
Mall/Framework/Smarty/libs/plugins/outputfilter.trimwhitespace.php

@@ -0,0 +1,89 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFilter
+ */
+
+/**
+ * Smarty trimwhitespace outputfilter plugin
+ * Trim unnecessary whitespace from HTML markup.
+ *
+ * @author   Rodney Rehm
+ *
+ * @param string $source input string
+ *
+ * @return string filtered output
+ * @todo     substr_replace() is not overloaded by mbstring.func_overload - so this function might fail!
+ */
+function smarty_outputfilter_trimwhitespace($source)
+{
+    $store = array();
+    $_store = 0;
+    $_offset = 0;
+
+    // Unify Line-Breaks to \n
+    $source = preg_replace("/\015\012|\015|\012/", "\n", $source);
+
+    // capture Internet Explorer and KnockoutJS Conditional Comments
+    if (preg_match_all('#<!--((\[[^\]]+\]>.*?<!\[[^\]]+\])|(\s*/?ko\s+.+))-->#is', $source, $matches,
+                       PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
+        foreach ($matches as $match) {
+            $store[] = $match[ 0 ][ 0 ];
+            $_length = strlen($match[ 0 ][ 0 ]);
+            $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
+            $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);
+
+            $_offset += $_length - strlen($replace);
+            $_store ++;
+        }
+    }
+
+    // Strip all HTML-Comments
+    // yes, even the ones in <script> - see http://stackoverflow.com/a/808850/515124
+    $source = preg_replace('#<!--.*?-->#ms', '', $source);
+
+    // capture html elements not to be messed with
+    $_offset = 0;
+    if (preg_match_all('#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is',
+                       $source, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
+        foreach ($matches as $match) {
+            $store[] = $match[ 0 ][ 0 ];
+            $_length = strlen($match[ 0 ][ 0 ]);
+            $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
+            $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);
+
+            $_offset += $_length - strlen($replace);
+            $_store ++;
+        }
+    }
+
+    $expressions = array(// replace multiple spaces between tags by a single space
+                         // can't remove them entirely, becaue that might break poorly implemented CSS display:inline-block elements
+                         '#(:SMARTY@!@|>)\s+(?=@!@SMARTY:|<)#s' => '\1 \2',
+                         // remove spaces between attributes (but not in attribute values!)
+                         '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5',
+                         // note: for some very weird reason trim() seems to remove spaces inside attributes.
+                         // maybe a \0 byte or something is interfering?
+                         '#^\s+<#Ss' => '<', '#>\s+$#Ss' => '>',);
+
+    $source = preg_replace(array_keys($expressions), array_values($expressions), $source);
+    // note: for some very weird reason trim() seems to remove spaces inside attributes.
+    // maybe a \0 byte or something is interfering?
+    // $source = trim( $source );
+
+    $_offset = 0;
+    if (preg_match_all('#@!@SMARTY:([0-9]+):SMARTY@!@#is', $source, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
+        foreach ($matches as $match) {
+            $_length = strlen($match[ 0 ][ 0 ]);
+            $replace = $store[ $match[ 1 ][ 0 ] ];
+            $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] + $_offset, $_length);
+
+            $_offset += strlen($replace) - $_length;
+            $_store ++;
+        }
+    }
+
+    return $source;
+}

+ 34 - 0
Mall/Framework/Smarty/libs/plugins/shared.escape_special_chars.php

@@ -0,0 +1,34 @@
+<?php
+/**
+ * Smarty shared plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsShared
+ */
+
+/**
+ * escape_special_chars common function
+ * Function: smarty_function_escape_special_chars<br>
+ * Purpose:  used by other smarty functions to escape
+ *           special chars except for already escaped ones
+ *
+ * @author   Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param  string $string text that should by escaped
+ *
+ * @return string
+ */
+function smarty_function_escape_special_chars($string)
+{
+    if (!is_array($string)) {
+        if (version_compare(PHP_VERSION, '5.2.3', '>=')) {
+            $string = htmlspecialchars($string, ENT_COMPAT, Smarty::$_CHARSET, false);
+        } else {
+            $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
+            $string = htmlspecialchars($string);
+            $string = str_replace(array('%%%SMARTY_START%%%', '%%%SMARTY_END%%%'), array('&', ';'), $string);
+        }
+    }
+
+    return $string;
+}

+ 36 - 0
Mall/Framework/Smarty/libs/plugins/shared.literal_compiler_param.php

@@ -0,0 +1,36 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsShared
+ */
+
+/**
+ * evaluate compiler parameter
+ *
+ * @param array   $params  parameter array as given to the compiler function
+ * @param integer $index   array index of the parameter to convert
+ * @param mixed   $default value to be returned if the parameter is not present
+ *
+ * @return mixed evaluated value of parameter or $default
+ * @throws SmartyException if parameter is not a literal (but an expression, variable, …)
+ * @author Rodney Rehm
+ */
+function smarty_literal_compiler_param($params, $index, $default = null)
+{
+    // not set, go default
+    if (!isset($params[ $index ])) {
+        return $default;
+    }
+    // test if param is a literal
+    if (!preg_match('/^([\'"]?)[a-zA-Z0-9-]+(\\1)$/', $params[ $index ])) {
+        throw new SmartyException('$param[' . $index .
+                                  '] is not a literal and is thus not evaluatable at compile time');
+    }
+
+    $t = null;
+    eval("\$t = " . $params[ $index ] . ";");
+
+    return $t;
+}

+ 45 - 0
Mall/Framework/Smarty/libs/plugins/shared.make_timestamp.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * Smarty shared plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsShared
+ */
+
+/**
+ * Function: smarty_make_timestamp<br>
+ * Purpose:  used by other smarty functions to make a timestamp from a string.
+ *
+ * @author   Monte Ohrt <monte at ohrt dot com>
+ *
+ * @param DateTime|int|string $string date object, timestamp or string that can be converted using strtotime()
+ *
+ * @return int
+ */
+function smarty_make_timestamp($string)
+{
+    if (empty($string)) {
+        // use "now":
+        return time();
+    } elseif ($string instanceof DateTime ||
+              (interface_exists('DateTimeInterface', false) && $string instanceof DateTimeInterface)
+    ) {
+        return (int) $string->format('U'); // PHP 5.2 BC
+    } elseif (strlen($string) == 14 && ctype_digit($string)) {
+        // it is mysql timestamp format of YYYYMMDDHHMMSS?
+        return mktime(substr($string, 8, 2), substr($string, 10, 2), substr($string, 12, 2), substr($string, 4, 2),
+                      substr($string, 6, 2), substr($string, 0, 4));
+    } elseif (is_numeric($string)) {
+        // it is a numeric string, we handle it as timestamp
+        return (int) $string;
+    } else {
+        // strtotime should handle it
+        $time = strtotime($string);
+        if ($time == - 1 || $time === false) {
+            // strtotime() was not able to parse $string, use "now":
+            return time();
+        }
+
+        return $time;
+    }
+}

+ 55 - 0
Mall/Framework/Smarty/libs/plugins/shared.mb_str_replace.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * Smarty shared plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsShared
+ */
+if (!function_exists('smarty_mb_str_replace')) {
+
+    /**
+     * Multibyte string replace
+     *
+     * @param  string $search  the string to be searched
+     * @param  string $replace the replacement string
+     * @param  string $subject the source string
+     * @param  int    &$count  number of matches found
+     *
+     * @return string replaced string
+     * @author Rodney Rehm
+     */
+    function smarty_mb_str_replace($search, $replace, $subject, &$count = 0)
+    {
+        if (!is_array($search) && is_array($replace)) {
+            return false;
+        }
+        if (is_array($subject)) {
+            // call mb_replace for each single string in $subject
+            foreach ($subject as &$string) {
+                $string = smarty_mb_str_replace($search, $replace, $string, $c);
+                $count += $c;
+            }
+        } elseif (is_array($search)) {
+            if (!is_array($replace)) {
+                foreach ($search as &$string) {
+                    $subject = smarty_mb_str_replace($string, $replace, $subject, $c);
+                    $count += $c;
+                }
+            } else {
+                $n = max(count($search), count($replace));
+                while ($n --) {
+                    $subject = smarty_mb_str_replace(current($search), current($replace), $subject, $c);
+                    $count += $c;
+                    next($search);
+                    next($replace);
+                }
+            }
+        } else {
+            $parts = mb_split(preg_quote($search), $subject);
+            $count = count($parts) - 1;
+            $subject = implode($replace, $parts);
+        }
+
+        return $subject;
+    }
+}

+ 54 - 0
Mall/Framework/Smarty/libs/plugins/shared.mb_unicode.php

@@ -0,0 +1,54 @@
+<?php
+/**
+ * Smarty shared plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsShared
+ */
+
+/**
+ * convert characters to their decimal unicode equivalents
+ *
+ * @link   http://www.ibm.com/developerworks/library/os-php-unicode/index.html#listing3 for inspiration
+ *
+ * @param string $string   characters to calculate unicode of
+ * @param string $encoding encoding of $string, if null mb_internal_encoding() is used
+ *
+ * @return array sequence of unicodes
+ * @author Rodney Rehm
+ */
+function smarty_mb_to_unicode($string, $encoding = null)
+{
+    if ($encoding) {
+        $expanded = mb_convert_encoding($string, "UTF-32BE", $encoding);
+    } else {
+        $expanded = mb_convert_encoding($string, "UTF-32BE");
+    }
+
+    return unpack("N*", $expanded);
+}
+
+/**
+ * convert unicodes to the character of given encoding
+ *
+ * @link   http://www.ibm.com/developerworks/library/os-php-unicode/index.html#listing3 for inspiration
+ *
+ * @param integer|array $unicode  single unicode or list of unicodes to convert
+ * @param string        $encoding encoding of returned string, if null mb_internal_encoding() is used
+ *
+ * @return string unicode as character sequence in given $encoding
+ * @author Rodney Rehm
+ */
+function smarty_mb_from_unicode($unicode, $encoding = null)
+{
+    $t = '';
+    if (!$encoding) {
+        $encoding = mb_internal_encoding();
+    }
+    foreach ((array) $unicode as $utf32be) {
+        $character = pack("N*", $utf32be);
+        $t .= mb_convert_encoding($character, $encoding, "UTF-32BE");
+    }
+
+    return $t;
+}

+ 75 - 0
Mall/Framework/Smarty/libs/plugins/shared.mb_wordwrap.php

@@ -0,0 +1,75 @@
+<?php
+/**
+ * Smarty shared plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsShared
+ */
+
+if (!function_exists('smarty_mb_wordwrap')) {
+
+    /**
+     * Wrap a string to a given number of characters
+     *
+     * @link   http://php.net/manual/en/function.wordwrap.php for similarity
+     *
+     * @param  string  $str   the string to wrap
+     * @param  int     $width the width of the output
+     * @param  string  $break the character used to break the line
+     * @param  boolean $cut   ignored parameter, just for the sake of
+     *
+     * @return string  wrapped string
+     * @author Rodney Rehm
+     */
+    function smarty_mb_wordwrap($str, $width = 75, $break = "\n", $cut = false)
+    {
+        // break words into tokens using white space as a delimiter
+        $tokens =
+            preg_split('!(\s)!S' . Smarty::$_UTF8_MODIFIER, $str, - 1, PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE);
+        $length = 0;
+        $t = '';
+        $_previous = false;
+        $_space = false;
+
+        foreach ($tokens as $_token) {
+            $token_length = mb_strlen($_token, Smarty::$_CHARSET);
+            $_tokens = array($_token);
+            if ($token_length > $width) {
+                if ($cut) {
+                    $_tokens = preg_split('!(.{' . $width . '})!S' . Smarty::$_UTF8_MODIFIER, $_token, - 1,
+                                          PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE);
+                }
+            }
+
+            foreach ($_tokens as $token) {
+                $_space = !!preg_match('!^\s$!S' . Smarty::$_UTF8_MODIFIER, $token);
+                $token_length = mb_strlen($token, Smarty::$_CHARSET);
+                $length += $token_length;
+
+                if ($length > $width) {
+                    // remove space before inserted break
+                    if ($_previous) {
+                        $t = mb_substr($t, 0, - 1, Smarty::$_CHARSET);
+                    }
+
+                    if (!$_space) {
+                        // add the break before the token
+                        if (!empty($t)) {
+                            $t .= $break;
+                        }
+                        $length = $token_length;
+                    }
+                } elseif ($token == "\n") {
+                    // hard break must reset counters
+                    $_previous = 0;
+                    $length = 0;
+                }
+                $_previous = $_space;
+                // add the token
+                $t .= $token;
+            }
+        }
+
+        return $t;
+    }
+}

+ 19 - 0
Mall/Framework/Smarty/libs/plugins/variablefilter.htmlspecialchars.php

@@ -0,0 +1,19 @@
+<?php
+/**
+ * Smarty plugin
+ *
+ * @package    Smarty
+ * @subpackage PluginsFilter
+ */
+
+/**
+ * Smarty htmlspecialchars variablefilter plugin
+ *
+ * @param string $source input string
+ *
+ * @return string filtered output
+ */
+function smarty_variablefilter_htmlspecialchars($source)
+{
+    return htmlspecialchars($source, ENT_QUOTES, Smarty::$_CHARSET);
+}

Some files were not shown because too many files changed in this diff