Browse Source

Memcache, refactoring, View helpers, #16

git-svn-id: svn+ssh://code.netmonsters.ru/svn/majestic/branches/evo@124 4cb57b5f-5bbd-dd11-951b-001d605cbbc5
master
pzinovkin 15 years ago
parent
commit
8fc917dca2
  1. 39
      app/ErrorAction.php
  2. 10
      app/FrontController.php
  3. 66
      app/PagerAction.php
  4. 72
      cache/CacheKey.php
  5. 48
      cache/CacheKeySet.php
  6. 54
      cache/MemcacheCache.php
  7. 24
      cache/iCacheable.php
  8. 78
      classes/Pager.class.php
  9. 4
      exception/ErrorHandler.php
  10. 3
      model/DbDriver.php
  11. 50
      model/DbStatement.php
  12. 135
      model/Model.php
  13. 23
      model/MySQLiDriver.php
  14. 16
      model/MySQLiStatement.php
  15. 82
      util/Profiler.php
  16. 39
      util/QueryProfiler.php
  17. 29
      view/PHPView.php
  18. 24
      view/helpers/ViewHelper.php
  19. 57
      view/helpers/ViewHelperGet.php

39
app/ErrorAction.php

@ -13,7 +13,7 @@ class ErrorAction extends ViewAction
{
/**
* @var Exception
* @var ErrorException
*/
protected $exception;
@ -36,12 +36,12 @@ class ErrorAction extends ViewAction
return '/static/' . $this->template;
}
protected function sendHttpCode($code)
protected function sendHttpCode()
{
if (headers_sent()) {
return;
}
switch ($code) {
switch ($this->template) {
case 404:
header('HTTP/1.0 404 Not Found');
break;
@ -50,9 +50,40 @@ class ErrorAction extends ViewAction
}
}
protected function logError()
{
if ($this->template = 500) {
$error = 0;
$ex = $this->exception;
if ($ex instanceof ErrorException) {
$error = $ex->getSeverity();
}
switch ($error) {
case E_NOTICE:
$error = 'Notice';
break;
case E_WARNING:
$error = 'Warning';
break;
case E_ERROR:
$error = 'Fatal Error';
break;
default:
$error = 'Unknown Error';
break;
}
$message = 'PHP ' . $error . ': ' . $ex->getMessage() . ' in ' . $ex->getFile()
. ' on line ' . $ex->getLine();
error_log($message);
}
}
public function fetch()
{
$this->sendHTTPCode($this->template);
$this->logError();
$this->sendHTTPCode();
return $this->view->fetch($this->getTemplate());
}
}

10
app/FrontController.php

