| @@ -40,7 +40,7 @@ if (!is_array($row) || $cfg_digg_update == 0) { | |||
| } | |||
| DelCache($prefix, $key); | |||
| } | |||
| SetCache($prefix, $key, $row, 0); | |||
| SetCache($prefix, $key, $row); | |||
| } else { | |||
| if ($action == 'good') { | |||
| $row['goodpost'] = $row['goodpost'] + 1; | |||
| @@ -59,7 +59,7 @@ if (!is_array($row) || $cfg_digg_update == 0) { | |||
| DelCache($prefix, $key); | |||
| } | |||
| } | |||
| SetCache($prefix, $key, $row, 0); | |||
| SetCache($prefix, $key, $row); | |||
| } | |||
| $digg = ''; | |||
| 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标签 | |||
| >>dede>> | |||
| {dede:sql sql=''}[field:title/]{/dede} | |||
| {dede:sql sql=''}[field:title/]{/dede:sql} | |||
| >>dede>> | |||
| 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 ""; | |||
| } | |||
| } | |||