@@ -40,7 +40,7 @@ if (!is_array($row) || $cfg_digg_update == 0) { | |||||
} | } | ||||
DelCache($prefix, $key); | DelCache($prefix, $key); | ||||
} | } | ||||
SetCache($prefix, $key, $row, 0); | |||||
SetCache($prefix, $key, $row); | |||||
} else { | } else { | ||||
if ($action == 'good') { | if ($action == 'good') { | ||||
$row['goodpost'] = $row['goodpost'] + 1; | $row['goodpost'] = $row['goodpost'] + 1; | ||||
@@ -59,7 +59,7 @@ if (!is_array($row) || $cfg_digg_update == 0) { | |||||
DelCache($prefix, $key); | DelCache($prefix, $key); | ||||
} | } | ||||
} | } | ||||
SetCache($prefix, $key, $row, 0); | |||||
SetCache($prefix, $key, $row); | |||||
} | } | ||||
$digg = ''; | $digg = ''; | ||||
if (!is_array($row)) exit(); | if (!is_array($row)) exit(); | ||||
@@ -0,0 +1,278 @@ | |||||
<?php | |||||
if (!defined('DEDEINC')) exit('dedebiz'); | |||||
class Condition | |||||
{ | |||||
/** | |||||
* Simple equals | |||||
* | |||||
* @param mixed $value | |||||
* @param mixed $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function equal($value, $comparable) | |||||
{ | |||||
return $value == $comparable; | |||||
} | |||||
/** | |||||
* Strict equals | |||||
* | |||||
* @param mixed $value | |||||
* @param mixed $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function strictEqual($value, $comparable) | |||||
{ | |||||
return $value === $comparable; | |||||
} | |||||
/** | |||||
* Simple not equal | |||||
* | |||||
* @param mixed $value | |||||
* @param mixed $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function notEqual($value, $comparable) | |||||
{ | |||||
return $value != $comparable; | |||||
} | |||||
/** | |||||
* Strict not equal | |||||
* | |||||
* @param mixed $value | |||||
* @param mixed $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function strictNotEqual($value, $comparable) | |||||
{ | |||||
return $value !== $comparable; | |||||
} | |||||
/** | |||||
* Strict greater than | |||||
* | |||||
* @param mixed $value | |||||
* @param mixed $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function greaterThan($value, $comparable) | |||||
{ | |||||
return $value > $comparable; | |||||
} | |||||
/** | |||||
* Strict less than | |||||
* | |||||
* @param mixed $value | |||||
* @param mixed $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function lessThan($value, $comparable) | |||||
{ | |||||
return $value < $comparable; | |||||
} | |||||
/** | |||||
* Greater or equal | |||||
* | |||||
* @param mixed $value | |||||
* @param mixed $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function greaterThanOrEqual($value, $comparable) | |||||
{ | |||||
return $value >= $comparable; | |||||
} | |||||
/** | |||||
* Less or equal | |||||
* | |||||
* @param mixed $value | |||||
* @param mixed $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function lessThanOrEqual($value, $comparable) | |||||
{ | |||||
return $value <= $comparable; | |||||
} | |||||
/** | |||||
* In array | |||||
* | |||||
* @param mixed $value | |||||
* @param array $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function in($value, $comparable) | |||||
{ | |||||
return (is_array($comparable) && in_array($value, $comparable)); | |||||
} | |||||
/** | |||||
* Not in array | |||||
* | |||||
* @param mixed $value | |||||
* @param array $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function notIn($value, $comparable) | |||||
{ | |||||
return (is_array($comparable) && !in_array($value, $comparable)); | |||||
} | |||||
/** | |||||
* Is null equal | |||||
* | |||||
* @param mixed $value | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function isNull($value, $comparable) | |||||
{ | |||||
return is_null($value); | |||||
} | |||||
/** | |||||
* Is not null equal | |||||
* | |||||
* @param mixed $value | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function isNotNull($value, $comparable) | |||||
{ | |||||
return !is_null($value); | |||||
} | |||||
/** | |||||
* Start With | |||||
* | |||||
* @param mixed $value | |||||
* @param string $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function startWith($value, $comparable) | |||||
{ | |||||
if (is_array($comparable) || is_array($value) || is_object($comparable) || is_object($value)) { | |||||
return false; | |||||
} | |||||
if (preg_match("/^$comparable/", $value)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* End with | |||||
* | |||||
* @param mixed $value | |||||
* @param string $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function endWith($value, $comparable) | |||||
{ | |||||
if (is_array($comparable) || is_array($value) || is_object($comparable) || is_object($value)) { | |||||
return false; | |||||
} | |||||
if (preg_match("/$comparable$/", $value)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* Match with pattern | |||||
* | |||||
* @param mixed $value | |||||
* @param string $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function match($value, $comparable) | |||||
{ | |||||
if (is_array($comparable) || is_array($value) || is_object($comparable) || is_object($value)) { | |||||
return false; | |||||
} | |||||
$comparable = trim($comparable); | |||||
if (preg_match("/^$comparable$/", $value)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* Contains substring in string | |||||
* | |||||
* @param string $value | |||||
* @param string $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function contains($value, $comparable) | |||||
{ | |||||
return (strpos($value, $comparable) !== false); | |||||
} | |||||
/** | |||||
* Dates equal | |||||
* | |||||
* @param string $value | |||||
* @param string $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function dateEqual($value, $comparable, $format = 'Y-m-d') | |||||
{ | |||||
$date = date($format, strtotime($value)); | |||||
return $date == $comparable; | |||||
} | |||||
/** | |||||
* Months equal | |||||
* | |||||
* @param string $value | |||||
* @param string $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function monthEqual($value, $comparable) | |||||
{ | |||||
$month = date('m', strtotime($value)); | |||||
return $month == $comparable; | |||||
} | |||||
/** | |||||
* Years equal | |||||
* | |||||
* @param string $value | |||||
* @param string $comparable | |||||
* | |||||
* @return bool | |||||
*/ | |||||
public static function yearEqual($value, $comparable) | |||||
{ | |||||
$year = date('Y', strtotime($value)); | |||||
return $year == $comparable; | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
<?php | |||||
if (!defined('DEDEINC')) exit('dedebiz'); | |||||
class ConditionNotAllowedException extends \Exception | |||||
{ | |||||
public function __construct($message = "Condition not allowed exception", $code = 0, \Throwable $previous = null) | |||||
{ | |||||
parent::__construct($message, $code, $previous); | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
<?php | |||||
if (!defined('DEDEINC')) exit('dedebiz'); | |||||
class FileNotFoundException extends \Exception | |||||
{ | |||||
public function __construct($message = "File not found exception", $code = 0, \Throwable $previous = null) | |||||
{ | |||||
parent::__construct($message, $code, $previous); | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
<?php | |||||
if (!defined('DEDEINC')) exit('dedebiz'); | |||||
class InvalidJsonException extends \Exception | |||||
{ | |||||
public function __construct($message = "Invalid JSON format", $code = 0, \Throwable $previous = null) | |||||
{ | |||||
parent::__construct($message, $code, $previous); | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
<?php | |||||
if (!defined('DEDEINC')) exit('dedebiz'); | |||||
class InvalidNodeException extends \Exception | |||||
{ | |||||
public function __construct($message = "Invalid JSON node exception", $code = 0, \Throwable $previous = null) | |||||
{ | |||||
parent::__construct($message, $code, $previous); | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
<?php | |||||
if (!defined('DEDEINC')) exit('dedebiz'); | |||||
class NullValueException extends \Exception | |||||
{ | |||||
public function __construct($message = "Null value exception", $code = 0, \Throwable $previous = null) | |||||
{ | |||||
parent::__construct($message, $code, $previous); | |||||
} | |||||
} |
@@ -0,0 +1,644 @@ | |||||
<?php | |||||
if (!defined('DEDEINC')) exit('dedebiz'); | |||||
require_once(dirname(__FILE__)."/Exceptions/ConditionNotAllowedException.php"); | |||||
require_once(dirname(__FILE__)."/Exceptions/FileNotFoundException.php"); | |||||
require_once(dirname(__FILE__)."/Exceptions/InvalidJsonException.php"); | |||||
require_once(dirname(__FILE__)."/Results/ValueNotFound.php"); | |||||
require_once(dirname(__FILE__)."/Condition.php"); | |||||
trait JsonQueriable | |||||
{ | |||||
/** | |||||
* store node path | |||||
* @var string|array | |||||
*/ | |||||
protected $_node = ''; | |||||
/** | |||||
* contain prepared data for process | |||||
* @var mixed | |||||
*/ | |||||
protected $_map; | |||||
/** | |||||
* contains column names | |||||
* @var array | |||||
*/ | |||||
protected $_select = []; | |||||
/** | |||||
* contains column names for except | |||||
* @var array | |||||
*/ | |||||
protected $_except = []; | |||||
/** | |||||
* Stores base contents. | |||||
* | |||||
* @var array | |||||
*/ | |||||
protected $_baseContents = []; | |||||
/** | |||||
* Stores all conditions. | |||||
* | |||||
* @var array | |||||
*/ | |||||
protected $_conditions = []; | |||||
/** | |||||
* @var bool | |||||
*/ | |||||
protected $_isProcessed = false; | |||||
/** | |||||
* map all conditions with methods | |||||
* @var array | |||||
*/ | |||||
protected static $_rulesMap = [ | |||||
'=' => 'equal', | |||||
'eq' => 'equal', | |||||
'==' => 'strictEqual', | |||||
'seq' => 'strictEqual', | |||||
'!=' => 'notEqual', | |||||
'neq' => 'notEqual', | |||||
'!==' => 'strictNotEqual', | |||||
'sneq' => 'strictNotEqual', | |||||
'>' => 'greaterThan', | |||||
'gt' => 'greaterThan', | |||||
'<' => 'lessThan', | |||||
'lt' => 'lessThan', | |||||
'>=' => 'greaterThanOrEqual', | |||||
'gte' => 'greaterThanOrEqual', | |||||
'<=' => 'lessThanOrEqual', | |||||
'lte' => 'lessThanOrEqual', | |||||
'in' => 'in', | |||||
'notin' => 'notIn', | |||||
'null' => 'isNull', | |||||
'notnull' => 'isNotNull', | |||||
'startswith' => 'startWith', | |||||
'endswith' => 'endWith', | |||||
'match' => 'match', | |||||
'contains' => 'contains', | |||||
'dates' => 'dateEqual', | |||||
'month' => 'monthEqual', | |||||
'year' => 'yearEqual', | |||||
]; | |||||
/** | |||||
* import data from file | |||||
* | |||||
* @param string|null $file | |||||
* @return bool | |||||
* @throws FileNotFoundException | |||||
* @throws InvalidJsonException | |||||
*/ | |||||
public function import($file = null) | |||||
{ | |||||
if (!is_null($file)) { | |||||
if (is_string($file)) { | |||||
$this->_map = $this->getDataFromFile($file); | |||||
$this->_baseContents = $this->_map; | |||||
return true; | |||||
} | |||||
} | |||||
throw new FileNotFoundException(); | |||||
} | |||||
/** | |||||
* Prepare data from desire conditions | |||||
* | |||||
* @return $this | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
protected function prepare() | |||||
{ | |||||
if ($this->_isProcessed) { | |||||
return $this; | |||||
} | |||||
if (count($this->_conditions) > 0) { | |||||
$calculatedData = $this->processConditions(); | |||||
$this->_map = $this->objectToArray($calculatedData); | |||||
$this->_conditions = []; | |||||
$this->_node = ''; | |||||
$this->_isProcessed = true; | |||||
return $this; | |||||
} | |||||
$this->_isProcessed = true; | |||||
$this->_map = $this->objectToArray($this->getData()); | |||||
return $this; | |||||
} | |||||
/** | |||||
* Our system will cache processed data and prevend multiple time processing. If | |||||
* you want to reprocess this method can help you | |||||
* | |||||
* @return $this | |||||
*/ | |||||
public function reProcess() | |||||
{ | |||||
$this->_isProcessed = false; | |||||
return $this; | |||||
} | |||||
/** | |||||
* Parse object to array | |||||
* | |||||
* @param object $obj | |||||
* @return array|mixed | |||||
*/ | |||||
protected function objectToArray($obj) | |||||
{ | |||||
if (!is_array($obj) && !is_object($obj)) { | |||||
return $obj; | |||||
} | |||||
if (is_array($obj)) { | |||||
return $obj; | |||||
} | |||||
if (is_object($obj)) { | |||||
$obj = get_object_vars($obj); | |||||
} | |||||
return array_map([$this, 'objectToArray'], $obj); | |||||
} | |||||
/** | |||||
* Check given value is multidimensional array | |||||
* | |||||
* @param array $arr | |||||
* @return bool | |||||
*/ | |||||
protected function isMultiArray($arr) | |||||
{ | |||||
if (!is_array($arr)) { | |||||
return false; | |||||
} | |||||
rsort($arr); | |||||
return isset($arr[0]) && is_array($arr[0]); | |||||
} | |||||
/** | |||||
* Check given value is valid JSON | |||||
* | |||||
* @param string $value | |||||
* @param bool $isReturnMap | |||||
* | |||||
* @return bool|array | |||||
*/ | |||||
public function isJson($value, $isReturnMap = false) | |||||
{ | |||||
if (is_array($value) || is_object($value)) { | |||||
return false; | |||||
} | |||||
$data = json_decode($value, true); | |||||
if (json_last_error() !== JSON_ERROR_NONE) { | |||||
return false; | |||||
} | |||||
return $isReturnMap ? $data : true; | |||||
} | |||||
public function takeColumn($array) | |||||
{ | |||||
return $this->selectColumn($this->exceptColumn($array)); | |||||
} | |||||
/** | |||||
* selecting specific column | |||||
* | |||||
* @param $array | |||||
* @return array | |||||
*/ | |||||
protected function selectColumn($array) | |||||
{ | |||||
$keys = $this->_select; | |||||
if (count($keys) == 0) { | |||||
return $array; | |||||
} | |||||
return array_intersect_key($array, array_flip((array) $keys)); | |||||
} | |||||
/** | |||||
* selecting specific column | |||||
* | |||||
* @param $array | |||||
* @return array | |||||
*/ | |||||
protected function exceptColumn($array) | |||||
{ | |||||
$keys = $this->_except; | |||||
if (count($keys) == 0) { | |||||
return $array; | |||||
} | |||||
return array_diff_key($array, array_flip((array) $keys)); | |||||
} | |||||
/** | |||||
* Prepare data for result | |||||
* | |||||
* @param mixed $data | |||||
* @param bool $isObject | |||||
* @return array|mixed | |||||
*/ | |||||
protected function prepareResult($data, $isObject) | |||||
{ | |||||
$output = []; | |||||
if (is_null($data) || is_scalar($data)) { | |||||
return $data; | |||||
} | |||||
if ($this->isMultiArray($data)) { | |||||
foreach ($data as $key => $val) { | |||||
$val = $this->takeColumn($val); | |||||
$output[$key] = $isObject ? (object) $val : $val; | |||||
} | |||||
} else { | |||||
$output = json_decode(json_encode($this->takeColumn($data)), $isObject); | |||||
} | |||||
return $output; | |||||
} | |||||
/** | |||||
* Read JSON data from file | |||||
* | |||||
* @param string $file | |||||
* @param string $type | |||||
* @return bool|string|array | |||||
* @throws FileNotFoundException | |||||
* @throws InvalidJsonException | |||||
*/ | |||||
protected function getDataFromFile($file, $type = 'application/json') | |||||
{ | |||||
$opts = [ | |||||
'http' => [ | |||||
'header' => 'Content-Type: '.$type.'; charset=utf-8', | |||||
], | |||||
]; | |||||
$context = stream_context_create($opts); | |||||
$data = file_get_contents($file, 0, $context); | |||||
$json = $this->isJson($data, true); | |||||
if (!$json) { | |||||
throw new InvalidJsonException(); | |||||
} | |||||
return $json; | |||||
} | |||||
/** | |||||
* Get data from nested array | |||||
* | |||||
* @param $map array | |||||
* @param $node string | |||||
* @return bool|array|mixed | |||||
*/ | |||||
protected function getFromNested($map, $node) | |||||
{ | |||||
if (empty($node) || $node == '.') { | |||||
return $map; | |||||
} | |||||
if ($node) { | |||||
$terminate = false; | |||||
$path = explode('.', $node); | |||||
foreach ($path as $val) { | |||||
if (!is_array($map)) return $map; | |||||
if (!array_key_exists($val, $map)) { | |||||
$terminate = true; | |||||
break; | |||||
} | |||||
$map = &$map[$val]; | |||||
} | |||||
if ($terminate) { | |||||
return new ValueNotFound(); | |||||
} | |||||
return $map; | |||||
} | |||||
return new ValueNotFound(); | |||||
} | |||||
/** | |||||
* get data from node path | |||||
* | |||||
* @return mixed | |||||
*/ | |||||
protected function getData() | |||||
{ | |||||
return $this->getFromNested($this->_map, $this->_node); | |||||
} | |||||
/** | |||||
* process AND and OR conditions | |||||
* | |||||
* @return array|string|object | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
protected function processConditions() | |||||
{ | |||||
$data = $this->getData(); | |||||
$conditions = $this->_conditions; | |||||
$result = array_filter($data, function ($val) use ($conditions) { | |||||
$res = false; | |||||
foreach ($conditions as $cond) { | |||||
$tmp = true; | |||||
foreach ($cond as $rule) { | |||||
$function = self::$_rulesMap[$rule['condition']]; | |||||
if (!is_callable($function)) { | |||||
if (!method_exists(Condition::class, $function)) { | |||||
throw new ConditionNotAllowedException("Exception: $function condition not allowed"); | |||||
} | |||||
$function = [Condition::class, $function]; | |||||
} | |||||
$value = $this->getFromNested($val, $rule['key']); | |||||
$return = $value instanceof ValueNotFound ? false : call_user_func_array($function, [$value, $rule['value']]); | |||||
$tmp &= $return; | |||||
} | |||||
$res |= $tmp; | |||||
} | |||||
return $res; | |||||
}); | |||||
return $result; | |||||
} | |||||
/** | |||||
* make WHERE clause | |||||
* | |||||
* @param string $key | |||||
* @param string $condition | |||||
* @param mixed $value | |||||
* @return $this | |||||
*/ | |||||
public function where($key, $condition = null, $value = null) | |||||
{ | |||||
if (!is_null($condition) && is_null($value)) { | |||||
$value = $condition; | |||||
$condition = '='; | |||||
} | |||||
if (count($this->_conditions) < 1) { | |||||
array_push($this->_conditions, []); | |||||
} | |||||
return $this->makeWhere($key, $condition, $value); | |||||
} | |||||
/** | |||||
* make WHERE clause with OR | |||||
* | |||||
* @param string $key | |||||
* @param string $condition | |||||
* @param mixed $value | |||||
* @return $this | |||||
*/ | |||||
public function orWhere($key = null, $condition = null, $value = null) | |||||
{ | |||||
if (!is_null($condition) && is_null($value)) { | |||||
$value = $condition; | |||||
$condition = '='; | |||||
} | |||||
array_push($this->_conditions, []); | |||||
return $this->makeWhere($key, $condition, $value); | |||||
} | |||||
/** | |||||
* generator for AND and OR where | |||||
* | |||||
* @param string $key | |||||
* @param string $condition | |||||
* @param mixed $value | |||||
* @return $this | |||||
*/ | |||||
protected function makeWhere($key, $condition = null, $value = null) | |||||
{ | |||||
$current = end($this->_conditions); | |||||
$index = key($this->_conditions); | |||||
if (is_callable($key)) { | |||||
$key($this); | |||||
return $this; | |||||
} | |||||
array_push($current, [ | |||||
'key' => $key, | |||||
'condition' => $condition, | |||||
'value' => $value, | |||||
]); | |||||
$this->_conditions[$index] = $current; | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE IN clause | |||||
* | |||||
* @param string $key | |||||
* @param array $value | |||||
* @return $this | |||||
*/ | |||||
public function whereIn($key = null, $value = []) | |||||
{ | |||||
$this->where($key, 'in', $value); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE NOT IN clause | |||||
* | |||||
* @param string $key | |||||
* @param mixed $value | |||||
* @return $this | |||||
*/ | |||||
public function whereNotIn($key = null, $value = []) | |||||
{ | |||||
$this->where($key, 'notin', $value); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE NULL clause | |||||
* | |||||
* @param string $key | |||||
* @return $this | |||||
*/ | |||||
public function whereNull($key = null) | |||||
{ | |||||
$this->where($key, 'null', 'null'); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE Boolean clause | |||||
* | |||||
* @param string $key | |||||
* @return $this | |||||
*/ | |||||
public function whereBool($key, $value) | |||||
{ | |||||
if (is_bool($value)) { | |||||
$this->where($key, '==', $value); | |||||
} | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE NOT NULL clause | |||||
* | |||||
* @param string $key | |||||
* @return $this | |||||
*/ | |||||
public function whereNotNull($key = null) | |||||
{ | |||||
$this->where($key, 'notnull', 'null'); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE START WITH clause | |||||
* | |||||
* @param string $key | |||||
* @param string $value | |||||
* @return $this | |||||
*/ | |||||
public function whereStartsWith($key, $value) | |||||
{ | |||||
$this->where($key, 'startswith', $value); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE ENDS WITH clause | |||||
* | |||||
* @param string $key | |||||
* @param string $value | |||||
* @return $this | |||||
*/ | |||||
public function whereEndsWith($key, $value) | |||||
{ | |||||
$this->where($key, 'endswith', $value); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE MATCH clause | |||||
* | |||||
* @param string $key | |||||
* @param string $value | |||||
* @return $this | |||||
*/ | |||||
public function whereMatch($key, $value) | |||||
{ | |||||
$this->where($key, 'match', $value); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE CONTAINS clause | |||||
* | |||||
* @param string $key | |||||
* @param string $value | |||||
* @return $this | |||||
*/ | |||||
public function whereContains($key, $value) | |||||
{ | |||||
$this->where($key, 'contains', $value); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE DATE clause | |||||
* | |||||
* @param string $key | |||||
* @param string $value | |||||
* @return $this | |||||
*/ | |||||
public function whereDate($key, $value) | |||||
{ | |||||
$this->where($key, 'dates', $value); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE month clause | |||||
* | |||||
* @param string $key | |||||
* @param string $value | |||||
* @return $this | |||||
*/ | |||||
public function whereMonth($key, $value) | |||||
{ | |||||
$this->where($key, 'month', $value); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make WHERE Year clause | |||||
* | |||||
* @param string $key | |||||
* @param string $value | |||||
* @return $this | |||||
*/ | |||||
public function whereYear($key, $value) | |||||
{ | |||||
$this->where($key, 'year', $value); | |||||
return $this; | |||||
} | |||||
/** | |||||
* make macro for custom where clause | |||||
* | |||||
* @param string $name | |||||
* @param callable $fn | |||||
* @return bool | |||||
*/ | |||||
public static function macro($name, callable $fn) | |||||
{ | |||||
if (!in_array($name, self::$_rulesMap)) { | |||||
self::$_rulesMap[$name] = $fn; | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
} |
@@ -0,0 +1,741 @@ | |||||
<?php | |||||
if (!defined('DEDEINC')) exit('dedebiz'); | |||||
require_once(dirname(__FILE__)."/Exceptions/ConditionNotAllowedException.php"); | |||||
require_once(dirname(__FILE__)."/Exceptions/FileNotFoundException.php"); | |||||
require_once(dirname(__FILE__)."/Exceptions/InvalidJsonException.php"); | |||||
require_once(dirname(__FILE__)."/Exceptions/InvalidNodeException.php"); | |||||
require_once(dirname(__FILE__)."/Exceptions/NullValueException.php"); | |||||
require_once(dirname(__FILE__)."/JsonQueriable.php"); | |||||
class Jsonq | |||||
{ | |||||
use JsonQueriable; | |||||
/** | |||||
* this constructor set main json file path | |||||
* otherwise create it and read file contents | |||||
* and decode as an array and store it in $this->_data | |||||
* | |||||
* @param null $jsonFile | |||||
* @throws Exceptions\FileNotFoundException | |||||
* @throws InvalidJsonException | |||||
*/ | |||||
public function __construct($jsonFile = null) | |||||
{ | |||||
if (!is_null($jsonFile)) { | |||||
$this->import($jsonFile); | |||||
} | |||||
} | |||||
/** | |||||
* Deep copy current instance | |||||
* | |||||
* @return Jsonq | |||||
*/ | |||||
public function copy() | |||||
{ | |||||
return clone $this; | |||||
} | |||||
/** | |||||
* Set node path, where JsonQ start to prepare | |||||
* | |||||
* @param null $node | |||||
* @return $this | |||||
* @throws NullValueException | |||||
*/ | |||||
public function from($node = null) | |||||
{ | |||||
$this->_isProcessed = false; | |||||
if (is_null($node) || $node == '') { | |||||
throw new NullValueException("Null node exception"); | |||||
} | |||||
$this->_node = $node; | |||||
return $this; | |||||
} | |||||
/** | |||||
* Alias of from() method | |||||
* | |||||
* @param null $node | |||||
* @return $this | |||||
* @throws NullValueException | |||||
*/ | |||||
public function at($node = null) | |||||
{ | |||||
return $this->from($node); | |||||
} | |||||
/** | |||||
* select desired column | |||||
* | |||||
* @param ... scalar | |||||
* @return $this | |||||
*/ | |||||
public function select() | |||||
{ | |||||
$args = func_get_args(); | |||||
if (count($args) > 0 ){ | |||||
$this->_select = $args; | |||||
} | |||||
return $this; | |||||
} | |||||
/** | |||||
* select desired column for except | |||||
* | |||||
* @param ... scalar | |||||
* @return $this | |||||
*/ | |||||
public function except() | |||||
{ | |||||
$args = func_get_args(); | |||||
if (count($args) > 0 ){ | |||||
$this->_except = $args; | |||||
} | |||||
return $this; | |||||
} | |||||
/** | |||||
* getting prepared data | |||||
* | |||||
* @param bool $object | |||||
* @return array|object | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function get($object = false) | |||||
{ | |||||
$this->prepare(); | |||||
return $this->prepareResult($this->_map, $object); | |||||
} | |||||
/** | |||||
* alias of get method | |||||
* | |||||
* @param bool $object | |||||
* @return array|object | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function fetch($object = true) | |||||
{ | |||||
return $this->get($object); | |||||
} | |||||
/** | |||||
* check data exists in system | |||||
* | |||||
* @return bool | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function exists() | |||||
{ | |||||
$this->prepare(); | |||||
return (!empty($this->_map) && !is_null($this->_map)); | |||||
} | |||||
/** | |||||
* reset given data to the $_map | |||||
* | |||||
* @param mixed $data | |||||
* @param bool $instance | |||||
* @return jsonq | |||||
*/ | |||||
public function reset($data = null, $instance = false) | |||||
{ | |||||
if (!is_null($data)) { | |||||
$this->_baseContents = $data; | |||||
} | |||||
if ($instance) { | |||||
$self = new self(); | |||||
$self->collect($this->_baseContents); | |||||
return $self; | |||||
} | |||||
$this->_map = $this->_baseContents; | |||||
$this->reProcess(); | |||||
return $this; | |||||
} | |||||
/** | |||||
* getting group data from specific column | |||||
* | |||||
* @param string $column | |||||
* @return $this | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function groupBy($column) | |||||
{ | |||||
$this->prepare(); | |||||
$data = []; | |||||
foreach ($this->_map as $map) { | |||||
$value = $this->getFromNested($map, $column); | |||||
if ($value) { | |||||
$data[$value][] = $map; | |||||
} | |||||
} | |||||
$this->_map = $data; | |||||
return $this; | |||||
} | |||||
public function countGroupBy($column) | |||||
{ | |||||
$this->prepare(); | |||||
$data = []; | |||||
foreach ($this->_map as $map) { | |||||
$value = $this->getFromNested($map, $column); | |||||
if (!$value) { | |||||
continue; | |||||
} | |||||
if (isset($data[$value])) { | |||||
$data[$value] ++; | |||||
} else { | |||||
$data[$value] = 1; | |||||
} | |||||
} | |||||
$this->_map = $data; | |||||
return $this; | |||||
} | |||||
/** | |||||
* count prepared data | |||||
* | |||||
* @return int | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function count() | |||||
{ | |||||
$this->prepare(); | |||||
return count($this->_map); | |||||
} | |||||
/** | |||||
* size is an alias of count | |||||
* | |||||
* @return int | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function size() | |||||
{ | |||||
return $this->count(); | |||||
} | |||||
/** | |||||
* sum prepared data | |||||
* @param int $column | |||||
* @return int | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function sum($column = null) | |||||
{ | |||||
$this->prepare(); | |||||
$sum = 0; | |||||
if (is_null($column)) { | |||||
$sum = array_sum($this->_map); | |||||
} else { | |||||
foreach ($this->_map as $key => $val) { | |||||
$value = $this->getFromNested($val, $column); | |||||
if (is_scalar($value)) { | |||||
$sum += $value; | |||||
} | |||||
} | |||||
} | |||||
return $sum; | |||||
} | |||||
/** | |||||
* getting max value from prepared data | |||||
* | |||||
* @param int $column | |||||
* @return int | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function max($column = null) | |||||
{ | |||||
$this->prepare(); | |||||
if (is_null($column)) { | |||||
$max = max($this->_map); | |||||
} else { | |||||
$max = max(array_column($this->_map, $column)); | |||||
} | |||||
return $max; | |||||
} | |||||
/** | |||||
* getting min value from prepared data | |||||
* | |||||
* @param int $column | |||||
* @return string | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function min($column = null) | |||||
{ | |||||
$this->prepare(); | |||||
if (is_null($column)) { | |||||
$min = min($this->_map); | |||||
} else { | |||||
$min = min(array_column($this->_map, $column)); | |||||
} | |||||
return $min; | |||||
} | |||||
/** | |||||
* getting average value from prepared data | |||||
* | |||||
* @param int $column | |||||
* @return string | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function avg($column = null) | |||||
{ | |||||
$this->prepare(); | |||||
$count = $this->count(); | |||||
$total = $this->sum($column); | |||||
return ($total/$count); | |||||
} | |||||
/** | |||||
* getting first element of prepared data | |||||
* | |||||
* @param bool $object | |||||
* @return object|array|null | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function first($object = false) | |||||
{ | |||||
$this->prepare(); | |||||
$data = $this->_map; | |||||
if (count($data) > 0) { | |||||
return $this->prepareResult(reset($data), $object); | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* getting last element of prepared data | |||||
* | |||||
* @param bool $object | |||||
* @return object|array|null | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function last($object = false) | |||||
{ | |||||
$this->prepare(); | |||||
$data = $this->_map; | |||||
if (count($data) > 0) { | |||||
return $this->prepareResult(end($data), $object); | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* getting nth number of element of prepared data | |||||
* | |||||
* @param int $index | |||||
* @param bool $object | |||||
* @return object|array|null | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function nth($index, $object = false) | |||||
{ | |||||
$this->prepare(); | |||||
$data = $this->_map; | |||||
$total_elm = count($data); | |||||
$idx = abs($index); | |||||
if (!is_integer($index) || $total_elm < $idx || $index == 0 || !is_array($this->_map)) { | |||||
return null; | |||||
} | |||||
if ($index > 0) { | |||||
$result = $data[$index - 1]; | |||||
} else { | |||||
$result = $data[$this->count() + $index]; | |||||
} | |||||
return $this->prepareResult($result, $object); | |||||
} | |||||
/** | |||||
* sorting from prepared data | |||||
* | |||||
* @param string $column | |||||
* @param string $order | |||||
* @return object|array|null | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function sortBy($column, $order = 'asc') | |||||
{ | |||||
$this->prepare(); | |||||
if (!is_array($this->_map)) { | |||||
return $this; | |||||
} | |||||
usort($this->_map, function ($a, $b) use ($column, $order) { | |||||
$val1 = $this->getFromNested($a, $column); | |||||
$val2 = $this->getFromNested($b, $column); | |||||
if (is_string($val1)) { | |||||
$val1 = strtolower($val1); | |||||
} | |||||
if (is_string($val2)) { | |||||
$val2 = strtolower($val2); | |||||
} | |||||
if ($val1 == $val2) { | |||||
return 0; | |||||
} | |||||
$order = strtolower(trim($order)); | |||||
if ($order == 'desc') { | |||||
return ($val1 > $val2) ? -1 : 1; | |||||
} else { | |||||
return ($val1 < $val2) ? -1 : 1; | |||||
} | |||||
}); | |||||
return $this; | |||||
} | |||||
/** | |||||
* Sort prepared data using a custom sort function. | |||||
* | |||||
* @param callable $sortFunc | |||||
* | |||||
* @return object|array|null | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function sortByCallable(callable $sortFunc) | |||||
{ | |||||
$this->prepare(); | |||||
if (!is_array($this->_map)) { | |||||
return $this; | |||||
} | |||||
usort($this->_map, $sortFunc); | |||||
return $this; | |||||
} | |||||
/** | |||||
* Sort an array value | |||||
* | |||||
* @param string $order | |||||
* @return Jsonq | |||||
*/ | |||||
public function sort($order = 'asc') | |||||
{ | |||||
if ($order == 'desc') { | |||||
rsort($this->_map); | |||||
}else{ | |||||
sort($this->_map); | |||||
} | |||||
return $this; | |||||
} | |||||
/** | |||||
* getting data from desire path | |||||
* | |||||
* @param string $path | |||||
* @param bool $object | |||||
* @return mixed | |||||
* @throws NullValueException | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function find($path, $object = false) | |||||
{ | |||||
return $this->from($path)->prepare()->get($object); | |||||
} | |||||
/** | |||||
* take action of each element of prepared data | |||||
* | |||||
* @param callable $fn | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function each(callable $fn) | |||||
{ | |||||
$this->prepare(); | |||||
foreach ($this->_map as $key => $val) { | |||||
$fn($key, $val); | |||||
} | |||||
} | |||||
/** | |||||
* transform prepared data by using callable function | |||||
* | |||||
* @param callable $fn | |||||
* @return object|array | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function transform(callable $fn) | |||||
{ | |||||
$this->prepare(); | |||||
$new_data = []; | |||||
foreach ($this->_map as $key => $val) { | |||||
$new_data[$key] = $fn($val); | |||||
} | |||||
return $this->prepareResult($new_data, false); | |||||
} | |||||
/** | |||||
* pipe send output in next pipe | |||||
* | |||||
* @param callable $fn | |||||
* @param string|null $class | |||||
* @return object|array | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function pipe(callable $fn, $class = null) | |||||
{ | |||||
$this->prepare(); | |||||
if (is_string($fn) && !is_null($class)) { | |||||
$instance = new $class; | |||||
$this->_map = call_user_func_array([$instance, $fn], [$this]); | |||||
return $this; | |||||
} | |||||
$this->_map = $fn($this); | |||||
return $this; | |||||
} | |||||
/** | |||||
* filtered each element of prepared data | |||||
* | |||||
* @param callable $fn | |||||
* @param bool $key | |||||
* @return mixed|array | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function filter(callable $fn, $key = false) | |||||
{ | |||||
$this->prepare(); | |||||
$data = []; | |||||
foreach ($this->_map as $k => $val) { | |||||
if ($fn($val)) { | |||||
if ($key) { | |||||
$data[$k] = $val; | |||||
} else { | |||||
$data[] = $val; | |||||
} | |||||
} | |||||
} | |||||
return $this->prepareResult($data, false); | |||||
} | |||||
/** | |||||
* then method set position of working data | |||||
* | |||||
* @param string $node | |||||
* @return jsonq | |||||
* @throws NullValueException | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function then($node) | |||||
{ | |||||
$this->_map = $this->prepare()->first(false); | |||||
$this->from($node); | |||||
return $this; | |||||
} | |||||
/** | |||||
* import raw JSON data for process | |||||
* | |||||
* @param string $data | |||||
* @return jsonq | |||||
*/ | |||||
public function json($data) | |||||
{ | |||||
$json = $this->isJson($data, true); | |||||
if ($json) { | |||||
return $this->collect($json); | |||||
} | |||||
return $this; | |||||
} | |||||
/** | |||||
* import parsed data from raw json | |||||
* | |||||
* @param array|object $data | |||||
* @return jsonq | |||||
*/ | |||||
public function collect($data) | |||||
{ | |||||
$this->_map = $this->objectToArray($data); | |||||
$this->_baseContents = &$this->_map; | |||||
return $this; | |||||
} | |||||
/** | |||||
* implode resulting data from desire key and delimeter | |||||
* | |||||
* @param string|array $key | |||||
* @param string $delimiter | |||||
* @return string|array | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function implode($key, $delimiter = ',') | |||||
{ | |||||
$this->prepare(); | |||||
$implode = []; | |||||
if (is_string($key)) { | |||||
return $this->makeImplode($key, $delimiter); | |||||
} | |||||
if (is_array($key)) { | |||||
foreach ($key as $k) { | |||||
$imp = $this->makeImplode($k, $delimiter); | |||||
$implode[$k] = $imp; | |||||
} | |||||
return $implode; | |||||
} | |||||
return ''; | |||||
} | |||||
/** | |||||
* process implode from resulting data | |||||
* | |||||
* @param string $key | |||||
* @param string $delimiter | |||||
* @return string|null | |||||
*/ | |||||
protected function makeImplode($key, $delimiter) | |||||
{ | |||||
$data = array_column($this->_map, $key); | |||||
if (is_array($data)) { | |||||
return implode($delimiter, $data); | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* getting specific key's value from prepared data | |||||
* | |||||
* @param string $column | |||||
* @return object|array | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function column($column) | |||||
{ | |||||
$this->prepare(); | |||||
return array_column($this->_map, $column); | |||||
} | |||||
/** | |||||
* getting raw JSON from prepared data | |||||
* | |||||
* @return string | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function toJson() | |||||
{ | |||||
$this->prepare(); | |||||
return json_encode($this->_map); | |||||
} | |||||
/** | |||||
* getting all keys from prepared data | |||||
* | |||||
* @return object|array | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function keys() | |||||
{ | |||||
$this->prepare(); | |||||
return array_keys($this->_map); | |||||
} | |||||
/** | |||||
* getting all values from prepared data | |||||
* | |||||
* @return object|array | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function values() | |||||
{ | |||||
$this->prepare(); | |||||
return array_values($this->_map); | |||||
} | |||||
/** | |||||
* getting chunk values from prepared data | |||||
* | |||||
* @param int $amount | |||||
* @param $fn | |||||
* @return object|array|bool | |||||
* @throws ConditionNotAllowedException | |||||
*/ | |||||
public function chunk($amount, callable $fn = null) | |||||
{ | |||||
$this->prepare(); | |||||
$chunk_value = array_chunk($this->_map, $amount); | |||||
$chunks = []; | |||||
if (!is_null($fn) && is_callable($fn)) { | |||||
foreach ($chunk_value as $chunk) { | |||||
$return = $fn($chunk); | |||||
if (!is_null($return)) { | |||||
$chunks[] = $return; | |||||
} | |||||
} | |||||
return count($chunks) > 0 ? $chunks : null; | |||||
} | |||||
return $chunk_value; | |||||
} | |||||
} |
@@ -0,0 +1,7 @@ | |||||
<?php | |||||
if (!defined('DEDEINC')) exit('dedebiz'); | |||||
/** | |||||
* This class represents a query result where a given | |||||
* value was queried but did not exist. | |||||
*/ | |||||
class ValueNotFound {} |
@@ -0,0 +1,7 @@ | |||||
JSONQ标签 | |||||
>>dede>> | |||||
{dede:jsonq url='https://www.dedebiz.com/api/v1/ping' path='list' cachetime=3600}[field:id/] - [field:name/]<br/>{/dede:jsonq} | |||||
>>dede>> | |||||
url='' JSON接口地址 | |||||
path='' 路径(可选),如果为空[field:path/]可获取指定path的值 | |||||
cachetime=3600 缓存时间 |
@@ -1,5 +1,5 @@ | |||||
SQL标签 | SQL标签 | ||||
>>dede>> | >>dede>> | ||||
{dede:sql sql=''}[field:title/]{/dede} | |||||
{dede:sql sql=''}[field:title/]{/dede:sql} | |||||
>>dede>> | >>dede>> | ||||
sql='' 完整的SQL语句 | sql='' 完整的SQL语句 |
@@ -0,0 +1,83 @@ | |||||
<?php | |||||
/** | |||||
* JSONQ标签 | |||||
* | |||||
* @version $id:jsonq.lib.php 2023年3月20日 tianya $ | |||||
* @package DedeBIZ.Taglib | |||||
* @copyright Copyright (c) 2022 DedeBIZ.COM | |||||
* @license https://www.dedebiz.com/license | |||||
* @link https://www.dedebiz.com | |||||
*/ | |||||
require_once(DEDEINC . "/libraries/jsonq/Jsonq.php"); | |||||
helper('cache'); | |||||
function lib_jsonq(&$ctag, &$refObj) | |||||
{ | |||||
$attlist = "url|,path|,cachetime|3600"; | |||||
FillAttsDefault($ctag->CAttribute->Items, $attlist); | |||||
extract($ctag->CAttribute->Items, EXTR_SKIP); | |||||
$Innertext = trim($ctag->GetInnerText()); | |||||
if ($url == '' || $Innertext == '') return ''; | |||||
$key = md5($url); | |||||
try { | |||||
if ($path=='') { | |||||
$jsonq = new Jsonq($url); | |||||
$revalue = GetCache("tagjsonq2", $key); | |||||
if (!empty($revalue)) { | |||||
return $revalue; | |||||
} | |||||
$revalue = ""; | |||||
$ctp = new DedeTagParse(); | |||||
$ctp->SetNameSpace('field', '[', ']'); | |||||
$ctp->LoadSource($Innertext); | |||||
foreach ($ctp->CTags as $tagid => $ctag) { | |||||
$tagname = $ctag->GetName(); | |||||
$vv = $jsonq->from($tagname)->get(); | |||||
$ctp->Assign($tagid, $vv); | |||||
$jsonq->reset(); | |||||
} | |||||
$revalue .= $ctp->GetResult(); | |||||
SetCache("tagjsonq2", $key, $revalue, $cachetime); | |||||
return $revalue; | |||||
} | |||||
$row = GetCache("tagjsonq", $key); | |||||
if (!is_array($row) || $cachetime == 0) { | |||||
$jsonq = new Jsonq($url); | |||||
$row = $jsonq->from($path)->get(); | |||||
SetCache("tagjsonq", $key, $row, $cachetime); | |||||
} | |||||
if (!is_array($row)) { | |||||
return ""; | |||||
} | |||||
$ctp = new DedeTagParse(); | |||||
$ctp->SetNameSpace('field', '[', ']'); | |||||
$ctp->LoadSource($Innertext); | |||||
$GLOBALS['autoindex'] = 0; | |||||
$revalue = ""; | |||||
foreach ($row as $key => $value) { | |||||
$GLOBALS['autoindex']++; | |||||
foreach ($ctp->CTags as $tagid => $ctag) { | |||||
if ($ctag->GetName() == 'array') { | |||||
$ctp->Assign($tagid, $value); | |||||
} else { | |||||
if (!empty($value[$ctag->GetName()])) { | |||||
$ctp->Assign($tagid, $value[$ctag->GetName()]); | |||||
} else { | |||||
$ctp->Assign($tagid, ""); | |||||
} | |||||
} | |||||
} | |||||
$revalue .= $ctp->GetResult(); | |||||
} | |||||
return $revalue; | |||||
} catch (Exception $e) { | |||||
return ""; | |||||
} | |||||
} |