@ -32,6 +32,9 @@ class FrontController
private function __construct()
{
ErrorHandler::init();
if (DEBUG == true) {
Profiler::getInstance()->start();
}
$this->router = new Router();
}
@ -96,7 +99,6 @@ class FrontController
public function execute()
{
try {
$request = Env::getRequestUri();
$route = $this->getRouter()->route($request);
if (!$route) {
@ -107,14 +109,16 @@ class FrontController
if (!class_exists($action_class)) {
throw new GeneralException('Action class "' . $action_class . '" not found.');
}
$action = new $action_class();
$layout_class = $route->getLayout();
if (!class_exists($layout_class)) {
throw new GeneralException('Layout class "' . $layout_class . '" not found.');
}
$layout = new $layout_class();
return $layout->fetch($action);
$layout = new $layout_class();
$html = $layout->fetch($action);
return (!DEBUG) ? $html : Profiler::getInstance()->end($html);
} catch(Exception $e) {
if (DEBUG == true) {
return ErrorHandler::showDebug($e);

66
app/PagerAction.php

@ -0,0 +1,66 @@
<?php
/**
* @copyright NetMonsters <team@netmonsters.ru>
* @link http://netmonsters.ru
* @package Majestic
* @subpackage app
* @since 2010-03-07
* @version SVN: $Id$
* @filesource $URL$
*/
class PagerAction extends ViewAction
{
public $page;
public $last_page;
//protected $num_rows;
protected $offset = 0;
protected $count = 0;
protected $limit;
public function __construct($count, $limit = 20)
{
$this->count = $count;
$this->limit = $limit;
parent::__construct();
}
protected function execute()
{
$page = (int) Env::Get('p');
$this->last_page = ceil($this->count/$this->limit);
$this->page = ($page <= $this->last_page && $page > 0) ? $page : 1;
$this->offset = $this->limit * ($this->page - 1);
//$this->num_rows = ($this->limit + $this->offset) <= $this->count ? ($this->limit + $this->offset) : $this->count;
}
public function getOffset()
{
return $this->offset;
}
public function getLimit()
{
return $this->limit;
}
protected function getTemplate()
{
$template = ($this->template) ? $this->template : substr(get_class($this), 0, -6/*strlen('Action')*/);
return '/actions/' . $template;
}
/* public function setNumRows($num_rows)
{
$this->num_rows = $num_rows;
}
*/
/*function prepare()
{
$this->templater->assign('page', $this->page);
$this->templater->assign('page_max', $this->max_page_num);
}*/
}

72
cache/CacheKey.php

@ -0,0 +1,72 @@
<?php
/**
* @copyright NetMonsters <team@netmonsters.ru>
* @link http://netmonsters.ru
* @package Majestic
* @subpackage Cache
* @since 2010-03-10
* @version SVN: $Id$
* @filesource $URL$
*/
class CacheKey
{
protected $key;
protected $params = '';
protected $expire = 0;
/**
* @param string $key
* @param mixed $params
* @param iCacheable $cacheable
* @return CacheKey
*/
public function __construct($key, $params = array(), $cacheable)
{
$this->key = $key;
if (!$cacheable instanceof iCacheable) {
throw new GeneralException('CacheKey depends on iCacheable instance');
}
$this->cache = $cacheable->getCache();
$this->expire = $cacheable->getKeyExpire($this->key);
$this->params = (is_array($params)) ? implode('', $params) : $params;
}
protected function getCacheKey()
{
$params = ($this->params) ? ('_' . $this->params) : '';
return $this->key . $params;
}
protected function getExpire()
{
return $this->expire;
}
/**
* @param int $expire
*/
public function setExpire($expire)
{
$this->expire = $expire;
}
public function get()
{
return $this->cache->get($this->getCacheKey());
}
/**
* @param mixed $value
*/
public function set($value)
{
return $this->cache->set($this->getCacheKey(), $value, $this->expire);
}
public function del()
{
return $this->cache->del($this->getCacheKey());
}
}

48
cache/CacheKeySet.php

@ -0,0 +1,48 @@
<?php
/**
* @copyright NetMonsters <team@netmonsters.ru>
* @link http://netmonsters.ru
* @package Majestic
* @subpackage Cache
* @since 2010-03-10
* @version SVN: $Id$
* @filesource $URL$
*/
class CacheKeySet extends CacheKey
{
public function get()
{
$set = $this->cache->get($this->key);
$item_key = $this->getCacheKey();
if (!is_array($set) || !array_key_exists($item_key, $set)) {
return false;
}
return $this->cache->get($item_key);
}
/**
* @param mixed $value
*/
public function set($value)
{
$set = $this->cache->get($this->key);
if (!is_array($set)) {
$set = array();
}
$item_key = $this->getCacheKey();
if (!$this->cache->set($item_key, $value, $this->expire)) {
return false;
}
$set[$item_key] = $this->cache->getExpire($this->expire);
return $this->cache->set($this->key, $set, $this->expire);
}
public function del()
{
return $this->cache->del($this->key);
}
}

54
cache/MemcacheCache.php

@ -17,10 +17,26 @@ class MemcacheCache extends Cache
*/
protected $connection = null;
protected $key_salt = null;
/**
* One hour to live default
*
* @var int
*/
protected $expire = 3600;
protected $keys = array();
public function __construct($config)
{
$this->connection = new Memcache();
if (isset($config['key_salt'])) {
$this->key_salt = $config['key_salt'];
unset($config['key_salt']);
}
$required = array('hostname', 'port');
foreach ($config as $c) {
foreach ($required as $option) {
@ -42,7 +58,7 @@ class MemcacheCache extends Cache
*/
public function add($key, $value, $expire = 0)
{
return $this->connection->add($key, $value, null, $expire);
return $this->connection->add($this->getKey($key), $value, null, $this->getExpire($expire));
}
/**
@ -54,7 +70,7 @@ class MemcacheCache extends Cache
*/
public function decrement($key, $decrement = 1)
{
return $this->connection->decrement($key, $decrement);
return $this->connection->decrement($this->getKey($key), $decrement);
}
/**
@ -66,7 +82,7 @@ class MemcacheCache extends Cache
*/
public function del($key)
{
return $this->connection->delete($key);
return $this->connection->delete($this->getKey($key));
}
/**
@ -82,12 +98,12 @@ class MemcacheCache extends Cache
/**
* Retrieve item from the cache
*
* @param mixed $key
* @param string $key
* @return mixed
*/
public function get($key)
{
return $this->connection->get($key);
return $this->connection->get($this->getKey($key));
}
/**
@ -99,7 +115,7 @@ class MemcacheCache extends Cache
*/
public function increment($key, $increment = 1)
{
return $this->connection->increment($key, $increment);
return $this->connection->increment($this->getKey($key), $increment);
}
/**
@ -112,7 +128,7 @@ class MemcacheCache extends Cache
*/
public function replace($key, $value, $expire = 0)
{
return $this->connection->replace($key, $value, null, $expire);
return $this->connection->replace($this->getKey($key), $value, null, $this->getExpire($expire));
}
/**
@ -125,6 +141,28 @@ class MemcacheCache extends Cache
*/
public function set($key, $value, $expire = 0)
{
return $this->connection->set($key, $value, null, $expire);
return $this->connection->set($this->getKey($key), $value, null, $this->getExpire($expire));
}
/**
* @param string $key
* @return string
*/
protected function getKey($key)
{
if (!isset($this->keys[$key])) {
$this->keys[$key] = md5($this->key_salt . $key);
}
return $this->keys[$key];
}
public function getExpire($expire)
{
return ($expire > 0) ? $expire : $this->expire;
}
public function cleanKeys()
{
$this->keys = array();
}
}

24
cache/iCacheable.php

@ -0,0 +1,24 @@
<?php
/**
* @copyright NetMonsters <team@netmonsters.ru>
* @link http://netmonsters.ru
* @package Majestic
* @subpackage cache
* @since 2010-03-12
* @version SVN: $Id$
* @filesource $URL$
*/
interface iCacheable
{
/**
* @return Cache
*/
public function getCache();
/**
* @param string $key
* @return Expiration time in seconds
*/
public function getKeyExpire($key);
}

78
classes/Pager.class.php

@ -1,78 +0,0 @@
<?php
/**
* PagerAction
*
* @copyright NetMonsters <team@netmonsters.ru>
* @link
* @package Nakon
* @subpackage face
* @since 04.02.2008
* @version SVN$
* @filesource $URL$
*/
class Pager extends Action
{
public $template_dir = '.';
protected $current_page = 1;
protected $max_page_num;
protected $num_rows;
protected $limit;
protected $start_offset = 0;
protected $count = 0;
public function __construct($count, $current_page = 1, $records_limit = 20)
{
parent::__construct();
$this->count = $count;
$this->limit = $records_limit;
$this->max_page_num = ceil($this->count/$this->limit);
$this->current_page = ((int)$current_page <= $this->max_page_num && (int)$current_page > 0) ? (int)$current_page : 1;
$this->start_offset = $this->limit * ($this->current_page - 1);
$this->num_rows = ($this->limit + $this->start_offset) <= $this->count ? ($this->limit + $this->start_offset) : $this->count;
}
/**
* Возвращает текущую страницу
*
* @return int
*/
public function getPage()
{
return $this->current_page;
}
public function startOffset()
{
return $this->start_offset;
}
public function limit()
{
return $this->limit;
}
public function setNumRows($num_rows)
{
$this->num_rows = $num_rows;
}
function init()
{
$this->template = "Pager";
}
function prepare()
{
$this->templater->assign('page', $this->current_page);
$this->templater->assign('page_max', $this->max_page_num);
$this->templater->assign('limit', $this->limit);
$this->templater->assign('count', $this->count);
}
}
?>

4
exception/ErrorHandler.php

@ -51,6 +51,9 @@ class ErrorHandler
}
$text = '<table class="req"><thead><tr><th>Variable</th><th>Value</th></tr></thead><tbody>';
foreach ($array as $key => $value) {
if (is_array($value)) {
$value = print_r($value, true);
}
$value = ($value) ? htmlentities($value, ENT_QUOTES, 'UTF-8') : '&nbsp;';
$text .= '<tr><td>' . $key . '</td><td class="code"><div>' . $value . '</div></td></tr>';
}
@ -68,6 +71,7 @@ class ErrorHandler
*/
static public function showDebug($exception)
{
ob_clean();
$class = get_class($exception);
$method = Env::Server('REQUEST_METHOD', '');

3
model/DbDriver.php

@ -91,9 +91,10 @@ abstract class DbDriver
/**
* @param string $table
* @param mixed $bind
* @param mixed $on_duplicate
* @return int Affected rows count
*/
public function insert($table, $bind)
public function insert($table, $bind, $on_duplicate = array())
{
$columns = array();
foreach ($bind as $col => $val) {

50
model/DbStatement.php

@ -34,31 +34,6 @@ abstract class DbStatement
$this->sql = $sql;
}
/**
* @param mixed $style
* @return array
*/
public function fetchAll($style = Db::FETCH_OBJ)
{
$data = array();
while ($row = $this->fetch($style)) {
$data[] = $row;
}
return $data;
}
/**
* @param string $field
*/
public function fetchField($field)
{
$row = $this->fetch(Db::FETCH_ASSOC);
if (isset($row[$field])) {
return $row[$field];
}
return false;
}
public function bindParam($param, &$value)
{
if ($this->map === null) {
@ -138,6 +113,31 @@ abstract class DbStatement
{
$this->close();
}
/**
* @param mixed $style
* @return array
*/
public function fetchAll($style = Db::FETCH_OBJ)
{
$data = array();
while ($row = $this->fetch($style)) {
$data[] = $row;
}
return $data;
}
/**
* @param string $field
*/
public function fetchField($field)
{
$row = $this->fetch(Db::FETCH_ASSOC);
if (isset($row[$field])) {
return $row[$field];
}
return false;
}
/* Abstract methods */

135
model/Model.php

@ -12,7 +12,7 @@
* @filesource $URL$
*/
abstract class Model
abstract class Model implements iCacheable
{
/**
@ -28,6 +28,20 @@ abstract class Model
* @var Cache
*/
protected $cache;
/**
* Custom expiration time for keys
*
* @var mixed
*/
protected $cache_keys = array();
/**
* Caches to clean.
*
* @var mixed
*/
protected $caches_clean = array();
protected $table;
@ -35,7 +49,6 @@ abstract class Model
protected $key = 'id';
public function __construct()
{
$this->db = Db::connect($this->connection);
@ -73,37 +86,21 @@ abstract class Model
*/
public function get($id)
{
$sql = 'SELECT * FROM ' . $this->table() . ' WHERE ' . $this->identify($this->key) . '=' . (int) $id;
return $this->db->query($sql)->fetch();
}
/**
* @param array $data
* @return int Affect row or id of inserted field.
*/
public function save($data)
{
$key = isset($data[$this->key]) ? $data[$this->key] : false;
$result = false;
if ($key) {
unset($data[$this->key]);
$result = $this->update($data, $this->identify($this->key) . '=' . (int) $id);
} else {
$result = $this->insert($data);
}
return $result;
$sql = 'SELECT * FROM ' . $this->table() . ' WHERE ' . $this->identify($this->key) . '=?';
return $this->fetch($sql, $id);
}
/**
* @param array $data
* @param array $on_duplicate
* @return int Id of inserted row
*/
public function insert($data)
public function insert($data, $on_duplicate = array())
{
if (!$this->db->insert($this->table(false), $data)) {
if (!$res = $this->db->insert($this->table(false), $data, $on_duplicate)) {
return false;
}
return $this->getInsertId();
return ($on_duplicate) ? $res : $this->getInsertId();
}
/**
@ -146,11 +143,99 @@ abstract class Model
/**
* @return Cache
*/
protected function cache()
public function getCache()
{
if (!$this->cache) {
$this->cache = Cacher::get(Config::get(__CLASS__, 'MemcacheCache'));
}
return $this->cache;
}
/**
* @param string $key
* @return int
*/
public function getKeyExpire($key)
{
return (isset($this->cache_keys[$key])) ? ($this->cache_keys[$key] * 60) : 0;
}
/**
* @param string $name
* @param array $params
* @return CacheKey
*/
protected function cacheKey($name, $params = array())
{
if (substr(strtolower($name), -3, 3) == 'set') {
return new CacheKeySet($name, $params, $this);
}
return new CacheKey($name, $params, $this);
}
/**
* @param CacheKey | CacheKeySet $cache
*/
protected function addCleanCache($cache)
{
$this->caches_clean[] = $cache;
}
protected function cleanCaches()
{
// cleaning caches
foreach ($this->caches_clean as $cache) {
$cache->del();
}
$this->caches_clean = array();
}
/**
* @param string $sql
* @param array $params
* @param string $field
* @param CacheKey | CacheKeySet $cache_key
*/
protected function fetchField($sql, $params = array(), $field, $cache_key = null)
{
if (!$cache_key || !$result = $cache_key->get()) {
$result = $this->db->query($sql, $params)->fetchField($field);
if ($cache_key) {
$cache_key->set($result);
}
}
return $result;
}
/**
* @param string $sql
* @param array $params
* @param CacheKey | CacheKeySet $cache_key
*/
protected function fetch($sql, $params = array(), $cache_key = null)
{
if (!$cache_key || !$result = $cache_key->get()) {
$result = $this->db->query($sql, $params)->fetch();
if ($cache_key) {
$cache_key->set($result);
}
}
return $result;
}
/**
* @param string $sql
* @param array $params
* @param CacheKey | CacheKeySet $cache_key
*/
protected function fetchAll($sql, $params = array(), $cache_key = null)
{
if (!$cache_key || !$result = $cache_key->get()) {
$result = $this->db->query($sql, $params)->fetchAll();
if ($cache_key) {
$cache_key->set($result);
}
}
return $result;
}
}

23
model/MySQLiDriver.php

@ -15,6 +15,28 @@
class MySQLiDriver extends DbDriver
{
public function insert($table, $bind, $on_duplicate = array())
{
$columns = array();
foreach ($bind as $col => $val) {
$columns[] = $this->quoteIdentifier($col);
}
$values = array_values($bind);
if ($on_duplicate) {
$update = array();
foreach ($on_duplicate as $col => $val) {
$update[] = $this->quoteIdentifier($col) . '=' . $this->quote($val);
}
$on_duplicate = ' ON DUPLICATE KEY UPDATE ' . implode(', ', $update);
}
$sql = 'INSERT INTO ' . $this->quoteIdentifier($table)
. ' (' . implode(', ', $columns) . ') VALUES (' . $this->quote($values) . ')'
. (($on_duplicate) ? $on_duplicate : '');
return $this->query($sql)->affectedRows();
}
/**
* @param mixed $sql
* @return DbStatement
@ -72,7 +94,6 @@ class MySQLiDriver extends DbDriver
}
if (is_bool($value)) {
var_dump($value);
return (int) $value;
}

16
model/MySQLiStatement.php

@ -16,9 +16,15 @@
class MySQLiStatement extends DbStatement
{
/**
* Fetches single row
*
* @param mixed $style
* @return mixed
*/
public function fetch($style = Db::FETCH_OBJ)
{
if (! $this->result) {
if (!$this->result) {
return false;
}
@ -77,7 +83,13 @@ class MySQLiStatement extends DbStatement
* @var MySQLi
*/
$mysqli = $this->driver->getConnection();
$result = $mysqli->query($sql);
if (DEBUG) {
$profiler = Profiler::getInstance()->profilerQuery($sql);
$result = $mysqli->query($sql);
$profiler->end();
} else {
$result = $mysqli->query($sql);
}
if ($result === false) {
throw new Exception($mysqli->error, $mysqli->errno);
}

82
util/Profiler.php

@ -0,0 +1,82 @@
<?php
/**
* @copyright NetMonsters <team@netmonsters.ru>
* @link http://netmonsters.ru
* @package Majestic
* @subpackage util
* @since 2010-03-09
* @version SVN: $Id$
* @filesource $URL$
*/
class Profiler
{
protected $start = null;
protected $end = null;
protected $queries = array();
static protected $instance = null;
private function __construct()
{
if (DEBUG == false) {
throw new GeneralException('Need to turn on DEBUG before use.');
}
}
/**
* Refuse cloning
*/
private function __clone(){}
/**
* @return Profiler
*/
static public function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* @param string $query
* @return QueryProfiler
*/
public function profilerQuery($query)
{
$profiler = new QueryProfiler($query);
$this->queries[] = $profiler;
return $profiler;
}
public function start()
{
$this->start = microtime(true);
}
public function end($html)
{
$this->end = microtime(true);
if (stripos($html, '</body>') == False) {
return $html;
}
return str_ireplace('</body>', $this->getOutput() . '</body>', $html);
}
protected function getOutput()
{
$html = '<div style="clear:both; font:12px monospace; margin: 5px;">'
. 'Elapsed time: ' . round(($this->end- $this->start) * 1000) . 'ms.<br/>'
. 'Queries: ' . count($this->queries) . '<br/>';
foreach ($this->queries as $query) {
$html .= '[' . round($query->getElapsed() * 1000) . 'ms] ' . $query->getQuery() . '<br/>';
}
$html .= '</div>';
return $html;
}
}

39
util/QueryProfiler.php

@ -0,0 +1,39 @@
<?php
/**
* @copyright NetMonsters <team@netmonsters.ru>
* @link http://netmonsters.ru
* @package Majestic
* @subpackage util
* @since 2010-03-09
* @version SVN: $Id$
* @filesource $URL$
*/
class QueryProfiler
{
protected $query = '';
protected $start = null;
protected $end = null;
public function __construct($query)
{
$this->query = $query;
$this->start = microtime(true);
}
public function end()
{
$this->end = microtime(true);
}
public function getQuery()
{
return $this->query;
}
public function getElapsed()
{
return $this->end - $this->start;
}
}

29
view/PHPView.php

@ -9,11 +9,15 @@
* @filesource $URL$
*/
/**
* @method ViewHelperGet get()
*/
class PHPView implements iView
{
protected $path = '';
protected $variables = array();
protected $helpers = array();
protected $extension = '.phtml';
protected $template = '';
@ -69,6 +73,31 @@ class PHPView implements iView
return htmlentities($var, ENT_QUOTES, 'UTF-8');
}
/**
* Helpers call
*
* @param mixed $name
* @param mixed $args
* @return ViewHelper
*/
public function __call($name, $args)
{
$helper = $this->getHelper($name);
return call_user_func_array(array($helper, $name), $args);
}
protected function getHelper($name)
{
if (!isset($this->helpers[$name])) {
$class = 'ViewHelper' . ucfirst($name);
if (!class_exists($class)) {
throw new GeneralException('View helper "' . $class . '" not found.');
}
$this->helpers[$name] = new $class($this);
}
return $this->helpers[$name];
}
protected function getTemplatePath($template)
{
return $this->path . $template . $this->extension;

24
view/helpers/ViewHelper.php

@ -0,0 +1,24 @@
<?php
/**
* @copyright NetMonsters <team@netmonsters.ru>
* @link http://netmonsters.ru
* @package Majestic
* @subpackage View
* @since 2010-03-09
* @version SVN: $Id$
* @filesource $URL$
*/
abstract class ViewHelper
{
/**
* @var PHPView
*/
protected $view = null;
public function __construct($view)
{
$this->view = $view;
}
}

57
view/helpers/ViewHelperGet.php

@ -0,0 +1,57 @@
<?php
/**
* @copyright NetMonsters <team@netmonsters.ru>
* @link http://netmonsters.ru
* @package Majestic
* @subpackage View
* @since 2010-03-09
* @version SVN: $Id$
* @filesource $URL$
*/
class ViewHelperGet extends ViewHelper
{
protected $get;
public function get($replace)
{
$get = $this->getSanitizedRequest();
if (!is_array($replace)) {
$replace = array($replace);
}
foreach ($replace as $key => $value) {
if (is_int($key)) {
unset($get[$value]);
} else {
$get[$key] = $this->impl($key, $value);
}
}
return '?' . $this->view->escape(implode('&', $get));
}
protected function getSanitizedRequest()
{
if ($this->get === null) {
$get = Env::Get();
foreach ($get as $key => $value) {
$this->get[$key] = $this->impl($key, $value);
}
}
return $this->get;
}
protected function impl($name, $value)
{
if (is_array($value)){
$result = array();
foreach ($value as $key => $val) {
$result[] = $name . '[' . $key . ']=' . $val;
}
$result = implode('&', $result);
} else {
$result = $name . '=' . $value;
}
return $result;
}
}
Loading…
Cancel
Save