diff --git a/Database/ActiveQuery.php b/Database/ActiveQuery.php new file mode 100644 index 00000000..0ad2f476 --- /dev/null +++ b/Database/ActiveQuery.php @@ -0,0 +1,406 @@ +modelClass = $model; + parent::__construct($config); + } + + /** + * + */ + public function afterInit() + { + $this->debug(get_called_class() . ' AFTER INIT.'); + $this->clear(); + } + + /** + * 清除不完整数据 + */ + public function clear() + { + $this->db = null; + $this->useCache = false; + $this->with = []; + } + + /** + * @param $key + * @param $value + * @return $this + */ + public function addParam($key, $value) + { + $this->attributes[$key] = $value; + return $this; + } + + /** + * @param array $values + * @return $this + */ + public function addParams(array $values) + { + foreach ($values as $key => $val) { + $this->addParam($key, $val); + } + return $this; + } + + /** + * @param $name + * @return $this + */ + public function with($name) + { + if (empty($name)) { + return $this; + } + if (is_string($name)) { + $name = explode(',', $name); + } + foreach ($name as $key => $val) { + array_push($this->with, $val); + } + return $this; + } + + /** + * @param bool $isArray + * @return $this + */ + public function asArray($isArray = TRUE) + { + $this->asArray = $isArray; + return $this; + } + + /** + * @return ActiveRecord + * @throws + */ + public function first() + { + $data = $this->command($this->oneLimit()->adaptation())->one(); + if (empty($data)) { + return NULL; + } + $newModel = Snowflake::createObject($this->modelClass); + $newModel = $this->populate($newModel, $data); + if ($this->asArray) { + return $newModel->toArray(); + } + return $newModel; + } + + /** + * @return array|Collection + */ + public function get() + { + return $this->all(); + } + + + /** + * @throws Exception + */ + public function flush() + { + $sql = $this->getChange()->truncate($this->getTable()); + return $this->command($sql)->flush(); + } + + + /** + * @param int $size + * @param callable $callback + * @param mixed $param + * @param int $offset + * @param int $total + * @throws Exception + * + * + * $pagination = new Pagination($this); + * $pagination->setOffset($offset); + * $pagination->setLimit($size); + * $pagination->setMax($total); + * $pagination->setCallback($callback); + * $pagination->search($param); + */ + public function plunk(int $size, callable $callback, $param = null, $offset = 0, $total = -1) + { + if (!is_callable($callback, true)) { + return; + } + $data = $this->limit($offset, $size)->get(); + if ($param !== null) { + call_user_func($callback, $data, $param); + } else { + call_user_func($callback, $data); + } + if ($data->size() < $size) { + return; + } + unset($data); + $this->plunk($size, $callback, $param, $offset + $size, $total); + } + + /** + * @param string $field + * @param string $setKey + * + * @return array|null + * @throws Exception + */ + public function column(string $field, $setKey = '') + { + return $this->all()->column($field, $setKey); + } + + /** + * @return array|Collection + * @throws + */ + public function all() + { + $data = $this->command($this->adaptation())->all(); + $collect = new Collection(); + $collect->setModel($this->modelClass); + if (empty($data) || !is_array($data)) { + return $this->asArray ? [] : $collect; + } + + $_tmp = []; + $model = Snowflake::createObject($this->modelClass); + foreach ($data as $key => $val) { + $_tmp[] = $this->populate(clone $model, $val); + } + $collect->setItems($_tmp); + if ($this->asArray) { + return $collect->toArray(); + } + return $collect; + } + + /** + * @param ActiveRecord $model + * @param $data + * @return ActiveRecord + * @throws Exception + */ + private function populate($model, $data) + { + $model = $model::populate($data); + if (empty($this->with) || !is_array($this->with)) { + return $model; + } + foreach ($this->with as $val) { + $method = 'get' . ucfirst($val); + if (!method_exists($model, $method)) { + continue; + } + $model->setRelate($val, $method); + } + return $model; + } + + /** + * @return int + * @throws Exception + */ + public function count() + { + $data = $this->command($this->getBuild()->count($this))->one(); + if ($data && is_array($data)) { + return (int)array_shift($data); + } + return 0; + } + + + /** + * @param array $data + * @return array|Command|bool|int|string + * @throws Exception + */ + public function batchUpdate(array $data) + { + return $this->getDb()->createCommand() + ->batchUpdate($this->getTable(), $data, $this->getCondition()) + ->exec(); + } + + /** + * @param array $data + * @return bool + * @throws Exception + */ + public function batchInsert(array $data) + { + return $this->getDb()->createCommand() + ->batchInsert($this->getTable(), $data) + ->exec(); + } + + /** + * @param $filed + * + * @return null + * @throws Exception + */ + public function value($filed) + { + $first = $this->first()->toArray(); + return $first[$filed] ?? null; + } + + /** + * @return bool + * @throws Exception + */ + public function exists() + { + return (bool)$this->command($this->adaptation())->fetchColumn(); + } + + /** + * @return bool + * @throws Exception + */ + public function deleteAll() + { + return $this->command($this->getChange()->delete($this))->delete(); + } + + /** + * @return bool + * @throws Exception + */ + public function delete() + { + return $this->deleteAll(); + } + + /** + * @return string + * @throws Exception + */ + public function getCondition() + { + return $this->getBuild()->getWhere($this->where); + } + + /** + * @return string + * @throws Exception + */ + public function adaptation() + { + return $this->getBuild()->getQuery($this); + } + + /** + * @param $sql + * @param array $attr + * @return Command + * @throws Exception + */ + private function command($sql, $attr = []) + { + if (!empty($attr) && is_array($attr)) { + $attr = array_merge($this->attributes, $attr); + } else { + $attr = $this->attributes; + } + return $this->getDb()->createCommand($sql, $attr) + ->setModelName($this->modelClass); + } + + /** + * @return Select + * @throws Exception + */ + public function getBuild() + { + return $this->getDb()->getSchema()->getQueryBuilder(); + } + + /** + * @return orm\Change + * @throws Exception + */ + public function getChange() + { + return $this->getDb()->getSchema()->getChange(); + } + + /** + * @return Connection + * @throws Exception + */ + public function getDb() + { + return $this->modelClass::getDb(); + } + + /** + * @return mixed + * @throws Exception + */ + public function getPrimary() + { + return $this->modelClass::getPrimary(); + } +} diff --git a/Database/ActiveRecord.php b/Database/ActiveRecord.php new file mode 100644 index 00000000..4215ed37 --- /dev/null +++ b/Database/ActiveRecord.php @@ -0,0 +1,416 @@ +mathematics(self::INCR, [$column => $value])) { + return false; + } + $this->{$column} += $value; + return $this->refresh(); + } + + /** + * @param $column + * @param $value + * @return array|Command|bool|int|string + * @throws Exception + */ + public function decrBy(string $column, int $value) + { + if (!$this->mathematics(self::DECR, [$column => $value])) { + return false; + } + $this->{$column} -= $value; + return $this->refresh(); + } + + /** + * @param array $attributes + * @return bool|self + * @throws Exception + */ + public function batchIncrColumn(array $attributes) + { + if (!$this->mathematics(self::INCR, $attributes)) { + return false; + } + foreach ($attributes as $key => $attribute) { + $this->$key += $attribute; + } + return $this; + } + + /** + * @param array $attributes + * @return bool|self + * @throws Exception + */ + public function batchDescColumn(array $attributes) + { + if (!$this->mathematics(self::DECR, $attributes)) { + return false; + } + foreach ($attributes as $key => $attribute) { + $this->$key -= $attribute; + } + return $this; + } + + + /** + * @param $action + * @param $columns + * @param array $condition + * @return array|bool|int|string|null + * @throws Exception + */ + private function mathematics($action, $columns, $condition = []) + { + if (empty($condition)) { + $condition = [static::getPrimary() => $this->{static::getPrimary()}]; + } + return static::getDb()->createCommand() + ->mathematics(self::getTable(), [$action => $columns], $condition) + ->exec(); + } + + /** + * @param $column + * @param $value + * @param array $condition + * @return bool + * @throws Exception + */ + public static function incrAll($column, $value, $condition = []) + { + return static::getDb()->createCommand() + ->mathematics(self::getTable(), [self::INCR => [$column, $value]], $condition) + ->exec(); + } + + + /** + * @param $column + * @param $value + * @param array $condition + * @return bool + * @throws Exception + */ + public static function decrAll($column, $value, $condition = []) + { + return static::getDb()->createCommand() + ->mathematics(self::getTable(), [self::DECR => [$column, $value]], $condition) + ->exec(); + } + + + /** + * @param array $attributes + * @return bool + * @throws + */ + public function update(array $attributes) + { + return $this->save($attributes); + } + + /** + * @param array $params + * @param array $condition + * @return bool|static + * @throws Exception + */ + public static function insertOrUpdate(array $params, array $condition = []) + { + if (empty($condition)) { + $condition = $params; + } + $first = static::find()->where($condition)->first(); + if (empty($first)) { + $model = new static(); + $model->attributes = $params; + return $model->save(); + } + + foreach ($params as $key => $param) { + $first->{$key} = $param; + } + if (!$first->save()) { + return false; + } + return $first; + } + + /** + * @param array $data + * @return bool + * @throws Exception + */ + public static function batchInsert(array $data): bool + { + /** @var static $class */ + $class = Snowflake::createObject(static::className()); + if (empty($data)) { + return $class->addError('Insert data empty.', 'mysql'); + } + return $class::find()->batchInsert($data); + } + + /** + * @return bool + * @throws Exception + */ + public function delete() + { + $conditions = $this->_oldAttributes; + if (empty($conditions)) { + return $this->addError("Delete condition do not empty.", 'mysql'); + } + $primary = static::getPrimary(); + + if (!empty($primary)) { + $sul = static::deleteAll([$primary => $this->getAttribute($primary)]); + } else { + $sul = static::deleteAll($conditions); + } + if (!$sul) { + return false; + } + if (method_exists($this, 'afterDelete')) { + $this->afterDelete(); + } + return true; + } + + + /** + * @param $condition + * @param array $attributes + * + * @return bool + * @throws Exception + */ + public static function batchUpdate($condition, $attributes = []) + { + $condition = static::find()->where($condition); + return $condition->batchUpdate($attributes); + } + + /** + * @param $condition + * @param array $attributes + * + * @return array|mixed|null|Collection + * @throws Exception + */ + public static function findAll($condition, $attributes = []) + { + $query = static::find()->where($condition); + if (!empty($attributes)) { + $query->bindParams($attributes); + } + return $query->all(); + } + + /** + * @param $data + * @return array|mixed + * @throws Exception + */ + private function resolveObject($data) + { + if (is_numeric($data) || !method_exists($this, $data)) { + return $data; + } + return ArrayAccess::toArray($this->{$data}()); + } + + + /** + * @return array|mixed + * @throws Exception + */ + public function toArray() + { + $data = []; + foreach ($this->_attributes as $key => $val) { + $data[$key] = $this->getAttribute($key); + } + return array_merge($data, $this->runRelate()); + } + + /** + * @return array + * @throws Exception + */ + private function runRelate() + { + $relates = []; + if (empty($this->_relate)) { + return $relates; + } + foreach ($this->_relate as $key => $val) { + $relates[$key] = $this->resolveObject($val); + } + return $relates; + } + + + /** + * @param $modelName + * @param $foreignKey + * @param $localKey + * @return mixed|ActiveQuery + * @throws Exception + */ + public function hasOne($modelName, $foreignKey, $localKey) + { + if (!$this->has($localKey)) { + throw new Exception("Need join table primary key."); + } + + $value = $this->getAttribute($localKey); + + $relation = $this->getRelation(); + + return new HasOne($modelName, $foreignKey, $value, $relation); + } + + + /** + * @param $modelName + * @param $foreignKey + * @param $localKey + * @return mixed|ActiveQuery + * @throws Exception + */ + public function hasCount($modelName, $foreignKey, $localKey) + { + if (!$this->has($localKey)) { + throw new Exception("Need join table primary key."); + } + + $value = $this->getAttribute($localKey); + + $relation = $this->getRelation(); + + return new HasCount($modelName, $foreignKey, $value, $relation); + } + + + /** + * @param $modelName + * @param $foreignKey + * @param $localKey + * @return mixed|ActiveQuery + * @throws Exception + */ + public function hasMany($modelName, $foreignKey, $localKey) + { + if (!$this->has($localKey)) { + throw new Exception("Need join table primary key."); + } + + $value = $this->getAttribute($localKey); + + $relation = $this->getRelation(); + + return new HasMany($modelName, $foreignKey, $value, $relation); + } + + /** + * @param $modelName + * @param $foreignKey + * @param $localKey + * @return mixed|ActiveQuery + * @throws Exception + */ + public function hasIn($modelName, $foreignKey, $localKey) + { + if (!$this->has($localKey)) { + throw new Exception("Need join table primary key."); + } + + $value = $this->getAttribute($localKey); + + $relation = $this->getRelation(); + + return new HasMany($modelName, $foreignKey, $value, $relation); + } + + /** + * @return bool + * @throws Exception + */ + public function afterDelete() + { + if (!static::hasPrimary()) { + return TRUE; + } + $value = $this->{static::getPrimary()}; + if (empty($value)) { + return TRUE; + } + return TRUE; + } + + /** + * @return bool + * @throws Exception + */ + public function beforeDelete() + { + if (!static::hasPrimary()) { + return TRUE; + } + $value = $this->{static::getPrimary()}; + if (empty($value)) { + return TRUE; + } + return TRUE; + } +} diff --git a/Database/Base/AbstractCollection.php b/Database/Base/AbstractCollection.php new file mode 100644 index 00000000..6408c011 --- /dev/null +++ b/Database/Base/AbstractCollection.php @@ -0,0 +1,126 @@ +_item = $array; + + parent::__construct([]); + } + + + /** + * @return int + */ + public function getLength() + { + return count($this->_item); + } + + + /** + * @param $item + */ + public function setItems($item) + { + $this->_item = $item; + } + + + /** + * @param $model + */ + public function setModel($model) + { + $this->model = $model; + } + + /** + * @param $item + */ + public function addItem($item) + { + array_push($this->_item, $item); + } + + /** + * @return \ArrayIterator|\Traversable + */ + public function getIterator() + { + return new \ArrayIterator($this->_item); + } + + /** + * @param mixed $offset + * @return bool + */ + public function offsetExists($offset) + { + return !empty($this->_item) && isset($this->_item[$offset]); + } + + /** + * @param mixed $offset + * @return mixed|null|ActiveRecord + */ + public function offsetGet($offset) + { + if (!$this->offsetExists($offset)) { + return NULL; + } + /** @var ActiveRecord $model */ + return $this->_item[$offset]; + } + + /** + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + $this->_item[$offset] = $value; + } + + + /** + * @param mixed $offset + */ + public function offsetUnset($offset) + { + if ($this->offsetExists($offset)) { + unset($this->_item[$offset]); + } + } +} diff --git a/Database/Base/BaseActiveRecord.php b/Database/Base/BaseActiveRecord.php new file mode 100644 index 00000000..e66602dc --- /dev/null +++ b/Database/Base/BaseActiveRecord.php @@ -0,0 +1,807 @@ +_relation = Snowflake::createObject(Relation::className()); + } + + /** + * @param string $column + * @param int $value + * @return void + * @throws Exception + */ + public function incrBy(string $column, int $value) + { + throw new Exception('Undefined function incrBy in ' . get_called_class()); + } + + /** + * @param string $column + * @param int $value + * @return void + * @throws Exception + */ + public function decrBy(string $column, int $value) + { + throw new Exception('Undefined function decrBy in ' . get_called_class()); + } + + /** + * @return array + */ + public function getActions() + { + return $this->actions; + } + + /** + * @return bool + */ + public function getIsCreate() + { + return $this->isNewExample === TRUE; + } + + /** + * @param bool $bool + * @return $this + */ + public function setIsCreate($bool = FALSE) + { + $this->isNewExample = $bool; + return $this; + } + + /** + * @return mixed + * + * get last exception or other error + */ + public function getLastError() + { + return Logger::getLastError('mysql'); + } + + /** + * @return bool + * @throws Exception + */ + public static function hasPrimary() + { + if (static::$primary !== NULL) { + return true; + } + $primary = static::getColumns()->getPrimaryKeys(); + if (!empty($primary)) { + return true; + } + return false; + } + + /** + * @throws Exception + */ + public function hasAutoIncrement() + { + $autoIncrement = $this->getAutoIncrement(); + return $autoIncrement !== null; + } + + /** + * @throws Exception + */ + public function getAutoIncrement() + { + return static::getColumns()->getAutoIncrement(); + } + + /** + * @return null|string + * @throws Exception + */ + public static function getPrimary() + { + if (!static::hasPrimary()) { + return null; + } + if (!empty(static::$primary)) { + return static::$primary; + } + $primary = static::getColumns()->getPrimaryKeys(); + if (!empty($primary)) { + return is_array($primary) ? current($primary) : $primary; + } + return null; + } + + /** + * @param $condition + * @param $db + * @return $this + * @throws + */ + public static function findOne($condition, $db = NULL) + { + if (empty($condition) || !is_numeric($condition)) { + return NULL; + } + return static::find()->where([static::getPrimary() => $condition])->first(); + } + + /** + * @param null $field + * @return ActiveRecord|null + * @throws Exception + * @throws Exception + */ + public static function max($field = null) + { + if (empty($field)) { + $field = self::getPrimary(); + } + + $columns = static::getColumns()->get_fields(); + if (!isset($columns[$field])) { + return null; + } + + $first = static::find()->max($field)->first(); + if (empty($first)) { + return null; + } + return $first[$field]; + } + + /** + * @return mixed|ActiveQuery + * @throws + */ + public static function find() + { + return Snowflake::createObject(ActiveQuery::class, [get_called_class()]); + } + + /** + * @param null $condition + * @param array $attributes + * + * @param bool $if_condition_is_null + * @return bool + * @throws Exception + */ + public static function deleteAll($condition = NULL, $attributes = [], $if_condition_is_null = false) + { + if (empty($condition)) { + if (!$if_condition_is_null) { + return false; + } + return static::find()->deleteAll(); + } + $model = static::find()->ifNotWhere($if_condition_is_null)->where($condition); + if (!empty($attributes)) { + $model->bindParams($attributes); + } + return $model->deleteAll(); + } + + + /** + * @return array + */ + public function getAttributes() + { + return $this->_attributes; + } + + /** + * @return array + */ + public function getOldAttributes() + { + return $this->_oldAttributes; + } + + /** + * @param $name + * @param $value + * @return mixed + */ + public function setAttribute($name, $value) + { + return $this->_attributes[$name] = $value; + } + + /** + * @param $name + * @param $value + * @return mixed + */ + public function setOldAttribute($name, $value) + { + return $this->_oldAttributes[$name] = $value; + } + + /** + * @param array $param + * @return $this + * @throws + */ + public function setAttributes($param) + { + if (empty($param) || !is_array($param)) { + return $this; + } + foreach ($param as $key => $val) { + if (!$this->has($key)) { + $this->setAttribute($key, $val); + } else { + $this->$key = $val; + } + } + return $this; + } + + public function setOldAttributes($param) + { + if (empty($param) || !is_array($param)) { + return $this; + } + foreach ($param as $key => $val) { + $this->setOldAttribute($key, $val); + } + return $this; + } + + /** + * @return bool + * @throws Exception + */ + public function beforeSave() + { + return true; + } + + /** + * @param $attributes + * @param $param + * @return $this|bool + * @throws Exception + */ + private function insert($param, $attributes) + { + if (empty($param)) { + return FALSE; + } + $dbConnection = static::getDb(); + $change = $dbConnection->getSchema()->getChange(); + $sqlBuilder = $change->insert(static::getTable(), $attributes, $param); + + $trance = $dbConnection->beginTransaction(); + try { + $commandExec = $dbConnection->createCommand($sqlBuilder, $param); + if (($lastId = $commandExec->save(true, $this->hasAutoIncrement())) === false) { + throw new Exception('保存失败.' . $sqlBuilder); + } + if ($this->hasAutoIncrement()) { + $this->setAttribute($this->getAutoIncrement(), (int)$lastId); + } else if (static::hasPrimary()) { + $primary = static::getPrimary(); + if (!isset($param[$primary]) || empty($param[$primary])) { + $this->setAttribute($primary, (int)$lastId); + } + } + $trance->commit(); + $this->setAttributes($param); + $this->afterSave($attributes, $param); + $this->refresh(); + } catch (Exception $exception) { + $lastId = false; + $trance->rollback(); + } + return $lastId; + } + + + /** + * @param $param + * @param $condition + * @param $attributes + * @return $this|bool + * @throws Exception + */ + private function update($attributes, $condition, $param) + { + if (empty($param)) { + return true; + } + $command = static::getDb(); + $change = $command->getSchema()->getChange(); + $sql = $change->update(static::getTable(), $attributes, $condition, $param); + + $trance = $command->beginTransaction(); + if (!($command = $command->createCommand($sql, $param)->save(false, $this->hasAutoIncrement()))) { + $trance->rollback(); + $result = $this->addError($this->getLastError()); + } else { + $result = static::populate($this->_attributes); + $trance->commit(); + $this->afterSave($attributes, $param); + } + return $result; + } + + /** + * @param null $data + * @return bool|mixed|ActiveRecord + * @throws Exception + */ + public function save($data = NULL) + { + $this->setAttributes($data); + if (!$this->validator($this->rules()) || !$this->beforeSave()) { + return false; + } + + $format = static::getColumns()->format(); + $this->_attributes = array_merge($format, $this->_attributes); + static::getDb()->enablingTransactions(); + + [$attributes, $condition, $param] = $this->filtration_and_separation(); + if (($primary = static::getPrimary()) !== null) { + $condition = [$primary => $this->getPrValue()]; + } + + if (!$this->getIsCreate()) { + return $this->update($param, $condition, $attributes); + } + return $this->insert($attributes, $param); + } + + + /** + * @param array $rule + * @return bool + * @throws Exception + */ + public function validator($rule) + { + if (empty($rule)) return true; + $validate = $this->resolve($rule); + if (!$validate->validation()) { + return $this->addError($validate->getError(), 'mysql'); + } else { + return TRUE; + } + } + + /** + * @param $rule + * @return Validator + * @throws Exception + */ + private function resolve($rule) + { + $validate = Validator::getInstance(); + $validate->setParams($this->_attributes); + $validate->setModel($this); + foreach ($rule as $Key => $val) { + $field = array_shift($val); + if (empty($val)) { + continue; + } + $validate->make($field, $val); + } + return $validate; + } + + /** + * @param string $name + * @return null + * @throws Exception + */ + public function getAttribute(string $name) + { + $method = 'get' . ucfirst($name) . 'Attribute'; + if (method_exists($this, $method)) { + return $this->$method($this->_attributes[$name]); + } + return $this->_attributes[$name] ?? null; + } + + + /** + * @return array + * @throws Exception + */ + private function filtration_and_separation() + { + $_tmp = []; + $condition = []; + $columns = static::getColumns(); + foreach ($this->_attributes as $key => $val) { + if ($val === NULL) continue; + $oldValue = $this->_oldAttributes[$key] ?? null; + if ($val !== $oldValue) { + $_tmp[$key] = $columns->fieldFormat($key, $val); + } else { + $condition[$key] = $val; + } + } + return [$_tmp, $condition, array_keys($_tmp)]; + } + + + /** + * @param $name + * @param $value + */ + public function setRelate($name, $value) + { + $this->_relate[$name] = $value; + } + + /** + * @param array $relates + */ + public function setRelates(array $relates) + { + if (empty($relates)) { + return; + } + foreach ($relates as $key => $val) { + $this->setRelate($key, $val); + } + } + + /** + * @return array + */ + public function getRelates() + { + return $this->_relate; + } + + + /** + * @return Relation + */ + public function getRelation() + { + return $this->_relation; + } + + + /** + * @param $name + * @return mixed|null + */ + public function getRelate($name) + { + if (!isset($this->_relate[$name])) { + return NULL; + } + return $this->_relate[$name]; + } + + + /** + * @param $attribute + * @return bool + * @throws Exception + */ + public function has($attribute) + { + $format = static::getColumns()->format(); + + return array_key_exists($attribute, $format); + } + + /**ƒ + * @return string + * @throws Exception + */ + public static function getTable() + { + $tablePrefix = static::getDb()->tablePrefix; + + $table = static::tableName(); + + if (strpos($table, $tablePrefix) === 0) { + return $table; + } + + if (empty($table)) { + $class = preg_replace('/model\\\/', '', get_called_class()); + $table = lcfirst($class); + } + + $table = trim($table, '{{%}}'); + if ($tablePrefix) { + $table = $tablePrefix . $table; + } + return $table; + } + + + /** + * @param $attributes + * @param $changeAttributes + * @return mixed + * @throws Exception + */ + public function afterSave($attributes, $changeAttributes) + { + return true; + } + + /** + * @return Connection + * @throws Exception + */ + public static function getDb() + { + return static::setDatabaseConnect('db'); + } + + /** + * @return mixed + * @throws Exception + */ + public function getPrValue() + { + return $this->getAttribute(static::getPrimary()); + } + + /** + * @return static + */ + public function refresh() + { + $this->_oldAttributes = $this->_attributes; + return $this; + } + + /** + * @param $name + * @param $value + * @throws Exception + */ + public function __set($name, $value) + { + if (!$this->has($name)) { + parent::__set($name, $value); + } else { + $sets = 'set' . ucfirst($name) . 'Attribute'; + if (method_exists($this, $sets)) { + $value = $this->$sets($value); + } + $this->_attributes[$name] = $value; + } + } + + /** + * @param $name + * @return mixed|null + * @throws Exception + */ + public function __get($name) + { + $method = 'get' . ucfirst($name); + if (method_exists($this, $method . 'Attribute')) { + return $this->{$method . 'Attribute'}($this->_attributes[$name] ?? null); + } + + if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) { + return stripcslashes($this->_attributes[$name]); + } + + if (isset($this->_relate[$name])) { + $gets = $this->{$this->_relate[$name]}(); + } else if (method_exists($this, $method)) { + $gets = $this->$method(); + } + + if (isset($gets)) { + return $this->resolveClass($gets); + } + + return parent::__get($name); + } + + /** + * @param $name + * @return mixed|null + */ + public function __isset($name) + { + return $this->_attributes[$name] ?? null; + } + + /** + * @param $call + * @return array|null|ActiveRecord + * @throws Exception + */ + private function resolveClass($call) + { + if ($call instanceof HasOne) { + return $call->get(); + } else if ($call instanceof HasMany) { + return $call->get(); + } else { + return $call; + } + } + + + /** + * @param mixed $offset + * @return bool + * @throws Exception + */ + public function offsetExists($offset) + { + return $this->has($offset); + } + + /** + * @param mixed $offset + * @return mixed|null + * @throws Exception + */ + public function offsetGet($offset) + { + return $this->__get($offset); + } + + /** + * @param mixed $offset + * @param mixed $value + * @throws Exception + */ + public function offsetSet($offset, $value) + { + return $this->__set($offset, $value); + } + + /** + * @param mixed $offset + * @throws Exception + */ + public function offsetUnset($offset) + { + if (!$this->has($offset)) { + return; + } + unset($this->_attributes[$offset]); + unset($this->_oldAttributes[$offset]); + if (isset($this->_relate)) { + unset($this->_relate[$offset]); + } + } + + /** + * @return array + */ + public function unset() + { + $fields = func_get_args(); + $fields = array_shift($fields); + if (!is_array($fields)) { + $fields = explode(',', $fields); + } + + $array = array_combine($fields, $fields); + + return array_diff_assoc($array, $this->_attributes); + } + + + /** + * @param $bsName + * @return mixed + * @throws Exception + */ + public static function setDatabaseConnect($bsName) + { + return Snowflake::$app->{$bsName}; + } + + /** + * @return Columns + * @throws Exception + */ + public static function getColumns() + { + return static::getDb()->getSchema() + ->getColumns() + ->table(static::getTable()); + } + + /** + * @param array $data + * @return static + * @throws + */ + public static function populate(array $data) + { + $model = new static(); + $model->setAttributes(self::parse($data)); + $model->setIsCreate(false); + $model->refresh(); + return $model; + } + + + /** + * @param $data + * @return array + * @throws Exception + */ + private static function parse($data) + { + return static::getColumns()->populate($data); + } +} diff --git a/Database/Base/ConditionClassMap.php b/Database/Base/ConditionClassMap.php new file mode 100644 index 00000000..29ccdd24 --- /dev/null +++ b/Database/Base/ConditionClassMap.php @@ -0,0 +1,72 @@ + [ + 'class' => InCondition::class + ], + 'NOT IN' => [ + 'class' => NotInCondition::class + ], + 'LIKE' => [ + 'class' => LikeCondition::class + ], + 'NOT LIKE' => [ + 'class' => NotLikeCondition::class + ], + 'LLike' => [ + 'class' => LLikeCondition::class + ], + 'RLike' => [ + 'class' => RLikeCondition::class + ], + 'EQ' => [ + 'class' => MathematicsCondition::class, + 'type' => 'EQ' + ], + 'NEQ' => [ + 'class' => MathematicsCondition::class, + 'type' => 'NEQ' + ], + 'GT' => [ + 'class' => MathematicsCondition::class, + 'type' => 'GT' + ], + 'EGT' => [ + 'class' => MathematicsCondition::class, + 'type' => 'EGT' + ], + 'LT' => [ + 'class' => MathematicsCondition::class, + 'type' => 'LT' + ], + 'ELT' => [ + 'class' => MathematicsCondition::class, + 'type' => 'ELT' + ], + 'BETWEEN' => BetweenCondition::class, + 'NOT BETWEEN' => NotBetweenCondition::class, + ]; + +} diff --git a/Database/Collection.php b/Database/Collection.php new file mode 100644 index 00000000..e8db24de --- /dev/null +++ b/Database/Collection.php @@ -0,0 +1,235 @@ +_item); + } + + /** + * @return array + */ + public function getItems() + { + return $this->_item; // TODO: Change the autogenerated stub + } + + /** + * @param $field + * + * @return array|null + * @throws Exception + */ + public function values($field) + { + if (empty($field) || !is_string($field)) { + return NULL; + } + $_tmp = []; + $data = $this->toArray(); + foreach ($data as $val) { + /** @var ActiveRecord $val */ + $_tmp[] = $val[$field]; + } + return $_tmp; + } + + /** + * @param string $field + * @return array|null + */ + public function keyBy(string $field) + { + $array = $this->toArray(); + $column = array_flip(array_column($array, $field)); + foreach ($column as $key => $value) { + $column[$key] = $array[$value]; + } + + return $column; + } + + /** + * @return $this + */ + public function orderRand() + { + shuffle($this->_item); + return $this; + } + + /** + * @param int $start + * @param int $length + * + * @return array + */ + public function slice($start = 0, $length = 20) + { + if (empty($this->_item) || !is_array($this->_item)) { + return []; + } + if (count($this->_item) < $length) { + return $this->_item; + } else { + return array_slice($this->_item, $start, $length); + } + } + + /** + * @param $field + * @param string $setKey + * + * @return array|null + */ + public function column($field, $setKey = '') + { + $data = $this->toArray(); + if (empty($data)) { + return []; + } + if (!empty($setKey) && is_string($setKey)) { + return array_column($data, $field, $setKey); + } else { + return array_column($data, $field); + } + } + + /** + * @param $field + * + * @return float|int|null + */ + public function sum($field) + { + $array = $this->column($field); + if (empty($array)) { + return NULL; + } + return array_sum($array); + } + + /** + * @return ActiveRecord|mixed + */ + public function current() + { + return current($this->_item); + } + + /** + * @return int + */ + public function size() + { + return (int)count($this->_item); + } + + /** + * @return array + * @throws + */ + public function toArray() + { + $array = []; + foreach ($this->_item as $value) { + if (!is_object($value)) { + continue; + } + $array[] = $value->toArray(); + } + $this->_item = []; + return $array; + } + + /** + * @throws Exception + * 批量删除 + */ + public function delete() + { + $model = $this->model; + if (!$model::hasPrimary()) { + return false; + } + $ids = []; + foreach ($this->_item as $item) { + $ids[] = $item->{$model::getPrimary()}; + } + $ids = array_filter($ids); + if (empty($ids)) { + return false; + } + return $this->model::find() + ->in($model::getPrimary(), $ids) + ->deleteAll(); + } + + /** + * @param array $condition + * @return Filters + * @throws + */ + public function filter(array $condition) + { + if (empty($condition)) { + return new Filters($this->_item); + } + $_filters = []; + foreach ($this->_item as $value) { + $_value = $value; + if ($_value instanceof ActiveRecord) { + $_value = $_value->toArray(); + } + $_tmp = array_intersect_key($_value, $condition); + if (count(array_diff_assoc($_tmp, $condition)) > 0) { + continue; + } + $_filters[] = $value; + } + return new Filters($_filters); + } + + /** + * @param $key + * @param $value + * @return ActiveRecord|mixed|null + */ + public function exists($key, $value) + { + foreach ($this->_item as $item) { + if ($item->$key === $value) { + return $item; + } + } + return null; + } + + /** + * @return bool + */ + public function isEmpty() + { + return $this->size() < 1; + } +} diff --git a/Database/Command.php b/Database/Command.php new file mode 100644 index 00000000..5ff3b875 --- /dev/null +++ b/Database/Command.php @@ -0,0 +1,394 @@ +connection = $this->db->getConnect($this->sql); + } + + /** + * 重新构建 + * @throws + */ + private function initPDOStatement() + { + if (empty($this->sql)) { + return; + } + if (($connect = $this->getConnection()) === false) { + throw new Exception('数据库繁忙, 请稍后再试!'); + } + $this->prepare = $connect->prepare($this->sql); + if (!($this->prepare instanceof PDOStatement)) { + throw new Exception($this->sql . ':' . $this->getError()); + } + } + + /** + * @return bool|PDOStatement + * @throws + */ + public function incrOrDecr() + { + return $this->execute(static::EXECUTE); + } + + /** + * @param bool $isInsert + * @param bool $hasAutoIncrement + * @return bool|string + * @throws + */ + public function save($isInsert = TRUE, $hasAutoIncrement = true) + { + return $this->execute(static::EXECUTE, $isInsert, $hasAutoIncrement); + } + + /** + * @param $model + * @param $attributes + * @param $condition + * @param $param + * @return Command + * @throws Exception + */ + public function update($model, $attributes, $condition, $param) + { + $change = $this->db->getSchema()->getChange(); + $sql = $change->update($model, $attributes, $condition, $param); + return $this->setSql($sql)->bindValues($param); + } + + + /** + * @param $tableName + * @param $attributes + * @param $condition + * @return Command + * @throws Exception + */ + public function batchUpdate($tableName, $attributes, $condition) + { + $change = $this->db->getSchema()->getChange(); + [$sql, $param] = $change->batchUpdate($tableName, $attributes, $condition); + return $this->setSql($sql)->bindValues($param); + } + + + /** + * @param $tableName + * @param $attributes + * @return Command + * @throws Exception + */ + public function batchInsert($tableName, $attributes) + { + $change = $this->db->getSchema()->getChange(); + $attribute_key = array_keys(current($attributes)); + [$sql, $param] = $change->batchInsert($tableName, $attribute_key, $attributes); + return $this->setSql($sql)->bindValues($param); + } + + /** + * @param $tableName + * @param $attributes + * @param $param + * @return Command + * @throws Exception + */ + public function insert($tableName, $attributes, $param) + { + $change = $this->db->getSchema()->getChange(); + $sql = $change->insert($tableName, $attributes, $param); + return $this->setSql($sql)->bindValues($param); + } + + /** + * @return bool|int + * @throws Exception + */ + public function all() + { + return $this->execute(static::FETCH_ALL); + } + + /** + * @param $tableName + * @param $param + * @param $condition + * @return Command + * @throws Exception + */ + public function mathematics($tableName, $param, $condition) + { + $change = $this->db->getSchema()->getChange(); + $sql = $change->mathematics($tableName, $param, $condition); + return $this->setSql($sql); + } + + /** + * @return array|mixed + * @throws Exception + */ + public function one() + { + return $this->execute(static::FETCH); + } + + /** + * @return bool|int + * @throws Exception + */ + public function fetchColumn() + { + return $this->execute(static::FETCH_COLUMN); + } + + /** + * @return bool|int + * @throws Exception + */ + public function rowCount() + { + return $this->execute(static::ROW_COUNT); + } + + /** + * @return bool|int + * @throws Exception + */ + public function flush() + { + return $this->execute(static::EXECUTE); + } + + /** + * @param $type + * @param $isInsert + * @param $hasAutoIncrement + * @return bool|int + * @throws Exception + */ + private function execute($type, $isInsert = null, $hasAutoIncrement = true) + { + try { + $time = microtime(true); + if ($type === static::EXECUTE) { + $result = $this->insert_or_change($isInsert, $hasAutoIncrement); + } else { + $result = $this->search($type); + } + if ($this->prepare instanceof PDOStatement) { + $this->prepare->closeCursor(); + } + $time = round(microtime(true) - $time, 6); + $this->debug($this->sql . ' Run-Time:' . $time); + } catch (\Throwable | Exception $exception) { + $this->error($this->sql . '. error: ' . $exception->getMessage()); + $result = null; + } finally { + return $result; + } + } + + /** + * @param $type + * @return array|int|mixed + * @throws Exception + */ + private function search($type) + { + $connect = $this->getConnection()->query($this->sql); + if ($type === static::ROW_COUNT) { + $result = $connect->rowCount(); + } else if ($type === static::FETCH_COLUMN) { + $result = $connect->fetchColumn(); + } else if ($type === static::FETCH_ALL) { + $result = $connect->fetchAll(PDO::FETCH_ASSOC); + } else { + $result = $connect->fetch(PDO::FETCH_ASSOC); + } + return $result; + } + + /** + * @param $isInsert + * @param $hasAutoIncrement + * @return bool|string + * @throws Exception + */ + private function insert_or_change($isInsert, $hasAutoIncrement) + { + $this->initPDOStatement(); + $this->bind($this->prepare); + if (($result = $this->prepare->execute()) === false) { + return $this->addErrorLog(); + } + if (!$isInsert) { + return true; + } + $result = $this->connection->lastInsertId(); + if ($result == 0 && $hasAutoIncrement) { + return $this->addErrorLog(); + } + return $result; + } + + /** + * @param $modelName + * @return $this + */ + public function setModelName($modelName) + { + $this->_modelName = $modelName; + return $this; + } + + /** + * @return string + */ + public function getModelName() + { + return $this->_modelName; + } + + /** + * @return bool|int + * @throws Exception + */ + public function delete() + { + return $this->execute(static::EXECUTE); + } + + /** + * @return bool|int + * @throws Exception + */ + public function exec() + { + return $this->execute(static::EXECUTE); + } + + /** + * @param PDOStatement $prepare + * @param $name + * @param $value + */ + public function bindParam($prepare, $name, $value) + { + $prepare->bindParam($name, $value); + } + + /** + * @param array|null $data + * @return $this + */ + public function bindValues(array $data = NULL) + { + if (!is_array($this->params)) { + $this->params = []; + } + if (!empty($data)) { + $this->params = array_merge($this->params, $data); + } + return $this; + } + + /** + * @param $sql + * @return $this + * @throws Exception + */ + public function setSql($sql) + { + $this->sql = $sql; + return $this; + } + + /** + * @return string + */ + public function getError() + { + return $this->prepare->errorInfo()[2] ?? 'Db 驱动错误.'; + } + + /** + * @return bool + * @throws Exception + */ + public function addErrorLog() + { + if ($this->prepare->errorCode() === '00000') { + return true; + } + return $this->addError($this->getError(), 'mysql'); + } + + /** + * @param PDOStatement $prepare + * @return PDOStatement + * @throws Exception + */ + private function bind($prepare) + { + if (empty($this->params)) { + return $prepare; + } + foreach ($this->params as $key => $val) { + if (is_array($val)) { + throw new Exception("Save data cannot have array"); + } + $this->bindParam($prepare, ':' . ltrim($key, ':'), $val); + } + return $prepare; + } +} diff --git a/Database/Condition/ArrayCondition.php b/Database/Condition/ArrayCondition.php new file mode 100644 index 00000000..5b932e83 --- /dev/null +++ b/Database/Condition/ArrayCondition.php @@ -0,0 +1,102 @@ +value instanceof Condition) { + return $this->value->builder(); + } + + $conditions = []; + + $classMap = ConditionClassMap::$conditionMap; + foreach ($this->value as $key => $value) { + if ($value === null) { + continue; + } + if ($value instanceof Condition) { + $value = $value->builder(); + } else if (isset($value[0]) && isset($classMap[strtoupper($value[0])])) { + $value = $this->buildOperaCondition($value); + } else { + $value = $this->buildHashCondition($key, $value); + } + if (empty($value)) { + continue; + } + $conditions[] = Str::encode($value); + } + if (is_array($conditions)) { + $conditions = implode(' AND ', $conditions); + } + return $conditions; + } + + /** + * @param $value + * @return bool + */ + private function isMath($value) + { + return isset($value[0]) && in_array($value[0], $this->math); + } + + /** + * @param array $value + * @return mixed + * @throws Exception + */ + public function buildOperaCondition($value) + { + [$option['opera'], $option['column'], $option['value']] = $value; + $strPer = strtoupper($option['opera']); + if (isset($this->conditionMap[$strPer])) { + $class = ConditionClassMap::$conditionMap[$strPer]; + if (!is_array($class)) { + $class = ['class' => $class]; + } + $option = array_merge($option, $class); + } else if ($value instanceof ActiveQuery) { + $option['value'] = $value->adaptation(); + $option['class'] = ChildCondition::class; + } else { + $option['class'] = DefaultCondition::class; + } + /** @var Condition $class */ + $class = Snowflake::createObject($option); + return $conditions[] = $class->builder(); + } + + /** + * @param $key + * @param $value + * @return string + */ + public function buildHashCondition($key, $value) + { + return $this->resolve($key, $value); + } + +} diff --git a/Database/Condition/BetweenCondition.php b/Database/Condition/BetweenCondition.php new file mode 100644 index 00000000..5624b4a2 --- /dev/null +++ b/Database/Condition/BetweenCondition.php @@ -0,0 +1,23 @@ +column . ' BETWEEN ' . $this->value[0] . ' AND ' . $this->value[1]; + } + +} diff --git a/Database/Condition/ChildCondition.php b/Database/Condition/ChildCondition.php new file mode 100644 index 00000000..5b733d58 --- /dev/null +++ b/Database/Condition/ChildCondition.php @@ -0,0 +1,22 @@ +column . ' ' . $this->opera . ' (' . $this->value . ')'; + } + +} diff --git a/Database/Condition/Condition.php b/Database/Condition/Condition.php new file mode 100644 index 00000000..00bfc18f --- /dev/null +++ b/Database/Condition/Condition.php @@ -0,0 +1,142 @@ +column = $column; + } + + /** + * @param string $opera + */ + public function setOpera(string $opera): void + { + $this->opera = $opera; + } + + /** + * @param $value + */ + public function setValue($value): void + { + $this->value = $value; + } + + /** + * @param $column + * @param $value + * @param $oprea + * + * @return string + * + * $query = new Build(); + * $query->where('id', '2'); + * $query->where(['id' => 3]); + * $query->where('id', '<', 4); + * $query->orWhere('id', '=', 5); + * $query->orWhere('id', '=', 6); + * $query->ANDWhere('id', '=', 7); + * $sql = '(((id=2 AND id=3 AND id<4) OR id=5) OR id=6) AND i(d=7)'; + */ + protected function resolve($column, $value = null, $oprea = '=') + { + if ($value === NULL) { + return ''; + } + + $value = Str::encode($value); + if (trim($oprea) == 'like') { + return $column . ' ' . $oprea . ' \'%' . $value . '%\''; + } + $columns = $this->column[$column] ?? ''; + if (empty($columns)) { + return $this->typeBuilder($column, $value, $oprea); + } + + $explode = explode('(', $columns); + $explode = array_shift($explode); + if (strpos($explode, ' ') !== false) { + $explode = explode(' ', $explode)[0]; + } + if (!in_array(trim($explode), static::INT_TYPE)) { + $str = $column . ' ' . $oprea . ' \'' . $value . '\''; + } else { + $str = $column . ' ' . $oprea . ' ' . $value; + } + return $str; + } + + + /** + * @param array $param + * @return array + */ + protected function format($param) + { + if (!is_array($param)) { + return null; + } + $_tmp = []; + foreach ($param as $value) { + if ($value === null) { + continue; + } + $value = Str::encode($value); + if (is_numeric($value)) { + $_tmp[] = Str::encode($value); + } else { + $_tmp[] = '\'' . Str::encode($value) . '\''; + } + } + return $_tmp; + } + + + /** + * @param $column + * @param null $value + * @param string $oprea + * @return string + */ + public function typeBuilder($column, $value = null, $oprea = '=') + { + if (is_numeric($value)) { + if ($value != (int)$value) { + return $column . ' ' . $oprea . ' \'' . $value . '\''; + } + return $column . ' ' . $oprea . ' ' . $value; + } else { + $encode = '\'' . Str::encode($value) . '\''; + return $column . ' ' . $oprea . ' ' . $encode; + } + } + +} diff --git a/Database/Condition/DefaultCondition.php b/Database/Condition/DefaultCondition.php new file mode 100644 index 00000000..b990f7d6 --- /dev/null +++ b/Database/Condition/DefaultCondition.php @@ -0,0 +1,22 @@ +resolve($this->column, $this->value, $this->opera); + } + +} diff --git a/Database/Condition/HashCondition.php b/Database/Condition/HashCondition.php new file mode 100644 index 00000000..5f2239cc --- /dev/null +++ b/Database/Condition/HashCondition.php @@ -0,0 +1,30 @@ +value)) { + return ''; + } + foreach ($this->value as $key => $value) { + if ($value === null) { + continue; + } + $array[] = $this->resolve($key, $value); + } + return implode(' AND ', $array); + } + +} diff --git a/Database/Condition/InCondition.php b/Database/Condition/InCondition.php new file mode 100644 index 00000000..67a76447 --- /dev/null +++ b/Database/Condition/InCondition.php @@ -0,0 +1,34 @@ +value instanceof ActiveQuery) { + $this->value = $this->value->getBuild()->getQuery($this->value); + } else { + $this->value = array_filter($this->format($this->value)); + if (empty($this->value)) { + return ''; + } + $this->value = implode(',', $this->value); + } + return '`' . $this->column . '` in(' . $this->value . ')'; + } + +} diff --git a/Database/Condition/LLikeCondition.php b/Database/Condition/LLikeCondition.php new file mode 100644 index 00000000..95018e74 --- /dev/null +++ b/Database/Condition/LLikeCondition.php @@ -0,0 +1,29 @@ +value)) { + $this->value = array_shift($this->value); + } + $this->value = Str::encode($this->value); + return $this->column . ' LIKE \'%' . $this->value . '\''; + } + +} diff --git a/Database/Condition/LikeCondition.php b/Database/Condition/LikeCondition.php new file mode 100644 index 00000000..610f7ef9 --- /dev/null +++ b/Database/Condition/LikeCondition.php @@ -0,0 +1,29 @@ +value)) { + $this->value = array_shift($this->value); + } + $this->value = Str::encode($this->value); + return $this->column . ' LIKE \'%' . $this->value . '%\''; + } + +} diff --git a/Database/Condition/MathematicsCondition.php b/Database/Condition/MathematicsCondition.php new file mode 100644 index 00000000..b1b283ff --- /dev/null +++ b/Database/Condition/MathematicsCondition.php @@ -0,0 +1,73 @@ +value = (float)$this->value; + return $this->{strtolower($this->type)}(); + } + + /** + * @return string + */ + public function eq() + { + return $this->column . ' = ' . $this->value; + } + + /** + * @return string + */ + public function neq() + { + return $this->column . ' <> ' . $this->value; + } + + /** + * @return string + */ + public function gt() + { + return $this->column . ' > ' . $this->value; + } + + /** + * @return string + */ + public function egt() + { + return $this->column . ' >= ' . $this->value; + } + + + /** + * @return string + */ + public function lt() + { + return $this->column . ' < ' . $this->value; + } + + /** + * @return string + */ + public function elt() + { + return $this->column . ' <= ' . $this->value; + } + +} diff --git a/Database/Condition/NotBetweenCondition.php b/Database/Condition/NotBetweenCondition.php new file mode 100644 index 00000000..7ff5b8b9 --- /dev/null +++ b/Database/Condition/NotBetweenCondition.php @@ -0,0 +1,22 @@ +column . ' NOT BETWEEN ' . $this->value[0] . ' AND ' . $this->value[1]; + } + +} diff --git a/Database/Condition/NotInCondition.php b/Database/Condition/NotInCondition.php new file mode 100644 index 00000000..1af8062c --- /dev/null +++ b/Database/Condition/NotInCondition.php @@ -0,0 +1,28 @@ +format($this->value)); + if (empty($format)) { + return ''; + } + + return '`' . $this->column . '` not in(' . implode(',', $format) . ')'; + } + +} diff --git a/Database/Condition/NotLikeCondition.php b/Database/Condition/NotLikeCondition.php new file mode 100644 index 00000000..dc29f57d --- /dev/null +++ b/Database/Condition/NotLikeCondition.php @@ -0,0 +1,29 @@ +value)) { + $this->value = array_shift($this->value); + } + $this->value = Str::encode($this->value); + return $this->column . ' NOT LIKE \'%' . $this->value . '%\''; + } + +} diff --git a/Database/Condition/OrCondition.php b/Database/Condition/OrCondition.php new file mode 100644 index 00000000..fb1c7d45 --- /dev/null +++ b/Database/Condition/OrCondition.php @@ -0,0 +1,23 @@ +resolve($this->column, $this->value, $this->opera); + } + +} diff --git a/Database/Condition/RLikeCondition.php b/Database/Condition/RLikeCondition.php new file mode 100644 index 00000000..c02fb86a --- /dev/null +++ b/Database/Condition/RLikeCondition.php @@ -0,0 +1,29 @@ +value)) { + $this->value = array_shift($this->value); + } + $this->value = Str::encode($this->value); + return $this->column . ' LIKE \'' . $this->value . '%\''; + } + +} diff --git a/Database/Connection.php b/Database/Connection.php new file mode 100644 index 00000000..80f3a866 --- /dev/null +++ b/Database/Connection.php @@ -0,0 +1,332 @@ + 'mysql:dbname=dbname;host=127.0.0.1', 'username' => 'root', 'password' => 'root'], + * ['cds' => 'mysql:dbname=dbname;host=127.0.0.1', 'username' => 'root', 'password' => 'root'], + * ['cds' => 'mysql:dbname=dbname;host=127.0.0.1', 'username' => 'root', 'password' => 'root'], + * ['cds' => 'mysql:dbname=dbname;host=127.0.0.1', 'username' => 'root', 'password' => 'root'], + * ] + */ + public $masterConfig = []; + + /** + * @var bool + * enable database cache + */ + public $enableCache = false; + public $cacheDriver = 'redis'; + + /** + * @var array + * + * @example [ + * ['cds' => 'mysql:dbname=dbname;host=127.0.0.1', 'username' => 'root', 'password' => 'root'], + * ['cds' => 'mysql:dbname=dbname;host=127.0.0.1', 'username' => 'root', 'password' => 'root'], + * ['cds' => 'mysql:dbname=dbname;host=127.0.0.1', 'username' => 'root', 'password' => 'root'], + * ['cds' => 'mysql:dbname=dbname;host=127.0.0.1', 'username' => 'root', 'password' => 'root'], + * ] + */ + public $slaveConfig = []; + + /** @var Schema $_schema */ + private $_schema = null; + + + /** + * @throws Exception + */ + public function init() + { + $event = Snowflake::get()->event; + $event->on(Event::RELEASE_ALL, [$this, 'disconnect']); + $event->on(Event::EVENT_AFTER_REQUEST, [$this, 'clear_connection']); + } + + + /** + * @throws Exception + */ + public function enablingTransactions() + { + if (!Db::transactionsActive()) { + return; + } + $this->beginTransaction(); + + $event = Snowflake::get()->event; + $event->on(Connection::TRANSACTION_COMMIT, [$this, 'commit'], false, true); + $event->on(Connection::TRANSACTION_ROLLBACK, [$this, 'rollback'], false, true); + } + + /** + * @param null $sql + * @return PDO + * @throws Exception + */ + public function getConnect($sql = NULL) + { + $connections = Snowflake::get()->connections; + $connections->initConnections($this->cds, true, $this->maxNumber); + $connections->initConnections($this->slaveConfig['cds'], false, $this->maxNumber); + $connections->setTimeout($this->timeout); + + return $this->getPdo($sql); + } + + /** + * @param $sql + * @return PDO + * @throws Exception + */ + private function getPdo($sql) + { + if ($this->isWrite($sql)) { + $connect = $this->masterInstance(); + } else { + $connect = $this->slaveInstance(); + } + return $connect; + } + + /** + * @return mixed|object|Schema + * @throws Exception + */ + public function getSchema() + { + if ($this->_schema === null) { + $this->_schema = Snowflake::createObject([ + 'class' => Schema::class, + 'db' => $this + ]); + } + return $this->_schema; + } + + /** + * @param $sql + * @return bool + */ + public function isWrite($sql) + { + if (empty($sql)) return false; + + $prefix = strtolower(mb_substr($sql, 0, 6)); + + return in_array($prefix, ['insert', 'update', 'delete']); + } + + /** + * @return mixed|null + * @throws ComponentException + */ + public function getCacheDriver() + { + if (!$this->enableCache) { + return null; + } + return Snowflake::get()->get($this->cacheDriver); + } + + /** + * @return PDO + * @throws Exception + */ + public function masterInstance() + { + $config = [ + 'cds' => $this->cds, + 'username' => $this->username, + 'password' => $this->password + ]; + $pool = Snowflake::get()->connections; + return $pool->getConnection($config, true); + } + + + /** + * @return PDO + * @throws Exception + */ + public function slaveInstance() + { + if (empty($this->slaveConfig)) { + return $this->masterInstance(); + } + + $connections = Snowflake::get()->connections; + return $connections->getConnection($this->slaveConfig, false); + } + + + /** + * @return $this + * @throws Exception + */ + public function beginTransaction() + { + $connections = Snowflake::get()->connections; + $connections->beginTransaction($this->cds); + return $this; + } + + /** + * @return $this|bool + * @throws Exception + */ + public function inTransaction() + { + $connections = Snowflake::get()->connections; + return $connections->inTransaction($this->cds); + } + + /** + * @throws Exception + * 事务回滚 + */ + public function rollback() + { + $connections = Snowflake::get()->connections; + return $connections->rollback($this->cds); + } + + /** + * @throws Exception + * 事务提交 + */ + public function commit() + { + $connections = Snowflake::get()->connections; + return $connections->commit($this->cds); + } + + /** + * @param $sql + * @return PDO + * @throws Exception + */ + public function refresh($sql) + { + if ($this->isWrite($sql)) { + $instance = $this->masterInstance(); + } else { + $instance = $this->slaveInstance(); + } + return $instance; + } + + /** + * @param $sql + * @param array $attributes + * @return Command + * @throws + */ + public function createCommand($sql = null, $attributes = []) + { + $command = new Command([ + 'db' => $this + ]); + $command->setSql($sql); + return $command->bindValues($attributes); + } + + /** + * @return Select + * @throws Exception + */ + public function getBuild() + { + return $this->getSchema()->getQueryBuilder(); + } + + /** + * + * 回收链接 + * @throws + */ + public function release() + { + $connections = Snowflake::get()->connections; + + $connections->release($this->cds, true); + $connections->release($this->slaveConfig['cds'], false); + } + + + /** + * 临时回收 + */ + public function recovery() + { + $connections = Snowflake::get()->connections; + + $connections->release($this->cds, true); + $connections->release($this->slaveConfig['cds'], false); + } + + /** + * + * 回收链接 + * @throws + */ + public function clear_connection() + { + $connections = Snowflake::get()->connections; + + $connections->release($this->cds, true); + $connections->release($this->slaveConfig['cds'], false); + } + + + /** + * @throws Exception + */ + public function disconnect() + { + $connections = Snowflake::get()->connections; + $connections->disconnect($this->cds); + $connections->disconnect($this->slaveConfig['cds']); + } + +} diff --git a/Database/Db.php b/Database/Db.php new file mode 100644 index 00000000..de97a3c3 --- /dev/null +++ b/Database/Db.php @@ -0,0 +1,328 @@ +event; + $event->trigger(Connection::TRANSACTION_COMMIT); + $event->offName(Connection::TRANSACTION_COMMIT); + static::$isActive = false; + } + + /** + * @throws Exception + */ + public static function rollback() + { + $event = Snowflake::get()->event; + $event->trigger(Connection::TRANSACTION_ROLLBACK); + $event->offName(Connection::TRANSACTION_ROLLBACK); + static::$isActive = false; + } + + /** + * @param $table + * + * @return static + */ + public static function table($table) + { + $db = new Db(); + $db->from($table); + return $db; + } + + /** + * @param string $column + * @param string $alias + * @return string + */ + public static function any_value(string $column, string $alias = '') + { + if (empty($alias)) { + $alias = $column . '_any_value'; + } + return 'ANY_VALUE(' . $column . ') as ' . $alias; + } + + /** + * @param Connection|null $db + * @return mixed + * @throws Exception + */ + public function get(Connection $db = NULL) + { + if (empty($db)) { + $db = Snowflake::get()->database; + } + $query = $db->getSchema()->getQueryBuilder(); + return $db->createCommand($query->getQuery($this)) + ->all(); + } + + /** + * @param $column + * @return string + */ + public static function raw($column) + { + return '`' . $column . '`'; + } + + /** + * @param Connection $db + * @return array|mixed + * @throws Exception + */ + public function find(Connection $db = NULL) + { + if (empty($db)) { + $db = Snowflake::get()->database; + } + $query = $db->getSchema()->getQueryBuilder(); + return $db->createCommand($query->getQuery($this)) + ->one(); + } + + /** + * @param Connection|NULL $db + * @return bool|int + * @throws Exception + */ + public function count(Connection $db = NULL) + { + if (empty($db)) { + $db = Snowflake::get()->database; + } + $query = $db->getSchema()->getQueryBuilder(); + return $db->createCommand($query->count($this)) + ->rowCount(); + } + + /** + * @param Connection|NULL $db + * @return bool|int + * @throws Exception + */ + public function exists(Connection $db = NULL) + { + if (empty($db)) { + $db = Snowflake::get()->database; + } + $query = $db->getSchema()->getQueryBuilder(); + return $db->createCommand($query->getQuery($this)) + ->fetchColumn(); + } + + /** + * @param string $sql + * @param array $attributes + * @param Connection|null $db + * @return array|bool|int|string|null + * @throws Exception + */ + public static function findAllBySql(string $sql, array $attributes = [], Connection $db = NULL) + { + return $db->createCommand($sql, $attributes)->all(); + } + + /** + * @param string $sql + * @param array $attributes + * @param Connection|NULL $db + * @return array|mixed + * @throws Exception + */ + public static function findBySql(string $sql, array $attributes = [], Connection $db = NULL) + { + return $db->createCommand($sql, $attributes)->one(); + } + + /** + * @param string $field + * @return array|null + * @throws Exception + */ + public function values(string $field) + { + $data = $this->get(); + if (empty($data) || empty($field)) { + return NULL; + } + $first = current($data); + if (!isset($first[$field])) { + return NULL; + } + return array_column($data, $field); + } + + /** + * @param $field + * @return array|mixed|null + * @throws Exception + */ + public function value($field) + { + $data = $this->find(); + if (!empty($field) && isset($data[$field])) { + return $data[$field]; + } + return $data; + } + + /** + * @param null $db + * @return bool|int + * @throws Exception + */ + public function delete($db = null) + { + if (empty($db)) { + $db = Snowflake::get()->database; + } + + $query = $db->getBuild()->builder($this); + + return $db->createCommand($query)->delete(); + } + + /** + * @param $table + * @param null $db + * @return bool|int + * @throws Exception + */ + public static function drop($table, $db = null) + { + if (empty($db)) { + $db = Snowflake::get()->database; + } + return $db->createCommand('DROP TABLE ' . $table)->delete(); + } + + /** + * @param $table + * @param null $db + * @return bool|int + * @throws Exception + */ + public static function truncate($table, $db = null) + { + + if (empty($db)) { + $db = Snowflake::get()->database; + } + + return $db->createCommand('TRUNCATE ' . $table)->exec(); + } + + /** + * @param $table + * @param Connection|NULL $db + * @return array|mixed|null + * @throws Exception + */ + public static function showCreateSql($table, Connection $db = NULL) + { + + if (empty($db)) { + $db = Snowflake::get()->database; + } + + + if (empty($table)) { + return null; + } + + return $db->createCommand('SHOW CREATE TABLE ' . $table)->one(); + } + + /** + * @param $table + * @param Connection|NULL $db + * @return bool|int|null + * @throws Exception + */ + public static function desc($table, Connection $db = NULL) + { + if (empty($db)) { + $db = Snowflake::get()->database; + } + + if (empty($table)) { + return null; + } + + return $db->createCommand('SHOW FULL FIELDS FROM ' . $table)->all(); + } + + + /** + * @param string $table + * @param Connection|NULL $db + * @return array|mixed|null + * @throws Exception + */ + public static function show(string $table, Connection $db = NULL) + { + if (empty($table)) { + return null; + } + + if (empty($db)) { + $db = Snowflake::get()->database; + } + + $table = [' const TABLE = \'select * from %s where REFERENCED_TABLE_NAME=%s\';']; + + return $db->createCommand((new Sql()) + ->select('*') + ->from('INFORMATION_SCHEMA.KEY_COLUMN_USAGE') + ->where(['REFERENCED_TABLE_NAME' => $table]) + ->getSql())->one(); + } + +} diff --git a/Database/Filters.php b/Database/Filters.php new file mode 100644 index 00000000..c938fdb1 --- /dev/null +++ b/Database/Filters.php @@ -0,0 +1,41 @@ +_filters = $data; + } + + /** + * @return Collection + */ + public function get() + { + return new Collection($this->_filters); + } + + /** + * @return int + */ + public function count() + { + return count($this->_filters); + } + +} diff --git a/Database/HasBase.php b/Database/HasBase.php new file mode 100644 index 00000000..fe6fb21d --- /dev/null +++ b/Database/HasBase.php @@ -0,0 +1,75 @@ +in($primaryId, $value); + } else { + $_model = $model::find()->where(['t1.' . $primaryId => $value]); + } + + $this->_relation = $relation->bindIdentification($model, $_model); + + $this->model = $model; + $this->primaryId = $primaryId; + $this->value = $value; + } + + abstract public function get(); + + /** + * @param $name + * @return mixed + */ + public function __get($name) + { + if (empty($this->value)) { + return null; + } + return $this->get(); + } +} diff --git a/Database/HasCount.php b/Database/HasCount.php new file mode 100644 index 00000000..149433c3 --- /dev/null +++ b/Database/HasCount.php @@ -0,0 +1,36 @@ +_relation->getQuery($this->model::className())->$name(...$arguments); + return $this; + } + + /** + * @return array|null|ActiveRecord + * @throws Exception + */ + public function get() + { + return $this->_relation->count($this->model::className(), $this->value); + } + +} diff --git a/Database/HasMany.php b/Database/HasMany.php new file mode 100644 index 00000000..4ffc9ab4 --- /dev/null +++ b/Database/HasMany.php @@ -0,0 +1,42 @@ +_relation->getQuery($this->model::className())->$name(...$arguments); + return $this; + } + + /** + * @return array|null|ActiveRecord + * @throws Exception + */ + public function get() + { + return $this->_relation->get($this->model::className(), $this->value); + } +} diff --git a/Database/HasOne.php b/Database/HasOne.php new file mode 100644 index 00000000..8830da5b --- /dev/null +++ b/Database/HasOne.php @@ -0,0 +1,41 @@ +_relation->getQuery($this->model::className())->$name(...$arguments); + return $this; + } + + /** + * @return array|null|ActiveRecord + * @throws Exception + */ + public function get() + { + return $this->_relation->first($this->model::className(), $this->value); + } +} diff --git a/Database/IOrm.php b/Database/IOrm.php new file mode 100644 index 00000000..efee2465 --- /dev/null +++ b/Database/IOrm.php @@ -0,0 +1,36 @@ +structure($this->table = $table); + } + + /** + * @return string + */ + public function getTable() + { + return $this->table; + } + + /** + * @param $key + * @param $val + * @return float|int|mixed|string + * @throws Exception + */ + public function fieldFormat($key, $val) + { + return $this->encode($val, $this->get_fields($key)); + } + + /** + * @param $data + * @return array + * @throws + */ + public function populate($data) + { + $column = $this->get_fields(); + foreach ($data as $key => $val) { + if (!isset($column[$key])) { + continue; + } + $data[$key] = $this->decode($val, $column[$key]); + } + return $data; + } + + /** + * @param $val + * @param $format + * @return float|int|mixed|string + * @throws + */ + public function decode($val, $format = null) + { + if (empty($format)) { + return $val; + } + $format = strtolower($format); + if ($this->isInt($format)) { + return (int)$val; + } else if ($this->isJson($format)) { + return JSON::decode($val, true); + } else if ($this->isFloat($format)) { + return (float)$val; + } else { + return stripslashes($val); + } + } + + /** + * @param $val + * @param $format + * @return float|int|mixed|string + * @throws + */ + public function encode($val, $format = null) + { + if (empty($format)) { + return $val; + } + $format = strtolower($format); + if ($this->isInt($format)) { + return (int)$val; + } else if ($this->isJson($format)) { + return JSON::encode($val); + } else if ($this->isFloat($format)) { + return (float)$val; + } else { + return addslashes($val); + } + } + + /** + * @param $format + * @return bool + */ + public function isInt($format) + { + return in_array($format, ['int', 'bigint', 'tinyint', 'smallint', 'mediumint']); + } + + /** + * @param $format + * @return bool + */ + public function isFloat($format) + { + return in_array($format, ['float', 'double', 'decimal']); + } + + /** + * @param $format + * @return bool + */ + public function isJson($format) + { + return in_array($format, ['json']); + } + + /** + * @param $format + * @return bool + */ + public function isString($format) + { + return in_array($format, ['varchar', 'char', 'text', 'longtext', 'tinytext', 'mediumtext']); + } + + + /** + * @return array + * @throws + */ + public function format() + { + return $this->columns('Default', 'Field'); + } + + /** + * @return int|string|null + * @throws Exception + */ + public function getAutoIncrement() + { + return $this->_auto_increment[$this->table] ?? null; + } + + /** + * @return array|null|string + * + * @throws Exception + */ + public function getPrimaryKeys() + { + if (isset($this->_auto_increment[$this->table])) { + return $this->_auto_increment[$this->table]; + } + return $this->_primary[$this->table] ?? null; + } + + /** + * @param $name + * @param null $index + * @return array + * @throws Exception + */ + private function columns($name, $index = null) + { + if (empty($index)) { + return array_column($this->getColumns(), $name); + } else { + return array_column($this->getColumns(), $name, $index); + } + } + + /** + * @return array|bool|int|mixed|string + * @throws Exception + */ + private function getColumns() + { + return $this->structure($this->getTable())->columns[$this->getTable()]; + } + + + /** + * @param $table + * @return $this + * @throws Exception + */ + private function structure($table) + { + if (isset($this->columns[$table])) { + return $this; + } + $sql = $this->db->getBuild()->getColumn($table); + $column = $this->db->createCommand($sql)->all(); + if (empty($column)) { + throw new Exception("The table " . $table . " not exists."); + } + foreach ($column as $key => $item) { + $column[$key]['Type'] = $this->clean($item['Type']); + if ($item['Key'] === 'PRI') { + if (!isset($this->_primary[$table])) { + $this->_primary[$table] = []; + } + $this->_primary[$table][] = $item['Field']; + } + if ($item['Extra'] === 'auto_increment') { + $this->_auto_increment[$table] = $item['Field']; + } + } + $this->columns[$table] = $column; + return $this; + } + + + /** + * @param $type + * @return string + */ + private function clean($type) + { + if (strpos($type, ')') === false) { + return $type; + } + + $replace = preg_replace('/\(\d+(,\d+)?\)(\s+\w+)*/', '', $type); + if (strpos(' ', $replace) !== FALSE) { + $replace = explode(' ', $replace)[1]; + } + return $replace; + } + + /** + * @param $field + * @return array|string + * @throws Exception + */ + public function get_fields($field = null) + { + $fields = $this->columns('Type', 'Field'); + if (empty($field)) { + return $fields; + } + if (!isset($fields[$field])) { + return null; + } + return strtolower($fields[$field]); + } + +} diff --git a/Database/Mysql/Schema.php b/Database/Mysql/Schema.php new file mode 100644 index 00000000..7a770c7e --- /dev/null +++ b/Database/Mysql/Schema.php @@ -0,0 +1,64 @@ +_builder === null) { + $this->_builder = new Select(); + } + return $this->_builder; + } + + /** + * @return Change + */ + public function getChange() + { + if ($this->_change === null) { + $this->_change = new Change(); + } + return $this->_change; + } + + + /** + * @return Columns + */ + public function getColumns() + { + if ($this->_column === null) { + $this->_column = new Columns(['db' => $this->db]); + } + + return $this->_column; + } +} diff --git a/Database/Orm/Change.php b/Database/Orm/Change.php new file mode 100644 index 00000000..c139ef4d --- /dev/null +++ b/Database/Orm/Change.php @@ -0,0 +1,276 @@ +builderWhere($condition); + } + return "UPDATE " . $model . ' SET ' . $where; + } + + /** + * @param string $table + * @param array $attributes + * @param $condition + * @return array|string + * @throws + */ + public function batchUpdate(string $table, array $attributes, $condition) + { + $param = []; + $_attributes = []; + foreach ($attributes as $key => $val) { + if ($val === null) { + continue; + } + $_attributes[':' . $key] = $this->valueEncode($val, true); + $param[] = $key . '=:' . $key; + } + if (empty($param)) { + return ''; + } + $param = implode(',', $param); + if (!empty($condition)) { + $param .= $condition; + } + return ['UPDATE ' . $table . ' SET ' . $param, $_attributes]; + } + + /** + * @param $table + * @param $params + * @param $condition + * @return string + * @throws Exception + */ + public function mathematics($table, $params, $condition) + { + $_tmp = $newParam = []; + if (isset($params['incr']) && is_array($params['incr'])) { + $_tmp = $this->assemble($params['incr'], ' + ', $_tmp); + } + if (isset($params['decr']) && is_array($params['decr'])) { + $_tmp = $this->assemble($params['decr'], ' - ', $_tmp); + } + if (empty($_tmp)) { + throw new Exception('Not has IncrBy or DecrBy values.'); + } + $_tmp = implode(',', $_tmp); + if (!empty($condition)) { + $_tmp .= $this->builderWhere($condition); + } + return 'UPDATE ' . $table . ' SET ' . $_tmp; + } + + /** + * @param $params + * @param $op + * @param array $_tmp + * @return array + * @throws Exception + */ + private function assemble($params, $op, $_tmp) + { + $message = 'Incr And Decr action. The value must a numeric.'; + foreach ($params as $key => $val) { + $_tmp[] = $key . '=' . $key . $op . $val; + if (!is_numeric($val)) { + throw new Exception($message); + } + } + + return $_tmp; + } + + /** + * @param $table + * @param array $params + * @return string + */ + public function insertOrUpdateByDUPLICATE($table, array $params) + { + $keys = implode(',', array_keys($params)); + + $onValues = []; + $values = array_values($params); + foreach ($values as $key => $val) { + $onValues[] = $this->valueEncode($val, true); + } + + $onUpdates = []; + foreach ($params as $key => $val) { + $onUpdates[] = $key . '=' . $this->valueEncode($val, true); + } + $newSql = $this->inserts($table, $keys, '(' . implode(',', $onValues) . ')'); + + return $newSql . ' ON DUPLICATE KEY UPDATE ' . implode(',', $onUpdates); + } + + /** + * @param $table + * @param $attributes + * @param array|null $params + * @return string + * @throws Exception + */ + public function insert($table, $attributes, array $params = NULL) + { + $sql = $this->inserts($table, implode(',', $attributes), '(:' . implode(',:', $attributes) . ')'); + if (empty($params)) { + throw new Exception("save data param not find."); + } + foreach ($params as $key => $val) { + if (strpos($sql, ':' . $key) === FALSE) { + throw new Exception("save $key data param not find."); + } + } + return $sql; + } + + + /** + * @param $table + * @param $attributes + * @param array|NULL $params + * @return array + * @throws Exception + */ + public function batchInsert($table, $attributes, array $params = NULL) + { + if (empty($params)) { + throw new Exception("save data param not find."); + } + $insert = $insertData = []; + foreach ($params as $key => $val) { + if (!is_array($val)) { + continue; + } + array_push($insert, '(:' . implode($key . ',:', $attributes) . $key . ')'); + foreach ($attributes as $myVal) { + $insertData[':' . $myVal . $key] = $this->valueEncode($val[$myVal], true); + } + } + if (empty($insertData) || empty($insert)) { + throw new Exception("save data is empty."); + } + $sql = $this->inserts($table, implode(',', $attributes), implode(',', $insert)); + return [$sql, $insertData]; + } + + + /** + * @param $table + * @param $fields + * @param $data + * @return string + * 构建SQL语句 + */ + private function inserts($table, $fields, $data) + { + $query = [ + 'INSERT IGNORE INTO', '%s', '(%s)', 'VALUES %s' + ]; + $query = implode(' ', $query); + + return sprintf($query, $table, $fields, $data); + } + + + /** + * @param $table + * @param $attributes + * @param $condition + * @return bool|string + * @throws Exception + */ + public function updateAll($table, $attributes, $condition) + { + $param = []; + foreach ($attributes as $key => $val) { + if ($val === null || $val === '') { + continue; + } + $param[] = $key . '=' . $this->valueEncode($val); + } + if (empty($param)) return true; + + $param = implode(',', $param); + if (!empty($condition)) { + $param .= $this->builderWhere($condition); + } + return 'UPDATE ' . $table . ' SET ' . $param; + } + + + /** + * @param ActiveQuery $query + * @return string + * @throws Exception + */ + public function delete($query) + { + if (empty($query->from)) { + $query->from = $query->getTable(); + } + + $condition = $this->builderWhere($query->where); + if (empty($condition) && !$query->ifNotWhere) { + throw new Exception('clear data must has condition.'); + } + $query = $this->builderFrom($query->from) . $condition; + + return 'DELETE ' . $query; + } + + /** + * @param string $tableName + * @return string + * @throws + */ + public function truncate($tableName) + { + return 'TRUNCATE ' . $tableName; + } + + +} diff --git a/Database/Orm/Condition.php b/Database/Orm/Condition.php new file mode 100644 index 00000000..58a0cb97 --- /dev/null +++ b/Database/Orm/Condition.php @@ -0,0 +1,211 @@ +builderWhere($query); + } + + + /** + * @param $alias + * @return string + */ + private function builderAlias($alias) + { + return " AS " . $alias; + } + + /** + * @param $table + * @return string + * @throws Exception + */ + private function builderFrom($table) + { + if ($table instanceof ActiveQuery) { + $table = '(' . $table->getBuild()->getQuery($table) . ')'; + } + return " FROM " . $table; + } + + /** + * @param $join + * @return string + */ + private function builderJoin($join) + { + if (!empty($join)) { + return ' ' . implode(' ', $join); + } + return ''; + } + + /** + * @param $where + * @return string + * @throws Exception + * ['id=1', 'a'=>2, ['in', 'a', 'b']] + */ + private function builderWhere($where) + { + if (empty($where)) { + return ''; + } + if (is_string($where)) { + return sprintf(' WHERE %s', $where); + } + + $_tmp = []; + foreach ($where as $key => $value) { + if (is_array($value)) { + $value = $this->arrayMap($value); + } else if (!is_numeric($key)) { + $value = $key . '=' . $this->valueEncode($value); + } + if (empty($value)) { + continue; + } + $_tmp[] = $value; + } + + if (!empty($_tmp)) { + return ' WHERE ' . implode(' AND ', $_tmp); + } + return ''; + } + + /** + * @param $value + * @return mixed|object|string|null + * @throws Exception + */ + private function arrayMap($value) + { + $classMap = ConditionClassMap::$conditionMap; + if (isset($value[0])) { + $value[0] = strtoupper($value[0]); + if (!isset($classMap[$value[0]])) { + return $value[0]; + } + $result = $this->classMap($value); + } else { + /** @var HashCondition $condition */ + $condition = Snowflake::createObject(HashCondition::class); + $condition->setValue($value); + $condition->setOpera('='); + $result = $condition->builder(); + } + return $result; + } + + /** + * @param $value + * @return mixed|object + * @throws Exception + */ + private function classMap($value) + { + [$option['opera'], $option['column'], $option['value']] = $value; + + $class = ConditionClassMap::$conditionMap[strtoupper($option['opera'])]; + if (!is_array($class)) { + $class = ['class' => $class]; + } + $option = array_merge($option, $class); + + /** @var Condition $class */ + return Snowflake::createObject($option)->builder(); + } + + /** + * @param $group + * @return string + */ + private function builderGroup($group) + { + if (empty($group)) { + return ''; + } + return ' GROUP BY ' . $group; + } + + /** + * @param $order + * @return string + */ + private function builderOrder($order) + { + if (!empty($order)) { + return ' ORDER BY ' . implode(',', $order); + } else { + return ''; + } + } + + /** + * @param QueryTrait $query + * @return string + */ + private function builderLimit($query) + { + $limit = $query->limit; + if (!is_numeric($limit) || $limit < 1) { + return ""; + } + $offset = $query->offset; + + if ($offset === null) { + return ' LIMIT ' . $limit; + } + + return ' LIMIT ' . $offset . ',' . $limit; + } + + + /** + * @param $value + * @param bool $isSearch + * @return int|string + */ + public function valueEncode($value, $isSearch = false) + { + if ($isSearch) { + return $value; + } + if (is_numeric($value)) { + return $value; + } else { + if (!is_null(JSON::decode($value))) { + return $value; + } + return '\'' . Str::encode($value) . '\''; + } + } + +} diff --git a/Database/Orm/Select.php b/Database/Orm/Select.php new file mode 100644 index 00000000..b1bf3a2b --- /dev/null +++ b/Database/Orm/Select.php @@ -0,0 +1,104 @@ +generate($query, false); + } + + /** + * @param ActiveQuery|Db $query + * @return string + * @throws Exception + */ + public function count($query) + { + return $this->generate($query, true); + } + + /** + * @param ActiveQuery|Db|Sql $query + * @param $isCount + * @return string + * @throws Exception + */ + private function generate($query, $isCount = false) + { + if (empty($query->from)) { + $query->from = $query->getTable(); + } + $builder = array_filter([ + $this->builderSelect($query->select, $isCount), + $this->builderFrom($query->from), + $this->builderAlias($query->alias), + $this->builderJoin($query->join), + $this->builderWhere($query->where), + $this->builderGroup($query->group) + ], function ($value) { + return !empty($value); + }); + if ($isCount) { + return implode('', $builder); + } + + $order = $this->builderOrder($query->order); + if (!empty($order)) { + $builder[] = $order; + } + $builder[] = $this->builderLimit($query); + + return implode('', $builder); + } + + /** + * @param null $select + * @param bool $isCount + * @return string + */ + private function builderSelect($select = NULL, $isCount = false) + { + if ($isCount === true) { + return 'SELECT COUNT(*)'; + } + if (empty($select)) { + return "SELECT *"; + } + if (is_array($select)) { + return "SELECT " . implode(',', $select); + } else { + return "SELECT " . $select; + } + } + + /** + * @param $table + * @return string + */ + public function getColumn($table) + { + return 'SHOW FULL FIELDS FROM ' . $table; + } +} diff --git a/Database/Pagination.php b/Database/Pagination.php new file mode 100644 index 00000000..41616297 --- /dev/null +++ b/Database/Pagination.php @@ -0,0 +1,135 @@ +activeQuery = $activeQuery; + } + + + /** + * @param Closure $callback + * @throws Exception + */ + public function setCallback($callback) + { + if (!is_callable($callback, true)) { + throw new Exception('非法回调函数~'); + } + $this->_callback = $callback; + } + + + /** + * @param int $number + */ + public function setOffset(int $number) + { + if ($number < 0) { + $number = 0; + } + $this->_offset = $number; + } + + + /** + * @param int $number + */ + public function setLimit(int $number) + { + if ($number < 1) { + $number = 100; + } else if ($number > 5000) { + $number = 5000; + } + $this->_limit = $number; + } + + + /** + * @param int $number + */ + public function setMax(int $number) + { + if ($number < 0) { + return; + } + $this->_max = $number; + } + + + /** + * @param array $param + */ + public function search($param = []) + { + if ($this->_length >= $this->_max) { + return; + } + [$length, $data] = $this->load(); + if ($param !== null) { + call_user_func($this->_callback, $data, $param); + } else { + call_user_func($this->_callback, $data); + } + unset($data); + if ($length < $this->_limit) { + return; + } + $this->search($param); + } + + + /** + * @return array|Collection + */ + private function load() + { + if ($this->_length + $this->_limit > $this->_max) { + $this->_limit = $this->_length + $this->_limit - $this->_max; + } + $data = $this->activeQuery->limit($this->_offset, $this->_limit)->get(); + $this->_offset += $this->_limit; + $this->_length += $data->size(); + return [$data->size(), $data]; + } + +} diff --git a/Database/Relation.php b/Database/Relation.php new file mode 100644 index 00000000..27e1c75b --- /dev/null +++ b/Database/Relation.php @@ -0,0 +1,111 @@ +_query[$identification] = $query; + return $this; + } + + /** + * @param $name + * @return ActiveQuery|null + */ + public function getQuery(string $name) + { + return $this->_query[$name] ?? null; + } + + + /** + * @param $identification + * @param $localValue + * @return ActiveRecord|mixed + * @throws + */ + public function first(string $identification, $localValue) + { + $_identification = $identification . '_count_' . $localValue; + if (isset($this->_relations[$_identification]) && $this->_relations[$_identification] !== null) { + return $this->_relations[$_identification]; + } + + $activeModel = $this->_query[$identification]->first(); + if (empty($activeModel)) { + return null; + } + + return $this->_relations[$_identification] = $activeModel; + } + + + /** + * @param $identification + * @param $localValue + * @return ActiveRecord|mixed + * @throws + */ + public function count(string $identification, $localValue) + { + $_identification = $identification . '_' . $localValue; + if (isset($this->_relations[$_identification]) && $this->_relations[$_identification] !== null) { + return $this->_relations[$_identification]; + } + + $activeModel = $this->_query[$identification]->count(); + if (empty($activeModel)) { + return null; + } + + return $this->_relations[$_identification] = $activeModel; + } + + + /** + * @param $identification + * @param $localValue + * @return array|Collection|mixed|null + * @throws + */ + public function get(string $identification, $localValue) + { + if (is_array($localValue)) { + $_identification = $identification . '_' . implode('_', $localValue); + } else { + $_identification = $identification . '_' . $localValue; + } + if (isset($this->_relations[$_identification]) && $this->_relations[$_identification] !== null) { + return $this->_relations[$_identification]; + } + + $activeModel = $this->_query[$identification]->get(); + if (empty($activeModel)) { + return null; + } + + return $this->_relations[$_identification] = $activeModel; + } + +} diff --git a/Database/Sql.php b/Database/Sql.php new file mode 100644 index 00000000..84310598 --- /dev/null +++ b/Database/Sql.php @@ -0,0 +1,32 @@ +getQuery($this); + } +} diff --git a/Database/Traits/QueryTrait.php b/Database/Traits/QueryTrait.php new file mode 100644 index 00000000..852a7056 --- /dev/null +++ b/Database/Traits/QueryTrait.php @@ -0,0 +1,771 @@ +where = []; + $this->select = []; + $this->join = []; + $this->order = []; + $this->offset = NULL; + $this->limit = NULL; + $this->group = ''; + $this->from = ''; + $this->alias = 't1'; + $this->filter = []; + } + + /** + * @param $bool + * @return $this + */ + public function ifNotWhere($bool) + { + $this->ifNotWhere = $bool; + return $this; + } + + /** + * @return string + * @throws Exception + */ + public function getTable() + { + return $this->modelClass::getTable(); + } + + + /** + * @param $column + * @return $this + */ + public function isNull($column) + { + $this->where[] = $column . ' IS NULL'; + return $this; + } + + + /** + * @param $column + * @return $this + */ + public function isEmpty($column) + { + $this->where[] = $column . ' = \'\''; + return $this; + } + + /** + * @param $column + * @return $this + */ + public function isNotEmpty($column) + { + $this->where[] = $column . ' <> \'\''; + return $this; + } + + /** + * @param $column + * @return $this + */ + public function isNotNull($column) + { + $this->where[] = $column . ' IS NOT NULL'; + return $this; + } + + /** + * @param $columns + * @return $this + * @throws Exception + */ + public function filter($columns) + { + if (!$columns) { + return $this; + } + if (is_callable($columns, TRUE)) { + return call_user_func($columns, $this); + } + if (is_string($columns)) { + $columns = explode(',', $columns); + } + if (!is_array($columns)) { + return $this; + } + $this->filter = $columns; + return $this; + } + + /** + * @param string $alias + * + * @return $this + * + * select * from tableName as t1 + */ + public function alias($alias = 't1') + { + $this->alias = $alias; + return $this; + } + + /** + * @param string|\Closure $tableName + * + * @return $this + * + */ + public function from($tableName) + { + $this->from = $tableName; + return $this; + } + + /** + * @param string $tableName + * @param string $alias + * @param null $on + * @param array|null $param + * @return $this + * $query->join([$tableName, ['userId'=>'uuvOd']], $param) + * $query->join([$tableName, ['userId'=>'uuvOd'], $param]) + * $query->join($tableName, ['userId'=>'uuvOd',$param]) + */ + private function join(string $tableName, string $alias, $on = NULL, array $param = NULL) + { + if (empty($on)) { + return $this; + } + $join[] = $tableName . ' AS ' . $alias; + $join[] = 'ON ' . $this->onCondition($alias, $on); + if (empty($join)) { + return $this; + } + + $this->join[] = implode(' ', $join); + if (!empty($param)) { + $this->addParams($param); + } + + return $this; + } + + /** + * @param $alias + * @param $on + * @return string + */ + private function onCondition($alias, $on) + { + $array = []; + foreach ($on as $key => $item) { + if (strpos($item, '.') === false) { + $this->addParam($key, $item); + } else { + $explode = explode('.', $item); + if (isset($explode[1]) && ($explode[0] == $alias || $this->alias == $explode[0])) { + $array[] = $key . '=' . $item; + } else { + $this->addParam($key, $item); + } + } + } + return implode(' AND ', $array); + } + + /** + * @param $tableName + * @param $alias + * @param $onCondition + * @param null $param + * @return $this + * @throws Exception + */ + public function leftJoin($tableName, $alias, $onCondition, $param = NULL) + { + if ($tableName instanceof ActiveRecord) { + $tableName = $tableName::getTable(); + } + return $this->join(...["LEFT JOIN " . $tableName, $alias, $onCondition, $param]); + } + + /** + * @param $tableName + * @param $alias + * @param $onCondition + * @param null $param + * @return $this + * @throws Exception + */ + public function rightJoin($tableName, $alias, $onCondition, $param = NULL) + { + if ($tableName instanceof ActiveRecord) { + $tableName = $tableName::getTable(); + } + return $this->join(...["RIGHT JOIN " . $tableName, $alias, $onCondition, $param]); + } + + /** + * @param $tableName + * @param $alias + * @param $onCondition + * @param null $param + * @return $this + * @throws Exception + */ + public function innerJoin($tableName, $alias, $onCondition, $param = NULL) + { + if ($tableName instanceof ActiveRecord) { + $tableName = $tableName::getTable(); + } + return $this->join(...["INNER JOIN " . $tableName, $alias, $onCondition, $param]); + } + + /** + * @param $array + * + * @return string + */ + private function toString($array) + { + $tmp = []; + if (!is_array($array)) { + return $array; + } + foreach ($array as $key => $val) { + if (is_array($val)) { + $tmp[] = $this->toString($array); + } else { + $tmp[] = $key . '=:' . $key; + $this->attributes[':' . $key] = $val; + } + } + return implode(' AND ', $tmp); + } + + /** + * @param $field + * + * @return $this + */ + public function sum($field) + { + $this->select[] = 'SUM(' . $field . ') AS ' . $field; + return $this; + } + + /** + * @param $field + * @return $this + */ + public function max($field) + { + $this->select[] = 'MAX(' . $field . ') AS ' . $field; + return $this; + } + + /** + * @param string $lngField + * @param string $latField + * @param int $lng1 + * @param int $lat1 + * + * @return $this + */ + public function distance(string $lngField, string $latField, int $lng1, int $lat1) + { + $sql = "ROUND(6378.138 * 2 * ASIN(SQRT(POW(SIN(($lat1 * PI() / 180 - $lat1 * PI() / 180) / 2),2) + COS($lat1 * PI() / 180) * COS($latField * PI() / 180) * POW(SIN(($lng1 * PI() / 180 - $lngField * PI() / 180) / 2),2))) * 1000) AS distance"; + $this->select[] = $sql; + return $this; + } + + /** + * @param $column + * @param string $sort + * + * @return $this + * + * [ + * 'addTime', + * 'descTime desc' + * ] + */ + public function orderBy($column, $sort = 'DESC') + { + if (empty($column)) { + return $this; + } + if (is_string($column)) { + return $this->addOrder(...func_get_args()); + } + + foreach ($column as $key => $val) { + $this->addOrder($val); + } + + return $this; + } + + /** + * @param $column + * @param string $sort + * + * @return $this + * + */ + private function addOrder($column, $sort = 'DESC') + { + $column = trim($column); + + if (func_num_args() == 1 || strpos($column, ' ') !== FALSE) { + $this->order[] = $column; + } else { + $this->order[] = "$column $sort"; + } + return $this; + } + + /** + * @param array|string $column + * + * @return $this + */ + public function select($column = '*') + { + if ($column == '*') { + $this->select = $column; + } else { + if (!is_array($column)) { + $column = explode(',', $column); + } + foreach ($column as $key => $val) { + $this->select[] = $val; + } + } + return $this; + } + + /** + * @return $this + */ + public function orderRand() + { + $this->order[] = 'RAND()'; + return $this; + } + + /** + * @param array $conditionArray + * + * @return $this|array|ActiveQuery + * @throws Exception + */ + public function or(array $conditionArray = []) + { + $conditions = []; + if (empty($conditionArray) || count($conditionArray) < 2) { + return $this; + } + + $select = new Select(); + + $conditions[] = $select->getWhere($this->where); + $conditions[] = $select->getWhere($conditionArray); + if (empty($conditions[count($conditions) - 1])) { + return $this; + } + $this->where = ['(' . implode(' OR ', $conditions) . ')']; + + return $this; + } + + /** + * @param $columns + * @param string $oprea + * @param null $value + * + * @return array|ActiveQuery|mixed + * @throws Exception + */ + public function and($columns, $value = NULL, $oprea = '=') + { + if (!is_numeric($value) && !is_bool($value)) { + $value = '\'' . $value . '\''; + } + $this->where[] = $columns . $oprea . $value; + return $this; + } + + /** + * @param $limit + * @return $this + */ + public function plunk($limit) + { + $this->offset = 0; + $this->limit = $limit; + return $this; + } + + /** + * @param $columns + * @param string $value + * @return $this + * @throws Exception + */ + public function like($columns, string $value) + { + if (empty($columns) || empty($value)) { + return $this; + } + + if (is_array($columns)) { + $columns = 'CONCAT(' . implode(',^,', $columns) . ')'; + } + + $this->where[] = ['LIKE', $columns, $value]; + + return $this; + } + + /** + * @param $columns + * @param string $value + * @return $this + * @throws Exception + */ + public function lLike($columns, string $value) + { + if (empty($columns) || empty($value)) { + return $this; + } + + if (is_array($columns)) { + $columns = 'CONCAT(' . implode(',^,', $columns) . ')'; + } + + $this->where[] = ['LLike', $columns, rtrim($value, '%')]; + + return $this; + } + + /** + * @param $columns + * @param string $value + * @return $this + * @throws Exception + */ + public function rLike($columns, string $value) + { + if (empty($columns) || empty($value)) { + return $this; + } + + if (is_array($columns)) { + $columns = 'CONCAT(' . implode(',^,', $columns) . ')'; + } + + $this->where[] = ['RLike', $columns, ltrim($value, '%')]; + + return $this; + } + + + /** + * @param $columns + * @param string $value + * @return $this + * @throws Exception + */ + public function notLike($columns, string $value) + { + if (empty($columns) || empty($value)) { + return $this; + } + + if (is_array($columns)) { + $columns = 'CONCAT(' . implode(',^,', $columns) . ')'; + } + + $this->where[] = ['NOT LIKE', $columns, ltrim($value, '%')]; + + return $this; + } + + /** + * @param string $column + * @param int $value + * @return $this + * @throws Exception + */ + public function eq(string $column, $value) + { + $this->where[] = ['EQ', $column, $value]; + + return $this; + } + + + /** + * @param string $column + * @param int $value + * @return $this + * @throws Exception + */ + public function neq(string $column, $value) + { + $this->where[] = ['NEQ', $column, $value]; + + return $this; + } + + + /** + * @param string $column + * @param int $value + * @return $this + * @throws Exception + */ + public function gt(string $column, $value) + { + $this->where[] = ['GT', $column, $value]; + + return $this; + } + + /** + * @param string $column + * @param int $value + * @return $this + * @throws Exception + */ + public function egt(string $column, $value) + { + $this->where[] = ['EGT', $column, $value]; + + return $this; + } + + + /** + * @param string $column + * @param int $value + * @return $this + * @throws Exception + */ + public function lt(string $column, $value) + { + $this->where[] = ['LT', $column, $value]; + + return $this; + } + + /** + * @param string $column + * @param int $value + * @return $this + * @throws Exception + */ + public function elt(string $column, $value) + { + $this->where[] = ['ELT', $column, $value]; + + return $this; + } + + /** + * @param $columns + * @param $value + * @return $this + * @throws Exception + */ + public function in($columns, $value) + { + if ($value instanceof \Closure) { + $value = $this->makeNewQuery($value); + } else if (empty($value) || !is_array($value)) { + $value = [-1]; + } else { + $value = array_filter($value, function ($value) { + return $value !== null; + }); + $value = array_unique($value); + } + $this->where[] = ['IN', $columns, $value]; + return $this; + } + + + /** + * @param $value + * @return string + * @throws Exception + */ + public function makeNewQuery($value) + { + $activeQuery = new ActiveQuery($this->modelClass); + call_user_func($value, $activeQuery); + if (empty($activeQuery->from)) { + $activeQuery->from($activeQuery->modelClass::getTable()); + } + return $activeQuery; + } + + + /** + * @param $columns + * @param $value + * @return $this + * @throws Exception + */ + public function notIn($columns, $value) + { + $this->where[] = ['NOT IN', $columns, $value]; + + return $this; + } + + /** + * @param string $column + * @param string $start + * @param string $end + * @return $this + * @throws Exception + */ + public function between(string $column, string $start, string $end) + { + if (empty($column) || empty($start) || empty($end)) { + return $this; + } + $this->where[] = ['BETWEEN', $column, [$start, $end]]; + + return $this; + } + + /** + * @param string $column + * @param string $start + * @param string $end + * @return $this + * @throws Exception + */ + public function notBetween(string $column, string $start, string $end) + { + if (empty($column) || empty($start) || empty($end)) { + return $this; + } + + $this->where[] = ['NOT BETWEEN', $column, [$start, $end]]; + + return $this; + } + + /** + * @param array $params + * + * @return $this + */ + public function bindParams(array $params = []) + { + if (empty($params)) { + return $this; + } + $this->attributes = $params; + return $this; + } + + /** + * @param array|callable $conditions + * @return $this + */ + public function where($conditions) + { + if ($conditions instanceof \Closure) { + call_user_func($conditions, $this); + } else { + if (is_string($conditions)) { + $conditions = [$conditions]; + } + $this->where[] = $conditions; + } + return $this; + } + + /** + * @param string $name + * @param string|null $having + * + * @return $this + */ + public function groupBy(string $name, string $having = NULL) + { + $this->group = $name; + if (empty($having)) { + return $this; + } + $this->group .= ' HAVING ' . $having; + return $this; + } + + /** + * @param int $offset + * @param int $limit + * + * @return $this + */ + public function limit(int $offset, int $limit = 20) + { + $this->offset = $offset; + $this->limit = $limit; + return $this; + } + + + public function oneLimit() + { + $this->limit = 1; + return $this; + } + +} diff --git a/composer.json b/composer.json index 6026e544..445e4c55 100644 --- a/composer.json +++ b/composer.json @@ -1,30 +1,42 @@ { - "name": "game-worker/snowflake", - "description": "test framework", - "authors": [ - { - "name": "XiangLin", - "email": "as2252258@163.com" - } - ], - "license": "MIT", - "require": { - "php": ">= 7.1", - "swoole/ide-helper": "@dev", - "amphp/amp": "^2.0.3", - "lcobucci/clock": "^1.0", - "psr/log": "^1.0", - "ext-json": "*", - "phpmailer/phpmailer": "^6.1" - }, - "autoload": { - "psr-4": { - "Snowflake\\": "system", - "HttpServer\\": "http-server" - }, - "files": [ - "error.php", - "function.php" - ] + "name": "game-worker/snowflake", + "description": "test framework", + "authors": [ + { + "name": "XiangLin", + "email": "as2252258@163.com" } + ], + "license": "MIT", + "require": { + "php": ">= 7.1", + "swoole/ide-helper": "@dev", + "amphp/amp": "^2.0.3", + "lcobucci/clock": "^1.0", + "psr/log": "^1.0", + "ext-json": "*", + "phpmailer/phpmailer": "^6.1", + "ext-exif": "*", + "ext-fileinfo": "*", + "ext-pdo": "*", + "ext-redis": "*", + "ext-simplexml": "*", + "ext-libxml": "*", + "ext-iconv": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-memcached": "*" + }, + "autoload": { + "psr-4": { + "Snowflake\\": "system", + "HttpServer\\": "http-server", + "validator\\": "validator", + "Database\\": "Database" + }, + "files": [ + "error.php", + "function.php" + ] + } } diff --git a/function.php b/function.php index 65cc6045..6c6647d9 100644 --- a/function.php +++ b/function.php @@ -32,6 +32,119 @@ if (!function_exists('make')) { } +if (!function_exists('exif_imagetype')) { + + /** + * @param $name + * @return string + */ + function exif_imagetype($name) + { + return get_file_extension($name); + } +} + +if (!function_exists('get_file_extension')) { + + function get_file_extension($filename) + { + $mime_types = array( + 'txt' => 'text/plain', + 'htm' => 'text/html', + 'html' => 'text/html', + 'php' => 'text/html', + 'css' => 'text/css', + 'js' => 'application/javascript', + 'json' => 'application/json', + 'xml' => 'application/xml', + 'swf' => 'application/x-shockwave-flash', + 'flv' => 'video/x-flv', + + // images + 'png' => 'image/png', + 'jpeg' => 'image/jpeg', + 'gif' => 'image/gif', + 'bmp' => 'image/bmp', + 'ico' => 'image/vnd.microsoft.icon', + 'tiff' => 'image/tiff', + 'svg' => 'image/svg+xml', + + // archives + 'zip' => 'application/zip', + 'rar' => 'application/x-rar-compressed', + 'exe' => 'application/x-msdownload', + 'msi' => 'application/x-msdownload', + 'cab' => 'application/vnd.ms-cab-compressed', + + // audio/video + 'mp3' => 'audio/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + + // adobe + 'pdf' => 'application/pdf', + 'psd' => 'image/vnd.adobe.photoshop', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + + // ms office + 'doc' => 'application/msword', + 'rtf' => 'application/rtf', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + + // open office + 'odt' => 'application/vnd.oasis.opendocument.text', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + ); + + $explode = explode('.', $filename); + $ext = strtolower(array_pop($explode)); + if (array_key_exists($ext, $mime_types)) { + return $ext; + } elseif (function_exists('finfo_open')) { + $fInfo = finfo_open(FILEINFO_MIME); + $mimeType = finfo_file($fInfo, $filename); + finfo_close($fInfo); + $mimeType = current(explode('; ', $mimeType)); + if (($search = array_search($mimeType, $mime_types)) == false) { + return $mimeType; + } + return $search; + } else { + return 'application/octet-stream'; + } + } +} + +if (!function_exists('request')) { + + /** + * @return mixed|null + */ + function request(): \HttpServer\Http\Request + { + if (!Context::hasContext('request')) { + return make('request', \HttpServer\Http\Request::class); + } + return Context::getContext('request'); + } + +} + +if (!function_exists('Input')) { + + /** + * @return mixed|null + */ + function Input() + { + return request()->params; + } + +} + if (!function_exists('storage')) { /** diff --git a/http-server/Abstracts/MiddlewareHandler.php b/http-server/Abstracts/MiddlewareHandler.php index 2500ca57..f2beaeba 100644 --- a/http-server/Abstracts/MiddlewareHandler.php +++ b/http-server/Abstracts/MiddlewareHandler.php @@ -7,7 +7,7 @@ use HttpServer\IInterface\IMiddleware; /** * Class MiddlewareHandler - * @package BeReborn\Route + * @package Snowflake\Snowflake\Route */ abstract class MiddlewareHandler implements IMiddleware { diff --git a/http-server/Abstracts/ServerBase.php b/http-server/Abstracts/ServerBase.php index 38813716..b693e884 100644 --- a/http-server/Abstracts/ServerBase.php +++ b/http-server/Abstracts/ServerBase.php @@ -14,7 +14,7 @@ use Swoole\WebSocket\Server; /** * Class ServerBase - * @package BeReborn\Server + * @package Snowflake\Snowflake\Server */ abstract class ServerBase extends Application { diff --git a/http-server/Client/Client.php b/http-server/Client/Client.php index 10fe3b31..1a119797 100644 --- a/http-server/Client/Client.php +++ b/http-server/Client/Client.php @@ -17,7 +17,7 @@ use Swoole\Coroutine\System; /** * Class Client - * @package BeReborn\Http + * @package Snowflake\Snowflake\Http */ class Client { diff --git a/http-server/Controller.php b/http-server/Controller.php index fa04a1e8..f6de1e4a 100644 --- a/http-server/Controller.php +++ b/http-server/Controller.php @@ -12,7 +12,7 @@ use Snowflake\Snowflake; /** * Class WebController - * @package BeReborn\Web + * @package Snowflake\Snowflake\Web */ class Controller extends Application { diff --git a/http-server/Events/WebSocket.php b/http-server/Events/WebSocket.php index 8aab923f..636d9cf8 100644 --- a/http-server/Events/WebSocket.php +++ b/http-server/Events/WebSocket.php @@ -8,7 +8,6 @@ namespace HttpServer\Events; -use BeReborn; use Exception; use HttpServer\ServerManager; use Snowflake\Error\Logger; @@ -22,7 +21,7 @@ use Swoole\WebSocket\Server; /** * Class ServerWebSocket - * @package BeReborn\Server + * @package Snowflake\Snowflake\Server */ class WebSocket extends Server { diff --git a/http-server/Exception/AuthException.php b/http-server/Exception/AuthException.php index 93a1a546..d4356d93 100644 --- a/http-server/Exception/AuthException.php +++ b/http-server/Exception/AuthException.php @@ -13,7 +13,7 @@ use Throwable; /** * Class AuthException - * @package BeReborn\Exception + * @package Snowflake\Snowflake\Exception */ class AuthException extends \Exception { diff --git a/http-server/Http/File.php b/http-server/Http/File.php index 49e24513..9361d80a 100644 --- a/http-server/Http/File.php +++ b/http-server/Http/File.php @@ -4,10 +4,10 @@ namespace HttpServer\Http; use Exception; +use Snowflake\Snowflake; /** * Class File - * @package BeReborn\Http */ class File { @@ -57,7 +57,7 @@ class File return $this->newName; } $param = ['tmp_name' => $this->getTmpPath()]; - $this->newName = \BeReborn::rename($param); + $this->newName = Snowflake::rename($param); return $this->newName; } diff --git a/http-server/Http/Formatter/HtmlFormatter.php b/http-server/Http/Formatter/HtmlFormatter.php index 3758795e..7dd53ff8 100644 --- a/http-server/Http/Formatter/HtmlFormatter.php +++ b/http-server/Http/Formatter/HtmlFormatter.php @@ -9,14 +9,14 @@ namespace HttpServer\Http\Formatter; -use BeReborn\Core\JSON; +use Snowflake\Core\JSON; use HttpServer\Application; use Swoole\Http\Response; use HttpServer\IInterface\IFormatter; /** * Class HtmlFormatter - * @package BeReborn\Http\Formatter + * @package Snowflake\Snowflake\Http\Formatter */ class HtmlFormatter extends Application implements IFormatter { diff --git a/http-server/Http/Formatter/JsonFormatter.php b/http-server/Http/Formatter/JsonFormatter.php index c41af0c8..6b8e1589 100644 --- a/http-server/Http/Formatter/JsonFormatter.php +++ b/http-server/Http/Formatter/JsonFormatter.php @@ -14,7 +14,7 @@ use HttpServer\IInterface\IFormatter; /** * Class JsonFormatter - * @package BeReborn\Http\Formatter + * @package Snowflake\Snowflake\Http\Formatter */ class JsonFormatter extends Application implements IFormatter { diff --git a/http-server/Http/Formatter/XmlFormatter.php b/http-server/Http/Formatter/XmlFormatter.php index 9c9aefb3..973954a4 100644 --- a/http-server/Http/Formatter/XmlFormatter.php +++ b/http-server/Http/Formatter/XmlFormatter.php @@ -17,7 +17,7 @@ use HttpServer\IInterface\IFormatter; /** * Class XmlFormatter - * @package BeReborn\Http\Formatter + * @package Snowflake\Snowflake\Http\Formatter */ class XmlFormatter extends Application implements IFormatter { diff --git a/http-server/Http/HttpHeaders.php b/http-server/Http/HttpHeaders.php index d9ee328a..6cfa2a0b 100644 --- a/http-server/Http/HttpHeaders.php +++ b/http-server/Http/HttpHeaders.php @@ -10,7 +10,7 @@ namespace HttpServer\Http; /** * Class HttpHeaders - * @package BeReborn\Http + * @package Snowflake\Snowflake\Http */ class HttpHeaders { diff --git a/http-server/Http/HttpParams.php b/http-server/Http/HttpParams.php index f06e4c9b..58899f9b 100644 --- a/http-server/Http/HttpParams.php +++ b/http-server/Http/HttpParams.php @@ -8,13 +8,13 @@ namespace HttpServer\Http; -use BeReborn\Core\JSON; use Exception; use HttpServer\Exception\RequestException; +use Snowflake\Snowflake; /** * Class HttpParams - * @package BeReborn\Http + * @package Snowflake\Snowflake\Http */ class HttpParams { @@ -177,7 +177,7 @@ class HttpParams } $param = $this->files[$name]; $param['class'] = File::class; - return \BeReborn::createObject($param); + return Snowflake::createObject($param); } /** diff --git a/http-server/Http/Request.php b/http-server/Http/Request.php index f936e7b1..b829deb8 100644 --- a/http-server/Http/Request.php +++ b/http-server/Http/Request.php @@ -6,6 +6,7 @@ use Snowflake\Core\Help; use Exception; use HttpServer\Application; use HttpServer\IInterface\AuthIdentity; +use Snowflake\Snowflake; defined('REQUEST_OK') or define('REQUEST_OK', 0); defined('REQUEST_FAIL') or define('REQUEST_FAIL', 500); @@ -13,7 +14,7 @@ defined('REQUEST_FAIL') or define('REQUEST_FAIL', 500); /** * Class HttpRequest * - * @package BeReborn\HttpRequest + * @package Snowflake\Snowflake\HttpRequest * * @property-read $isPost * @property-read $isGet @@ -284,14 +285,7 @@ class Request extends Application */ public function getIsHttp() { - if (!app()->has('socket')) { - return false; - } - $socket = \BeReborn::$app->getSocket()->getServer(); - if (empty($this->fd)) { - return false; - } - return $socket->exist($this->fd) && !$socket->isEstablished($this->fd); + return true; } /** diff --git a/http-server/Http/Response.php b/http-server/Http/Response.php index 3156e274..58aa725e 100644 --- a/http-server/Http/Response.php +++ b/http-server/Http/Response.php @@ -20,7 +20,7 @@ use Swoole\Http\Response as SResponse; /** * Class Response - * @package BeReborn\Http + * @package Snowflake\Snowflake\Http */ class Response extends Application { diff --git a/http-server/IInterface/AuthIdentity.php b/http-server/IInterface/AuthIdentity.php index e65ef7ed..d7e76e09 100644 --- a/http-server/IInterface/AuthIdentity.php +++ b/http-server/IInterface/AuthIdentity.php @@ -6,7 +6,7 @@ namespace HttpServer\IInterface; /** * Interface AuthIdentity - * @package BeReborn\Http + * @package Snowflake\Snowflake\Http */ interface AuthIdentity { diff --git a/http-server/IInterface/IFormatter.php b/http-server/IInterface/IFormatter.php index 464ce854..3114f6e1 100644 --- a/http-server/IInterface/IFormatter.php +++ b/http-server/IInterface/IFormatter.php @@ -11,7 +11,7 @@ namespace HttpServer\IInterface; /** * Interface IFormatter - * @package BeReborn\Http\Formatter + * @package Snowflake\Snowflake\Http\Formatter */ interface IFormatter { diff --git a/http-server/IInterface/IMiddleware.php b/http-server/IInterface/IMiddleware.php index 14002e9e..3a80b10c 100644 --- a/http-server/IInterface/IMiddleware.php +++ b/http-server/IInterface/IMiddleware.php @@ -8,7 +8,7 @@ use HttpServer\Http\Request; /** * Interface IMiddleware - * @package BeReborn\Route + * @package Snowflake\Snowflake\Route */ interface IMiddleware { diff --git a/http-server/Route/Any.php b/http-server/Route/Any.php index 483b396a..2e8ed3fc 100644 --- a/http-server/Route/Any.php +++ b/http-server/Route/Any.php @@ -6,7 +6,7 @@ namespace HttpServer\Route; /** * Class Any - * @package BeReborn\Route + * @package Snowflake\Snowflake\Route */ class Any { diff --git a/http-server/Route/CoreMiddleware.php b/http-server/Route/CoreMiddleware.php index 3d07ea37..d31cfa3c 100644 --- a/http-server/Route/CoreMiddleware.php +++ b/http-server/Route/CoreMiddleware.php @@ -4,13 +4,15 @@ namespace HttpServer\Route; +use HttpServer\Http\Context; use HttpServer\Http\Request; use HttpServer\Http\Response; use HttpServer\Abstracts\MiddlewareHandler; +use Snowflake\Snowflake; /** * Class CoreMiddleware - * @package BeReborn\Route + * @package Snowflake\Snowflake\Route * 跨域中间件 */ class CoreMiddleware extends MiddlewareHandler @@ -27,7 +29,7 @@ class CoreMiddleware extends MiddlewareHandler $header = $request->headers; /** @var Response $response */ - $response = \BeReborn::getApp('response'); + $response = Context::getContext('response'); $request_method = $header->getHeader('access-control-request-method'); $request_headers = $header->getHeader('access-control-request-headers'); $response->addHeader('Access-Control-Allow-Headers', $request_headers); diff --git a/http-server/Route/Filter.php b/http-server/Route/Filter.php index 1ea818a1..99da7fe3 100644 --- a/http-server/Route/Filter.php +++ b/http-server/Route/Filter.php @@ -11,10 +11,11 @@ use HttpServer\Route\Filter\HeaderFilter; use HttpServer\Route\Filter\QueryFilter; use Exception; use HttpServer\Application; +use Snowflake\Snowflake; /** * Class Filter - * @package BeReborn\Route + * @package Snowflake\Snowflake\Route */ class Filter extends Application { @@ -37,7 +38,7 @@ class Filter extends Application } /** @var BodyFilter $class */ - $class = \BeReborn::createObject(BodyFilter::class); + $class = Snowflake::createObject(BodyFilter::class); $class->rules = []; $class->params = Input()->params(); @@ -57,7 +58,7 @@ class Filter extends Application } /** @var HeaderFilter $class */ - $class = \BeReborn::createObject(HeaderFilter::class); + $class = Snowflake::createObject(HeaderFilter::class); $class->rules = []; $class->params = request()->headers->getHeaders(); @@ -77,7 +78,7 @@ class Filter extends Application } /** @var QueryFilter $class */ - $class = \BeReborn::createObject(QueryFilter::class); + $class = Snowflake::createObject(QueryFilter::class); $class->rules = []; $class->params = request()->headers->getHeaders(); diff --git a/http-server/Route/Filter/BodyFilter.php b/http-server/Route/Filter/BodyFilter.php index f5ddb425..182ac83a 100644 --- a/http-server/Route/Filter/BodyFilter.php +++ b/http-server/Route/Filter/BodyFilter.php @@ -8,7 +8,7 @@ use Exception; /** * Class BodyFilter - * @package BeReborn\Route\Filter + * @package Snowflake\Snowflake\Route\Filter */ class BodyFilter extends Filter { diff --git a/http-server/Route/Filter/Filter.php b/http-server/Route/Filter/Filter.php index 43fd1857..8dbae05e 100644 --- a/http-server/Route/Filter/Filter.php +++ b/http-server/Route/Filter/Filter.php @@ -10,7 +10,7 @@ use validator\Validator; /** * Class Filter - * @package BeReborn\Route\Filter + * @package Snowflake\Snowflake\Route\Filter */ abstract class Filter extends Application { diff --git a/http-server/Route/Filter/HeaderFilter.php b/http-server/Route/Filter/HeaderFilter.php index 478faf14..687fdf27 100644 --- a/http-server/Route/Filter/HeaderFilter.php +++ b/http-server/Route/Filter/HeaderFilter.php @@ -7,7 +7,7 @@ use Exception; /** * Class HeaderFilter - * @package BeReborn\Route\Filter + * @package Snowflake\Snowflake\Route\Filter */ class HeaderFilter extends Filter { diff --git a/http-server/Route/Filter/QueryFilter.php b/http-server/Route/Filter/QueryFilter.php index 914161a5..51430f0b 100644 --- a/http-server/Route/Filter/QueryFilter.php +++ b/http-server/Route/Filter/QueryFilter.php @@ -8,7 +8,7 @@ use Exception; /** * Class QueryFilter - * @package BeReborn\Route\Filter + * @package Snowflake\Snowflake\Route\Filter */ class QueryFilter extends Filter { diff --git a/http-server/Route/Handler.php b/http-server/Route/Handler.php index da5d4726..b108046e 100644 --- a/http-server/Route/Handler.php +++ b/http-server/Route/Handler.php @@ -6,10 +6,11 @@ namespace HttpServer\Route; use Exception; use HttpServer\Application; +use Snowflake\Snowflake; /** * Class TcpListen - * @package BeReborn\Route + * @package Snowflake\Snowflake\Route */ class Handler extends Application { @@ -23,7 +24,7 @@ class Handler extends Application */ public function __construct() { - $this->router = \BeReborn::$app->getRouter(); + $this->router = Snowflake::get()->router; parent::__construct([]); } diff --git a/http-server/Route/Limits.php b/http-server/Route/Limits.php deleted file mode 100644 index 6924e7c8..00000000 --- a/http-server/Route/Limits.php +++ /dev/null @@ -1,69 +0,0 @@ -route[$path] = [$limit, $duration, $isBindConsumer]; - return $this; - } - - /** - * @param int $userId - * @return bool - * @throws Exception - * - * 判断有没有被限流 - */ - public function isRestrictedCurrent(int $userId = 0) - { - $path = \request()->getUri(); - if (!isset($this->route[$path])) { - return false; - } - $redis = \BeReborn::getRedis(); - [$limit, $duration, $isBindConsumer] = $this->route[$path]; - if ($limit < 1) { - return false; - } - if ($isBindConsumer && $userId < 1) { - return true; - } - - $uri = md5($path) . '_' . $userId; - if ($redis->incr($uri) > $limit) { - return true; - } - if ($redis->ttl($uri) == -1) { - $redis->expire($uri, $duration); - } - return false; - } - - -} diff --git a/http-server/Route/Middleware.php b/http-server/Route/Middleware.php index 6a7fe448..207c0f80 100644 --- a/http-server/Route/Middleware.php +++ b/http-server/Route/Middleware.php @@ -15,7 +15,7 @@ use HttpServer\Route\Dispatch\Dispatch; /** * Class Middleware - * @package BeReborn\Route + * @package Snowflake\Snowflake\Route */ class Middleware { diff --git a/http-server/Route/Node.php b/http-server/Route/Node.php index 44c9be6b..1dd98a66 100644 --- a/http-server/Route/Node.php +++ b/http-server/Route/Node.php @@ -7,10 +7,11 @@ namespace HttpServer\Route; use HttpServer\Http\Request; use Exception; use HttpServer\Application; +use Snowflake\Snowflake; /** * Class Node - * @package BeReborn\Route + * @package Snowflake\Snowflake\Route */ class Node extends Application { @@ -100,7 +101,7 @@ class Node extends Application $rule['class'] = Filter::class; } /** @var Filter $object */ - $object = \BeReborn::createObject($rule); + $object = Snowflake::createObject($rule); if (!$object->handler()) { return false; }; @@ -243,7 +244,7 @@ class Node extends Application */ public function limits(int $limit, int $duration = 60, bool $isBindConsumer = false) { - $limits = \BeReborn::$app->getLimits(); + $limits = Snowflake::get()->getLimits(); $limits->addLimits($this->path, $limit, $duration, $isBindConsumer); return $this; } @@ -266,7 +267,7 @@ class Node extends Application if (is_array($middle)) { $_tmp = $this->each($middle, $_tmp); } else { - $_tmp[] = \BeReborn::createObject($middle); + $_tmp[] = Snowflake::createObject($middle); } } catch (Exception $exception) { } @@ -305,7 +306,7 @@ class Node extends Application if (is_array($class)) { $_temp = $this->each($class, $_temp); } else { - $_temp[] = \BeReborn::createObject($class); + $_temp[] = Snowflake::createObject($class); } } return $_temp; diff --git a/http-server/Route/Router.php b/http-server/Route/Router.php index ff6a5d2d..7f805ee7 100644 --- a/http-server/Route/Router.php +++ b/http-server/Route/Router.php @@ -12,10 +12,11 @@ use HttpServer\Application; use Snowflake\Config; use Snowflake\Core\JSON; use Snowflake\Exception\ConfigException; +use Snowflake\Snowflake; /** * Class Router - * @package BeReborn\Route + * @package Snowflake\Snowflake\Route */ class Router extends Application implements RouterInterface { @@ -173,7 +174,7 @@ class Router extends Application implements RouterInterface */ public function listen(int $port, Closure $closure) { - $stdClass = \BeReborn::createObject(Handler::class); + $stdClass = Snowflake::createObject(Handler::class); $this->group(['prefix' => $port], $closure, $stdClass); } diff --git a/http-server/ServerManager.php b/http-server/ServerManager.php index 7eb419e5..e35cddc7 100644 --- a/http-server/ServerManager.php +++ b/http-server/ServerManager.php @@ -5,10 +5,8 @@ namespace HttpServer; use Exception; use ReflectionException; -use Snowflake\Exception\ComponentException; use Snowflake\Exception\NotFindClassException; use Snowflake\Snowflake; -use Swoole\Process\Pool; class ServerManager { diff --git a/system/Abstracts/BaseAnnotation.php b/system/Abstracts/BaseAnnotation.php index 26346476..b2d14897 100644 --- a/system/Abstracts/BaseAnnotation.php +++ b/system/Abstracts/BaseAnnotation.php @@ -7,7 +7,7 @@ namespace Snowflake\Abstracts; /** * Class BaseAnnotation - * @package BeReborn\Annotation\Base + * @package Snowflake\Snowflake\Annotation\Base */ abstract class BaseAnnotation extends Component { diff --git a/system/Abstracts/BaseApplication.php b/system/Abstracts/BaseApplication.php index 99941b21..0c8181fc 100644 --- a/system/Abstracts/BaseApplication.php +++ b/system/Abstracts/BaseApplication.php @@ -29,7 +29,7 @@ use Snowflake\Event; /** * Class BaseApplication - * @package BeReborn\Base + * @package Snowflake\Snowflake\Base * @property $json * @property Annotation $annotation * @property Event $event @@ -38,6 +38,7 @@ use Snowflake\Event; * @property \Snowflake\Pool\Pool $pool * @property Server $servers * @property Connection $connections + * @property Logger $logger */ abstract class BaseApplication extends Service { @@ -192,6 +193,17 @@ abstract class BaseApplication extends Service return current($this->getLocalIps()); } + + /** + * @return Logger + * @throws ComponentException + */ + public function getLogger(): Logger + { + return $this->get('logger'); + } + + /** * @param $ip * @return bool diff --git a/system/Abstracts/BaseObject.php b/system/Abstracts/BaseObject.php index 00184a77..6c02c312 100644 --- a/system/Abstracts/BaseObject.php +++ b/system/Abstracts/BaseObject.php @@ -15,7 +15,7 @@ use Snowflake\Snowflake; /** * Class BaseObject * @method defer() - * @package BeReborn\Base + * @package Snowflake\Snowflake\Base * @method afterInit * @method initialization */ diff --git a/system/Abstracts/Component.php b/system/Abstracts/Component.php index d29f3cbc..4e72e356 100644 --- a/system/Abstracts/Component.php +++ b/system/Abstracts/Component.php @@ -10,10 +10,11 @@ namespace Snowflake\Abstracts; use Exception; +use Snowflake\Snowflake; /** * Class Component - * @package BeReborn\Base + * @package Snowflake\Snowflake\Base */ class Component extends BaseObject { @@ -73,6 +74,7 @@ class Component extends BaseObject */ public function trigger($name, $event = null, $params = [], $isRemove = false) { + $aEvents = Snowflake::get()->event; if (isset($this->_events[$name])) { $events = $this->_events[$name]; foreach ($events as $key => $_event) { @@ -82,11 +84,11 @@ class Component extends BaseObject call_user_func($_event, ...$params); if ($isRemove) { unset($this->_events[$name][$key]); - of($name, $_event); + $aEvents->of($name, $_event); } } } - fire($name, $event); + $aEvents->trigger($name, $event); } /** @@ -96,14 +98,15 @@ class Component extends BaseObject */ public function off($name, $handler = NULL) { + $aEvents = Snowflake::get()->event; if (!isset($this->_events[$name])) { - return of($name, $handler); + return $aEvents->of($name, $handler); } if (empty($handler)) { unset($this->_events[$name]); - return of($name, $handler); + return $aEvents->of($name, $handler); } foreach ($this->_events[$name] as $key => $val) { @@ -114,7 +117,7 @@ class Component extends BaseObject break; } - return of($name, $handler); + return $aEvents->of($name, $handler); } /** @@ -122,7 +125,8 @@ class Component extends BaseObject public function offAll() { $this->_events = []; - ofAll(); + $aEvents = Snowflake::get()->event; + $aEvents->clean(); } diff --git a/system/Abstracts/Config.php b/system/Abstracts/Config.php index ef38327f..7f540e3b 100644 --- a/system/Abstracts/Config.php +++ b/system/Abstracts/Config.php @@ -15,7 +15,7 @@ use Snowflake\Abstracts\Component; /** * Class Config - * @package BeReborn\Base + * @package Snowflake\Snowflake\Base */ class Config extends Component { diff --git a/system/Abstracts/Configure.php b/system/Abstracts/Configure.php index 28d8f675..89e6ec48 100644 --- a/system/Abstracts/Configure.php +++ b/system/Abstracts/Configure.php @@ -10,7 +10,7 @@ namespace Snowflake\Abstracts; /** * Interface Configure - * @package BeReborn\Base + * @package Snowflake\Snowflake\Base */ interface Configure { diff --git a/system/Abstracts/Pool.php b/system/Abstracts/Pool.php index e1147493..942c0e47 100644 --- a/system/Abstracts/Pool.php +++ b/system/Abstracts/Pool.php @@ -10,7 +10,7 @@ use Swoole\Coroutine\Channel; /** * Class Pool - * @package BeReborn\Pool + * @package Snowflake\Snowflake\Pool */ abstract class Pool extends Component { @@ -41,6 +41,7 @@ abstract class Pool extends Component * @param $name * @param int $timeout * @return mixed + * @throws Exception */ protected function get($name, $timeout = -1) { diff --git a/system/Annotation/Annotation.php b/system/Annotation/Annotation.php index 47b211e1..8988dd04 100644 --- a/system/Annotation/Annotation.php +++ b/system/Annotation/Annotation.php @@ -12,7 +12,7 @@ use Snowflake\Snowflake; /** * Class Annotation - * @package BeReborn\Annotation + * @package Snowflake\Snowflake\Annotation * @property Websocket $websocket */ class Annotation extends BaseAnnotation diff --git a/system/Application.php b/system/Application.php index 08eed1e8..13c1293c 100644 --- a/system/Application.php +++ b/system/Application.php @@ -10,14 +10,13 @@ namespace Snowflake; use Exception; -use HttpServer\Abstracts\HttpService; use HttpServer\Server; use Snowflake\Abstracts\BaseApplication; /** * Class Init * - * @package BeReborn\Web + * @package Snowflake * * @property-read Config $config */ @@ -66,6 +65,6 @@ class Application extends BaseApplication */ public function make($className, $abstracts = null) { - return Snowflake::createObject($className); + return make($className, $abstracts); } } diff --git a/system/Cache/File.php b/system/Cache/File.php index 7eecef9d..abeb34f4 100644 --- a/system/Cache/File.php +++ b/system/Cache/File.php @@ -15,7 +15,7 @@ use Swoole\Coroutine\System; /** * Class File - * @package BeReborn\Cache + * @package Snowflake\Snowflake\Cache */ class File extends Component implements ICache { diff --git a/system/Cache/ICache.php b/system/Cache/ICache.php index e12e521d..a90c3f5e 100644 --- a/system/Cache/ICache.php +++ b/system/Cache/ICache.php @@ -10,7 +10,7 @@ namespace Snowflake\Cache; /** * Interface ICache - * @package BeReborn\Cache + * @package Snowflake\Snowflake\Cache */ interface ICache { diff --git a/system/Cache/Redis.php b/system/Cache/Redis.php index 6480ecc7..4c70f5d2 100644 --- a/system/Cache/Redis.php +++ b/system/Cache/Redis.php @@ -17,7 +17,7 @@ use Swoole\Coroutine; /** * Class Redis - * @package BeReborn\Cache + * @package Snowflake\Snowflake\Cache * @see \Redis * @method append($key, $value): int * @method auth($password): diff --git a/system/Console/AbstractConsole.php b/system/Console/AbstractConsole.php index e886d155..e4f8ea3c 100644 --- a/system/Console/AbstractConsole.php +++ b/system/Console/AbstractConsole.php @@ -5,11 +5,12 @@ namespace Snowflake\Console; use Exception; +use Snowflake\Snowflake; use Swoole\Coroutine\Channel; /** * Class AbstractConsole - * @package BeReborn\Console + * @package Snowflake\Console */ abstract class AbstractConsole { @@ -33,7 +34,7 @@ abstract class AbstractConsole public function __construct(array $config = []) { $this->_config = $config; - $this->signCommand(\BeReborn::createObject(DefaultCommand::class)); + $this->signCommand(Snowflake::createObject(DefaultCommand::class)); } /** @@ -104,7 +105,7 @@ abstract class AbstractConsole return; } foreach ($kernel as $command) { - $this->signCommand(\BeReborn::createObject($command)); + $this->signCommand(Snowflake::createObject($command)); } } diff --git a/system/Console/Application.php b/system/Console/Application.php index 62b93cf2..b48886f2 100644 --- a/system/Console/Application.php +++ b/system/Console/Application.php @@ -6,18 +6,18 @@ * Time: 2:16 */ -namespace BeReborn\Console; +namespace Snowflake\Console; -use BeReborn\Base\BaseApplication; -use kafka\Protocol\SyncGroup; +use Snowflake\Abstracts\BaseApplication; use Swoole\Coroutine\Channel; use Swoole\Runtime; use Swoole\Timer; +use Snowflake\Snowflake; /** * Class Application - * @package BeReborn\Console + * @package Snowflake\Console */ class Application extends BaseApplication { @@ -52,7 +52,7 @@ class Application extends BaseApplication public function register($class) { if (is_string($class) || is_callable($class, true)) { - $class = \BeReborn::createObject($class); + $class = Snowflake::createObject($class); } $this->console->signCommand($class); } @@ -65,10 +65,8 @@ class Application extends BaseApplication */ public function run($kernel = null) { - setCommand(true); try { - $params = ''; - $kernel = \BeReborn::make($kernel, Kernel::class); + $kernel = make($kernel, Kernel::class); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); diff --git a/system/Console/Command.php b/system/Console/Command.php index faf7f742..0d3723c0 100644 --- a/system/Console/Command.php +++ b/system/Console/Command.php @@ -7,7 +7,7 @@ use Snowflake\Abstracts\BaseObject; /** * Class Command - * @package BeReborn\Console + * @package Snowflake\Console */ abstract class Command extends BaseObject implements CommandInterface { diff --git a/system/Console/CommandInterface.php b/system/Console/CommandInterface.php index 3be3a58a..cae5ea8f 100644 --- a/system/Console/CommandInterface.php +++ b/system/Console/CommandInterface.php @@ -5,7 +5,7 @@ namespace Snowflake\Console; /** * Interface CommandInterface - * @package BeReborn\Console + * @package Snowflake\Console */ interface CommandInterface { diff --git a/system/Console/Console.php b/system/Console/Console.php index 65736852..b697f3f2 100644 --- a/system/Console/Console.php +++ b/system/Console/Console.php @@ -1,11 +1,11 @@ fd = 0; $request->params = new HttpParams([], $arrays, []); - $request->headers = new HttpParams([]); + $request->headers = new HttpHeaders([]); - Context::setRequest($request); + Context::setContext('request', $request); return $arrays; } diff --git a/system/Console/ICommand.php b/system/Console/ICommand.php index 3ae021fd..ea4bdd90 100644 --- a/system/Console/ICommand.php +++ b/system/Console/ICommand.php @@ -1,7 +1,7 @@ newInstanceArgs($dependencies ?? []); } - if (!empty($dependencies) && $reflect->implementsInterface('BeReborn\Base\Configure')) { + if (!empty($dependencies) && $reflect->implementsInterface('Snowflake\Abstracts\Configure')) { $dependencies[count($dependencies) - 1] = $config; return $reflect->newInstanceArgs($dependencies); } diff --git a/system/Di/Service.php b/system/Di/Service.php index f6292f6e..06604cae 100644 --- a/system/Di/Service.php +++ b/system/Di/Service.php @@ -16,7 +16,7 @@ use Snowflake\Snowflake; /** * Class Service - * @package BeReborn\Di + * @package Snowflake\Snowflake\Di */ class Service extends Component { diff --git a/system/Error/ErrorHandler.php b/system/Error/ErrorHandler.php index 3537db9f..0726e18f 100644 --- a/system/Error/ErrorHandler.php +++ b/system/Error/ErrorHandler.php @@ -18,7 +18,7 @@ use Snowflake\Snowflake; /** * Class ErrorHandler * - * @package BeReborn\Base + * @package Snowflake\Snowflake\Base * @property-read $asError */ class ErrorHandler extends Component implements ErrorInterface diff --git a/system/Error/ErrorInterface.php b/system/Error/ErrorInterface.php index dca2cc28..d8307e81 100644 --- a/system/Error/ErrorInterface.php +++ b/system/Error/ErrorInterface.php @@ -10,7 +10,7 @@ namespace Snowflake\Error; /** * Interface ErrorInterface - * @package BeReborn\Error + * @package Snowflake\Snowflake\Error */ interface ErrorInterface { diff --git a/system/Error/Logger.php b/system/Error/Logger.php index 0f694cf4..3da4d1ea 100644 --- a/system/Error/Logger.php +++ b/system/Error/Logger.php @@ -9,13 +9,13 @@ namespace Snowflake\Error; use Exception; +use Snowflake\Core\JSON; use Snowflake\Snowflake; -use Swoole\Coroutine\System; use Swoole\Process; /** * Class Logger - * @package BeReborn\Error + * @package Snowflake\Snowflake\Error */ class Logger { @@ -102,7 +102,7 @@ class Logger public static function print_r($message, $category = '') { /** @var Process $logger */ - $logger = \BeReborn::getApp('logger'); + $logger = Snowflake::get()->logger; $logger->write(JSON::encode([$message, $category])); } diff --git a/system/Event.php b/system/Event.php index bf07843b..6fe1f2d7 100644 --- a/system/Event.php +++ b/system/Event.php @@ -148,6 +148,12 @@ class Event extends BaseObject } + public function clean() + { + $this->_events = []; + } + + /** * @param $name * @param null $handler diff --git a/system/Exception/ComponentException.php b/system/Exception/ComponentException.php index 486561b7..2ed4c509 100644 --- a/system/Exception/ComponentException.php +++ b/system/Exception/ComponentException.php @@ -13,7 +13,7 @@ use Throwable; /** * Class ComponentException - * @package BeReborn\Exception + * @package Snowflake\Snowflake\Exception */ class ComponentException extends \Exception { diff --git a/system/Exception/NotFindClassException.php b/system/Exception/NotFindClassException.php index c2593876..9b17264c 100644 --- a/system/Exception/NotFindClassException.php +++ b/system/Exception/NotFindClassException.php @@ -13,7 +13,7 @@ use Throwable; /** * Class NotFindClassException - * @package BeReborn\Exception + * @package Snowflake\Snowflake\Exception */ class NotFindClassException extends \Exception { diff --git a/system/Pool/Connection.php b/system/Pool/Connection.php index e0e5f6bd..c752d327 100644 --- a/system/Pool/Connection.php +++ b/system/Pool/Connection.php @@ -10,7 +10,7 @@ use Snowflake\Abstracts\Pool; /** * Class Connection - * @package BeReborn\Pool + * @package Snowflake\Pool */ class Connection extends Pool { diff --git a/system/Pool/RedisClient.php b/system/Pool/RedisClient.php index 5a4112f1..19e9934b 100644 --- a/system/Pool/RedisClient.php +++ b/system/Pool/RedisClient.php @@ -12,7 +12,7 @@ use Exception; /** * Class RedisClient - * @package BeReborn\Pool + * @package Snowflake\Snowflake\Pool */ class RedisClient extends Pool { diff --git a/system/Process/Leafleting.php b/system/Process/Leafleting.php index 4046ac27..3486adf0 100644 --- a/system/Process/Leafleting.php +++ b/system/Process/Leafleting.php @@ -10,7 +10,6 @@ use Swoole\Timer; /** * Class Logfilemonitoring - * @package components */ class Leafleting extends Process { diff --git a/system/Process/Process.php b/system/Process/Process.php index 42f955ed..362d3408 100644 --- a/system/Process/Process.php +++ b/system/Process/Process.php @@ -9,7 +9,7 @@ use Snowflake\Application; /** * Class Process - * @package BeReborn\Service + * @package Snowflake\Snowflake\Service */ abstract class Process extends Component { diff --git a/system/Process/ServerInotify.php b/system/Process/ServerInotify.php index 929f72a5..a402778e 100644 --- a/system/Process/ServerInotify.php +++ b/system/Process/ServerInotify.php @@ -10,7 +10,6 @@ namespace Snowflake\Process; use Exception; -use BeReborn; use Snowflake\Snowflake; use Swoole\Event; use Swoole\Timer; @@ -18,7 +17,7 @@ use swoole_process; /** * Class ServerInotify - * @package BeReborn\Server + * @package Snowflake\Snowflake\Server */ class ServerInotify extends Process { diff --git a/system/Processes.php b/system/Processes.php index 40948ba1..26985e2f 100644 --- a/system/Processes.php +++ b/system/Processes.php @@ -9,9 +9,7 @@ use HttpServer\ServerManager; use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\SMTP; use Snowflake\Abstracts\Component; -use Swoole\Process; use Swoole\Process\Pool; -use Swoole\Timer; /** * Class Processes @@ -39,6 +37,12 @@ class Processes extends Component if ($event->exists(Event::PROCESS_WORKER_STOP)) { $event->trigger(Event::PROCESS_WORKER_STOP); } + + $email = Config::get('admin.email'); + if (!empty($email)) { + $nickname = Config::get('admin.name', false, '亲爱的开发者'); + $this->system_mail($email, $nickname); + } }); $server->on('message', function ($pool, $message) { file_put_contents('a.log', func_get_args()); @@ -47,7 +51,11 @@ class Processes extends Component } - protected function system_mail() + /** + * @param $email + * @param $nickname + */ + protected function system_mail($email, $nickname) { try { $mail = new PHPMailer(true); @@ -62,16 +70,12 @@ class Processes extends Component $mail->Port = 587; // TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above //Recipients - $mail->setFrom('from@example.com', 'Mailer'); - $mail->addAddress('joe@example.net', 'Joe User'); // Add a recipient - $mail->addAddress('ellen@example.com'); // Name is optional - $mail->addReplyTo('info@example.com', 'Information'); - $mail->addCC('cc@example.com'); - $mail->addBCC('bcc@example.com'); + $mail->setFrom('system@example.com', '系统管理员'); + $mail->addAddress($email, $nickname); // Add a recipient // Attachments - $mail->addAttachment('/var/tmp/file.tar.gz'); // Add attachments - $mail->addAttachment('/tmp/image.jpg', 'new.jpg'); // Optional name +// $mail->addAttachment('/var/tmp/file.tar.gz'); // Add attachments +// $mail->addAttachment('/tmp/image.jpg', 'new.jpg'); // Optional name // Content $mail->isHTML(true); // Set email format to HTML diff --git a/system/Snowflake.php b/system/Snowflake.php index 3083a6e4..0f11fa16 100644 --- a/system/Snowflake.php +++ b/system/Snowflake.php @@ -155,6 +155,19 @@ class Snowflake } + public static function rename($tmp) + { + $hash = md5_file($tmp['tmp_name']); + + $later = '.' . exif_imagetype($tmp['tmp_name']); + + $match = '/(\w{12})(\w{5})(\w{9})(\w{6})/'; + $tmp = preg_replace($match, '$1-$2-$3-$4', $hash); + + return strtoupper($tmp) . $later; + } + + /** * @return bool */ diff --git a/validator/ArrayValidator.php b/validator/ArrayValidator.php new file mode 100644 index 00000000..b35a4967 --- /dev/null +++ b/validator/ArrayValidator.php @@ -0,0 +1,65 @@ +getParams(); + if (empty($param) || !is_array($param)) { + return true; + } + if (!isset($param[$this->field])) { + return true; + } + if (!is_array($param[$this->field])) { + return $this->addError("The param :attribute must a array"); + } + return $this->toArray($param[$this->field]); + } + + /** + * @param $data + * @return array + * + * 转成数组 + */ + private function toArray($data) + { + if (is_numeric($data)) { + return []; + } else if (is_null(json_decode($data, true))) { + return []; + } elseif (is_object($data)) { + $data = get_object_vars($data); + } + + $_tmp = []; + foreach ($data as $key => $val) { + if (is_object($val)) { + $_tmp[$key] = $this->toArray($val); + } else if (is_array($val)) { + $_tmp[$key] = $this->toArray($val); + } else { + $_tmp[$key] = $val; + } + } + + return $_tmp; + } + +} diff --git a/validator/BaseValidator.php b/validator/BaseValidator.php new file mode 100644 index 00000000..ef1d007a --- /dev/null +++ b/validator/BaseValidator.php @@ -0,0 +1,120 @@ +model = $model; + } + + /** + * @return ActiveRecord + */ + public function getModel() + { + return $this->model; + } + + public function __construct($config = []) + { + $this->regConfig($config); + } + + private function regConfig($config) + { + if (empty($config) || !is_array($config)) { + return; + } + foreach ($config as $key => $val) { + $this->$key = $val; + } + } + + /** + * @throws \Exception + * @return bool + */ + public function trigger() + { + throw new \Exception('Child Class must define method of trigger'); + } + + /** + * @return mixed + */ + protected function getParams() + { + return $this->params; + } + + /** + * @param $data + * @return $this + */ + public function setParams($data) + { + $this->params = $data; + return $this; + } + + /** + * @param $message + * @return bool + */ + public function addError($message) + { + $this->isFail = FALSE; + + $message = str_replace(':attribute', $this->field, $message); + + $this->message = $message; + + return $this->isFail; + } + + /** + * @return string + */ + public function getError() + { + return $this->message; + } + + /** + * @param $name + * @param $value + * @throws \Exception + */ + public function __set($name, $value) + { + $method = 'set' . ucfirst($name); + if (method_exists($this, $method)) { + $this->$method($value); + } else if (property_exists($this, $name)) { + $this->$name = $value; + } else { + throw new \Exception('unknown property ' . $name . ' in class ' . get_called_class()); + } + } +} diff --git a/validator/DateTimeValidator.php b/validator/DateTimeValidator.php new file mode 100644 index 00000000..4c44b70b --- /dev/null +++ b/validator/DateTimeValidator.php @@ -0,0 +1,131 @@ +getParams(); + if (empty($param) || !is_array($param)) { + return true; + } + if (!isset($param[$this->field]) || empty($param[$this->field])) { + return true; + } + $value = $param[$this->field]; + switch (strtolower($this->method)) { + case self::DATE: + return $this->validatorDate($value); + break; + case self::DATE_TIME: + return $this->validateDatetime($value); + break; + case self::TIME: + return $this->validatorTime($value); + break; + case self::STR_TO_TIME: + return $this->validatorTimestamp($value); + break; + default: + return true; + } + } + + /** + * @param $value + * @return bool + * + * 效验分秒 格式如 01:02 or 01-02 + */ + public function validatorTime($value) + { + if (empty($value) || !is_string($value)) { + return $this->addError('The param :attribute not is a date value'); + } + $match = preg_match('/^[0-5]?\d{1}.{1}[0-5]?\d{1}$/', $value, $result); + if ($match && $result[0] == $value) { + return true; + } else { + return $this->addError('The param :attribute format error'); + } + } + + + /** + * @param $value + * @return bool + * + * 效验分秒 格式如 2017-12-22 01:02 + */ + public function validateDatetime($value) + { + if (empty($value) || !is_string($value)) { + return $this->addError('The param :attribute not is a date value'); + } + $match = '/^\d{4}\-\d{2}\-\d{2}\s+\d{2}:\d{2}:\d{2}$/'; + $match = preg_match($match, $value, $result); + if ($match && $result[0] == $value) { + return true; + } else { + return $this->addError('The param :attribute format error'); + } + } + + /** + * @param $value + * @return bool + * + * 效验分秒 格式如 2017-12-22 + */ + public function validatorDate($value) + { + if (empty($value) || !is_string($value)) { + return $this->addError('The param :attribute not is a date value'); + } + $match = preg_match('/^(\d{4}).*([0-12]).*([0-31]).*$/', $value, $result); + if ($match && $result[0] == $value) { + return true; + } else { + return $this->addError('The param :attribute format error'); + } + } + + /** + * @param $value + * @return bool + * + * 效验时间戳 格式如 1521452254 + */ + public function validatorTimestamp($value) + { + if (empty($value) || !is_numeric($value)) { + return $this->addError('The param :attribute not is a timestamp value'); + } + if (strlen((string)$value) != 10) { + return $this->addError('The param :attribute not is a timestamp value'); + } + if (!date('YmdHis', $value)) { + return $this->addError('The param :attribute format error'); + } + return true; + } +} diff --git a/validator/EmailValidator.php b/validator/EmailValidator.php new file mode 100644 index 00000000..967f140c --- /dev/null +++ b/validator/EmailValidator.php @@ -0,0 +1,34 @@ +getParams(); + if (empty($param) || !isset($param[$this->field])) { + return true; + } else { + $value = $param[$this->field]; + if (preg_match('/^[a-zA-Z0-9]+([\.\_]{1,})[a-zA-Z0-9]+@[a-zA-Z]+(\.\w+)+/', $value)) { + return true; + } else { + return $this->addError('The param :attribute format error'); + } + } + } + +} diff --git a/validator/EmptyValidator.php b/validator/EmptyValidator.php new file mode 100644 index 00000000..d451903c --- /dev/null +++ b/validator/EmptyValidator.php @@ -0,0 +1,51 @@ +getParams(); + if (empty($param) || !isset($param[$this->field])) { + return $this->addError(':attribute not exists'); + } + + $value = $param[$this->field]; + + switch (strtolower($this->method)) { + case self::CAN_NOT_EMPTY: + if (strlen($value) < 1) { + return $this->addError('The :attribute can not empty.'); + } + break; + case self::CAN_NOT_NULL: + if ($value === null) { + return $this->addError('The :attribute can not is null.'); + } + break; + } + + return true; + } +} diff --git a/validator/EnumValidator.php b/validator/EnumValidator.php new file mode 100644 index 00000000..1a818a4e --- /dev/null +++ b/validator/EnumValidator.php @@ -0,0 +1,47 @@ +getParams(); + if (empty($param) || !isset($param[$this->field])) { + return $this->addError('The param :attribute is null'); + } + $value = $param[$this->field]; + if (is_null($value)) { + return $this->addError('The param :attribute is null'); + } + + if (!is_array($this->value)) { + return true; + } + if (!in_array($value, $this->value)) { + return $this->addError($this->i()); + } + + return true; + } + + /** + * @return string + */ + private function i() + { + return 'The param :attribute value only in ' . implode(',', $this->value); + } + +} diff --git a/validator/IntegerValidator.php b/validator/IntegerValidator.php new file mode 100644 index 00000000..586ff85f --- /dev/null +++ b/validator/IntegerValidator.php @@ -0,0 +1,44 @@ +getParams(); + if (empty($param) || !isset($param[$this->field])) { + return true; + } + + $value = $param[$this->field] ?? null; + if ($value === null) { + return $this->addError('The :attribute can not is null.'); + } + if ($this->type !== self::MIN && $value < $this->value) { + return $this->addError('The ' . $this->field . ' cannot be less than the default value.'); + } + + if ($this->type !== self::MAX && $value > $this->value) { + return $this->addError('The ' . $this->field . ' cannot be greater than the default value.'); + } + return true; + } +} diff --git a/validator/LengthValidator.php b/validator/LengthValidator.php new file mode 100644 index 00000000..f06df2fc --- /dev/null +++ b/validator/LengthValidator.php @@ -0,0 +1,119 @@ +getParams(); + if (empty($param) || !isset($param[$this->field])) { + if ($this->method != self::MAX_LENGTH) { + return $this->addError('The param :attribute not exists'); + } else { + return TRUE; + } + } + $value = $param[$this->field]; + if (is_null($value)) { + return $this->addError('The param :attribute is null'); + } + switch (strtolower($this->method)) { + case self::MAX_LENGTH: + return $this->maxLength($value); + break; + case self::MIN_LENGTH: + return $this->minLength($value); + break; + default: + return $this->defaultLength($value); + } + } + + /** + * @param $value + * @return bool + * + * 效验长度是否大于最大长度 + */ + private function maxLength($value) + { + if (is_array($value)) { + if (count($value) > $value) { + return $this->addError('The param :attribute length overflow'); + } + } else { + if (is_numeric($value) && strlen(floatval($value)) > $this->value) { + return $this->addError('The param :attribute length overflow'); + } + if (strlen($value) > $this->value) { + return $this->addError('The param :attribute length overflow'); + } + } + return TRUE; + } + + /** + * @param $value + * @return bool + * + * 效验长度是否小于最小长度 + */ + private function minLength($value) + { + if (is_array($value)) { + if (count($value) < $value) { + return $this->addError('The param :attribute length error'); + } + } else { + if (is_numeric($value) && strlen(floatval($value)) < $this->value) { + return $this->addError('The param :attribute length overflow'); + } + if (strlen($value) < $this->value) { + return $this->addError('The param :attribute length error'); + } + } + return TRUE; + } + + /** + * @param $value + * @return bool + * + * 效验长度是否小于最小长度 + */ + private function defaultLength($value) + { + if (is_array($value)) { + if (count($value) !== $value) { + return $this->addError('The param :attribute length error'); + } + } else { + if (is_numeric($value) && mb_strlen(floatval($value)) !== $this->value) { + return $this->addError('The param :attribute length overflow'); + } + if (mb_strlen($value) !== $this->value) { + return $this->addError('The param :attribute length error; ' . mb_strlen($value) . ':' . $this->value); + } + } + return TRUE; + } +} diff --git a/validator/RequiredValidator.php b/validator/RequiredValidator.php new file mode 100644 index 00000000..c2840b95 --- /dev/null +++ b/validator/RequiredValidator.php @@ -0,0 +1,32 @@ +getParams(); + if (is_numeric($param)) { + return true; + } + if (empty($param) || !isset($param[$this->field])) { + return $this->addError('The param :attribute not exists'); + } else { + return true; + } + } + +} diff --git a/validator/TypesOfValidator.php b/validator/TypesOfValidator.php new file mode 100644 index 00000000..4cdf760a --- /dev/null +++ b/validator/TypesOfValidator.php @@ -0,0 +1,149 @@ + 'json', + self::FLOAT => 'float', + self::ARRAY => 'array', + self::STRING => 'string', + self::INTEGER => 'integer', + self::SERIALIZE => 'serialize', + ]; + + /** @var string */ + public $method; + + + /** + * @return bool + */ + public function trigger() + { + if (!in_array($this->method, $this->types)) { + return true; + } + + $param = $this->getParams(); + if (empty($param) || !isset($param[$this->field])) { + return true; + } + + $value = $param[$this->field]; + + $method = $this->method . 'Format'; + + if ($value === null) { + return $this->addError('This ' . $this->field . ' is not an empty data.'); + } + + return $this->{$method}($value); + } + + /** + * @param $value + * @return bool + */ + public function jsonFormat($value) + { + if (!is_string($value) || is_numeric($value)) { + return $this->addError('The ' . $this->field . ' not is JSON data.'); + } + if (is_null(json_decode($value))) { + return $this->addError('The ' . $this->field . ' not is JSON data.'); + } + return true; + } + + /** + * @param $value + * @return bool + */ + public function serializeFormat($value) + { + if (!is_string($value) || is_numeric($value)) { + return $this->addError('The ' . $this->field . ' not is serialize data.'); + } + if (false === unserialize($value)) { + return $this->addError('The ' . $this->field . ' not is serialize data.'); + } + return true; + } + + /** + * @param $value + * @return bool + */ + public function arrayFormat($value) + { + if (!is_array($value)) { + return $this->addError('The ' . $this->field . ' not is array data.'); + } + return true; + } + + /** + * @param $value + * @return bool + */ + public function stringFormat($value) + { + if (is_array($value) || is_object($value) || is_bool($value)) { + return $this->addError('The ' . $this->field . ' not is string data.'); + } + return true; + } + + /** + * @param $value + * @return bool + */ + public function integerFormat($value) + { + $trim = trim($value); + if (!is_numeric($trim)) { + return $this->addError('The ' . $this->field . ' not is number data.'); + } + if ($trim != $value || intval($trim) != $value) { + return $this->addError('The ' . $this->field . ' not is number data.'); + } + + return true; + } + + /** + * @param $value + * @return bool + */ + public function floatFormat($value) + { + $trim = floatval(trim($value)); + if ($trim != $value || !is_float($trim)) { + return $this->addError('The ' . $this->field . ' not is float data.'); + } + return true; + } + +} diff --git a/validator/UniqueValidator.php b/validator/UniqueValidator.php new file mode 100644 index 00000000..aca837bf --- /dev/null +++ b/validator/UniqueValidator.php @@ -0,0 +1,42 @@ +getParams(); + if (empty($param) || !isset($param[$this->field])) { + return TRUE; + } + + if (empty($this->model)) { + return $this->addError('Model error.'); + } + + $model = $this->model; + if (!$this->model->getIsCreate()) { + return true; + } + if ($model::find()->where([$this->field => $param[$this->field]])->exists()) { + return $this->addError('The :attribute \'' . $param[$this->field] . '\' is exists!'); + } + return $this->isFail = TRUE; + } + + +} diff --git a/validator/Validator.php b/validator/Validator.php new file mode 100644 index 00000000..d4de8618 --- /dev/null +++ b/validator/Validator.php @@ -0,0 +1,208 @@ + [ + 'class' => 'validator\EmptyValidator', + 'method' => EmptyValidator::CAN_NOT_EMPTY, + ], + 'not null' => [ + 'class' => 'validator\EmptyValidator', + 'method' => EmptyValidator::CAN_NOT_NULL, + ], + 'required' => [ + 'class' => 'validator\RequiredValidator', + ], + 'enums' => [ + 'class' => 'validator\EnumValidator', + ], + 'unique' => [ + 'class' => 'validator\UniqueValidator', + ], + 'datetime' => [ + 'class' => 'validator\DatetimeValidator', + 'method' => DateTimeValidator::DATE_TIME, + ], + 'date' => [ + 'class' => 'validator\DatetimeValidator', + 'method' => DateTimeValidator::DATE, + ], + 'time' => [ + 'class' => 'validator\DatetimeValidator', + 'method' => DateTimeValidator::TIME, + ], + 'timestamp' => [ + 'class' => 'validator\DatetimeValidator', + 'method' => DateTimeValidator::STR_TO_TIME, + ], + 'string' => [ + 'class' => 'validator\TypesOfValidator', + 'method' => TypesOfValidator::STRING, + ], + 'int' => [ + 'class' => 'validator\TypesOfValidator', + 'method' => TypesOfValidator::INTEGER, + ], + 'min' => [ + 'class' => IntegerValidator::class + ], + 'max' => [ + 'class' => IntegerValidator::class + ], + 'json' => [ + 'class' => 'validator\TypesOfValidator', + 'method' => TypesOfValidator::JSON, + ], + 'float' => [ + 'class' => 'validator\TypesOfValidator', + 'method' => TypesOfValidator::FLOAT, + ], + 'array' => [ + 'class' => 'validator\TypesOfValidator', + 'method' => TypesOfValidator::ARRAY, + ], + 'serialize' => [ + 'class' => 'validator\TypesOfValidator', + 'method' => TypesOfValidator::SERIALIZE, + ], + 'maxLength' => [ + 'class' => 'validator\LengthValidator', + 'method' => 'max', + ], + 'minLength' => [ + 'class' => 'validator\LengthValidator', + 'method' => 'min', + ], + 'email' => [ + 'class' => 'validator\EmailValidator', + 'method' => 'email', + ], + 'length' => [ + 'class' => 'validator\LengthValidator', + 'method' => 'default', + ], + ]; + + /** + * @return Validator + */ + public static function getInstance() + { + if (static::$instance == null) { + static::$instance = new Validator(); + } + return static::$instance; + } + + /** + * @param $field + * @param $rules + * @return $this + * @throws \Exception + */ + public function make($field, $rules) + { + if (!is_array($field)) { + $field = [$field]; + } + + $param = $this->getParams(); + $model = $this->getModel(); + foreach ($field as $val) { + $this->createRule($val, $rules, $model, $param); + } + + return $this; + } + + /** + * @param $field + * @param $rule + * @param $model + * @param $param + * @throws \Exception + * ['maxLength'=>150, 'required', 'minLength' => 100] + */ + public function createRule($field, $rule, $model, $param) + { + $define = ['field' => $field]; + foreach ($rule as $key => $val) { + if (is_string($val) && method_exists($model, $val)) { + $this->validators[] = [$model, $val]; + continue; + } + if (is_string($key)) { + $type = strtolower($key); + $define['value'] = $val; + } else { + $type = strtolower($val); + } + + if (!isset($this->classMap[$type])) { + continue; + } + $constr = array_merge($this->classMap[$type], $define); + + /** @var BaseValidator $class */ + $class = Snowflake::createObject($constr); + $class->setParams($param); + $class->setModel($model); + + $this->validators[] = $class; + } + } + + /** + * @return bool + * @throws \Exception + */ + public function validation() + { + if (count($this->validators) < 1) { + return true; + } + + foreach ($this->validators as $val) { + if ($this->check($val)) { + continue; + } + $isTrue = false; + if ($val instanceof BaseValidator) { + $this->addError($val->getError()); + } + break; + } + $this->validators = []; + return !isset($isTrue); + } + + /** + * @param BaseValidator $val + * @return mixed + * @throws + */ + private function check($val) + { + if (is_callable($val, true)) { + return call_user_func($val, $this); + } + return $val->trigger(); + } + +}