├── src ├── Http │ ├── Exception │ │ ├── ServerException.php │ │ ├── RouterException.php │ │ └── ResponseException.php │ ├── Middlewares │ │ ├── Exception │ │ │ └── MiddlewareException.php │ │ └── Middleware.php │ ├── Router.php │ ├── Server.php │ ├── Globals.php │ ├── Response.php │ ├── Url.php │ └── Request.php ├── Libs │ ├── JWT │ │ ├── ExpiredException.php │ │ ├── BeforeValidException.php │ │ ├── SignatureInvalidException.php │ │ └── JWT.php │ ├── PhpToJs │ │ ├── Exception │ │ │ └── TransformerException.php │ │ └── Transformer.php │ ├── Image │ │ ├── Exception │ │ │ └── ImageException.php │ │ └── GD.php │ └── ClientURL │ │ └── ClientURL.php ├── File │ ├── Exception │ │ ├── LogException.php │ │ ├── CsvException.php │ │ ├── FileException.php │ │ └── ImageException.php │ ├── Log.php │ ├── Image.php │ ├── CSV.php │ └── File.php ├── IMDB │ ├── Exception │ │ └── ImdbException.php │ └── Redis.php ├── Models │ ├── Exception │ │ └── ModelException.php │ ├── Filterable.php │ └── Model.php ├── Storage │ ├── StorageInterface.php │ ├── Exception │ │ └── StorageException.php │ ├── StorageFactory.php │ └── LocalService.php ├── Db │ ├── DatabaseInterface.php │ ├── Exception │ │ └── DatabaseException.php │ ├── Driver.php │ ├── MySQL.php │ ├── PHPDataObjects.php │ └── Database.php ├── Views │ ├── Exception │ │ └── ViewException.php │ ├── ViewFactory.php │ └── View.php ├── Validation │ ├── Exception │ │ └── ValidationException.php │ └── Validator.php ├── Template │ ├── Exception │ │ └── TemplateException.php │ └── Template.php ├── Guard │ ├── Exception │ │ └── GuardException.php │ ├── Token.php │ └── Auth.php ├── Container │ ├── Exception │ │ └── ContainerException.php │ └── Container.php ├── Controllers │ ├── Exception │ │ └── ControllerException.php │ └── Controller.php ├── Support │ └── Facades │ │ └── Facade.php ├── Traits │ └── HasAttributes.php └── Helpers │ └── helpers.php ├── composer.json ├── README.md └── LICENSE /src/Http/Exception/ServerException.php: -------------------------------------------------------------------------------- 1 | $method(...$args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/File/Exception/FileException.php: -------------------------------------------------------------------------------- 1 | 8 | composer require cuongnd88/atom 9 | 10 | # Usage 11 | ## Single Point Entry 12 | #### index.php 13 |
14 | require __DIR__ . '/../vendor/autoload.php';
15 | 
16 | use Atom\Http\Server;
17 | 
18 | try {
19 |     $server = new Server(['env']);
20 |     $server->handle();
21 | } catch (Exception $e) {
22 |     echo $e->getMessage();
23 | }
24 | 
25 | 26 | For example of implementation and usage, please take a look at EzyCrazy project https://github.com/cuongnd88/ezycrazy which was developed using Atom framework 27 | -------------------------------------------------------------------------------- /src/Views/ViewFactory.php: -------------------------------------------------------------------------------- 1 | storageConfig = config('app.storage.'.$type); 18 | } 19 | 20 | /** 21 | * Open Storage Drive 22 | * @return mixed 23 | */ 24 | public function init() 25 | { 26 | switch ($this->storageConfig["driver"]) { 27 | case 'local': 28 | return new LocalService($this->storageConfig["path"]); 29 | break; 30 | case 's3': 31 | # code... 32 | break; 33 | default: 34 | throw new StorageException(StorageException::ERR_MSG_NOT_FOUND); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 cuongnd88 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Http/Router.php: -------------------------------------------------------------------------------- 1 | path = Globals::path(); 22 | } 23 | 24 | /** 25 | * Dispatch Router 26 | * @return array 27 | */ 28 | public function dispatchRouter() 29 | { 30 | $routeData = $this->identifyRouteData(); 31 | if (empty($routeData)) { 32 | throw new RouterException(RouterException::ERR_MSG_INVALID_ROUTE); 33 | } 34 | 35 | return $routeData; 36 | } 37 | 38 | /** 39 | * Get Route Data By Path 40 | * @return mixed 41 | */ 42 | public function getRouteDataByPath() 43 | { 44 | if (isApi()) { 45 | return route('api.' . $this->path); 46 | } 47 | return route('web.' . $this->path); 48 | } 49 | 50 | /** 51 | * Identify Route data 52 | * 53 | * @return array 54 | */ 55 | public function identifyRouteData() 56 | { 57 | $patternCurrentUri = preg_replace("/[0-9]+/", '#', $this->path); 58 | $routers = isApi() ? route('api') : route('web'); 59 | foreach ($routers as $route => $data) { 60 | $route = preg_replace('/\{[a-zA-Z]+\}+/', '#', $route); 61 | if ($route == $patternCurrentUri) { 62 | return $data; 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/IMDB/Redis.php: -------------------------------------------------------------------------------- 1 | env('REDIS_SCHEMA'), 18 | 'host' => env('REDIS_HOST'), 19 | 'port' => env('REDIS_PORT'), 20 | 'database' => env('REDIS_DATABASE') ? env('REDIS_DATABASE') : 0 21 | ]; 22 | parent::__construct($connection); 23 | $this->connect(); 24 | } catch (Predis\Connection\ConnectionException $e) { 25 | throw new ImdbException(ImdbException::ERR_REDIS_CONNECTION_FAIL.': '.$e->getMessage()); 26 | } 27 | } 28 | 29 | /** 30 | * Filter existed keys 31 | * @param array $keys 32 | * @return array 33 | */ 34 | public function keyFilter(array $keys) 35 | { 36 | foreach ($keys as $key => $value) { 37 | if (false === (bool) $this->exists($value)) { 38 | unset($keys[$key]); 39 | } 40 | } 41 | return $keys; 42 | } 43 | 44 | /** 45 | * Scan and list all keys by type 46 | * @return array 47 | */ 48 | public function scanKeys() 49 | { 50 | $result = []; 51 | $keys = $this->keys('*'); 52 | foreach ($keys as $key) { 53 | $type = $this->type($key); 54 | $result[(string) $type][] = $key; 55 | } 56 | return $result; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/Models/Filterable.php: -------------------------------------------------------------------------------- 1 | prepareFilterable($params); 16 | $this->where($conditions); 17 | return $this; 18 | } 19 | 20 | /** 21 | * Prepare filterable 22 | * @param array $params Params 23 | * @return array 24 | */ 25 | public function prepareFilterable(array $params) 26 | { 27 | $conditions = []; 28 | foreach ($params as $key => $value) { 29 | if (empty($this->filterable)) { 30 | break; 31 | } 32 | if (!is_array($this->filterable)) { 33 | throw new ModelException(ModelException::ERR_MSG_INVALID_FILTERABLE); 34 | } 35 | if (in_array($key, $this->filterable)) { 36 | $conditions[] = [$key, $value]; 37 | continue; 38 | } 39 | if (array_key_exists($key, $this->filterable)) { 40 | if (array_key_exists('LIKE', $this->filterable[$key])) { 41 | $value = str_replace('{'.$key.'}', $value, $this->filterable[$key]['LIKE']); 42 | $conditions[] = [$key, 'LIKE', $value]; 43 | continue; 44 | } 45 | $conditions[] = [$key, $this->filterable[$key], $value]; 46 | continue; 47 | } 48 | } 49 | return $conditions; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Guard/Token.php: -------------------------------------------------------------------------------- 1 | loadConfig($files); 29 | $this->router = new Router(); 30 | $this->controllerMaster = new ControllerMaster(); 31 | date_default_timezone_set(env('TIMEZONE')); 32 | } 33 | 34 | /** 35 | * Handle progress 36 | * @return void 37 | */ 38 | public function handle() 39 | { 40 | try { 41 | $routeData = $this->router->dispatchRouter(); 42 | 43 | $this->handleMiddlewares($routeData); 44 | 45 | $this->controllerMaster->loadController($routeData); 46 | } catch (\Exception $e) { 47 | echo $e->getMessage(); 48 | } 49 | } 50 | 51 | /** 52 | * Load system configuration 53 | * @param array $files 54 | * @return void 55 | */ 56 | public function loadConfig($files) 57 | { 58 | foreach ($files as $file) { 59 | config($file); 60 | } 61 | } 62 | 63 | /** 64 | * Handle Middlewares 65 | * @param array $routeData Route Data 66 | * @return void 67 | */ 68 | public function handleMiddlewares($routeData) 69 | { 70 | if (false === isset($routeData['middleware']) && empty($routeData['middleware'])) { 71 | return; 72 | } 73 | (new Middleware($routeData['middleware']))->execute(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Template/Template.php: -------------------------------------------------------------------------------- 1 | templates = $templates; 29 | $this->data = $data; 30 | } 31 | 32 | /** 33 | * Set templates 34 | * @param array $templates Templates 35 | * @return $this 36 | */ 37 | public function setTemplate(array $templates) 38 | { 39 | $this->templates = $templates; 40 | return $this; 41 | } 42 | 43 | /** 44 | * Set Data 45 | * @param array $data Data 46 | * @return $this 47 | */ 48 | public function setData(array $data) 49 | { 50 | $this->data = $data; 51 | return $this; 52 | } 53 | 54 | /** 55 | * Template render 56 | * @return [type] [description] 57 | */ 58 | public function render() 59 | { 60 | ob_start(); 61 | 62 | extract($this->data); 63 | 64 | foreach ($this->templates as $template) { 65 | $file = $this->loadFile($template); 66 | include($file); 67 | } 68 | 69 | $html = ob_get_clean(); 70 | 71 | return $html; 72 | } 73 | 74 | /** 75 | * Load file 76 | * @param string $template 77 | * @return string 78 | */ 79 | public function loadFile($template) 80 | { 81 | $file = VIEW_PATH . str_replace('.', '/', $template) . '.php'; 82 | if (!file_exists($file)) { 83 | throw new TemplateException(TemplateException::ERR_MSG_TEMPLATE_NOT_EXISTS); 84 | } 85 | return $file; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Storage/LocalService.php: -------------------------------------------------------------------------------- 1 | storage = $path; 19 | } 20 | 21 | /** 22 | * Upload file 23 | * @param string $directory /storage/images/test.jpg 24 | * @param string $file 25 | * @return void 26 | */ 27 | public function upload(string $directory, string $file) 28 | { 29 | try { 30 | $fileData = file_get_contents($file); 31 | file_put_contents($directory, $fileData); 32 | } catch (\Exception $e) { 33 | throw new StorageException(StorageException::ERR_MSG_UPLOAD_FAIL); 34 | } 35 | } 36 | 37 | /** 38 | * Get Full URL 39 | * @param string|null $directory 40 | * @return string 41 | */ 42 | public function getFullUrl(string $directory = null) 43 | { 44 | $child = array_filter(explode('/', $directory)); 45 | $parent = array_shift($child); 46 | if ($parent == 'public') { 47 | return public_path('/'.implode('/', $child)); 48 | } 49 | return $directory ? storage_path($directory) : storage_path(); 50 | } 51 | 52 | /** 53 | * [remove description] 54 | * @return [type] [description] 55 | */ 56 | public function remove(string $fileName) 57 | { 58 | unlink($fileName); 59 | } 60 | 61 | /** 62 | * Check Directory is existed 63 | * @param string $directory 64 | * @return mixed 65 | */ 66 | public function checkDirectory(string $directory) 67 | { 68 | if (false === file_exists($directory)) { 69 | throw new StorageException(StorageException::ERR_MSG_NOT_FOUND); 70 | } 71 | 72 | return; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Models/Model.php: -------------------------------------------------------------------------------- 1 | table; 19 | } 20 | 21 | /** 22 | * Convert data to array 23 | * 24 | * @return array 25 | */ 26 | public function toArray() 27 | { 28 | return (array) $this->attributes; 29 | } 30 | 31 | /** 32 | * Save (insert or dupdate) 33 | * 34 | * @return void 35 | */ 36 | public function save() 37 | { 38 | $this->insertDuplicate($this->getAttributes()); 39 | } 40 | 41 | /** 42 | * Create new 43 | * 44 | * @param array $data [description] 45 | * 46 | * @return [type] [description] 47 | */ 48 | public function create(array $data) 49 | { 50 | $id = $this->insert($data); 51 | $this->mapAttributes(array_merge($data, ["id" => $id])); 52 | return $this; 53 | } 54 | 55 | /** 56 | * Find all records by id 57 | * 58 | * @param [type] $id [description] 59 | * 60 | * @return [type] [description] 61 | */ 62 | public function find($id) 63 | { 64 | if (is_array($id)) { 65 | return $this->whereIn("id", $id)->get(); 66 | } else { 67 | return $this->where(["id", $id])->first(); 68 | } 69 | } 70 | 71 | /** 72 | * Delete multi records 73 | * 74 | * @param [type] $id [description] 75 | * 76 | * @return [type] [description] 77 | */ 78 | public function destroy($id) 79 | { 80 | if (is_array($id)) { 81 | array_walk($id, function ($item) { 82 | $this->where(["id", $item])->delete(); 83 | }); 84 | } else { 85 | $this->where(["id", $id])->delete(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/File/Log.php: -------------------------------------------------------------------------------- 1 | driver = $driver ?? env('DB_CONNECTION'); 58 | $this->host = $host ?? env('DB_HOST'); 59 | $this->user = $user ?? env('DB_USER'); 60 | $this->password = $password ?? env('DB_PASSWORD'); 61 | $this->database = $database ?? env('DB_NAME'); 62 | $this->port = $port ?? env('DB_PORT'); 63 | } 64 | 65 | /** 66 | * Create connection 67 | * 68 | * @return Object 69 | * @throws Exception 70 | */ 71 | public function createConnection() 72 | { 73 | switch ($this->driver) { 74 | case 'mysql': 75 | return new MySQL($this->host, $this->user, $this->password, $this->database, $this->port); 76 | default: 77 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_DRIVER); 78 | break; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Libs/PhpToJs/Transformer.php: -------------------------------------------------------------------------------- 1 | file = config('phpToJs.transform_php_to_js_file'); 28 | $this->namespace = config('phpToJs.namespace'); 29 | } 30 | 31 | /** 32 | * Cast variables 33 | * @param array $variables 34 | * @return void 35 | */ 36 | protected function cast(array $variables) 37 | { 38 | $tmp = []; 39 | foreach ($variables as $key => $value) { 40 | $tmp[] = $this->initializeVariable($key, $value); 41 | } 42 | 43 | $js = $this->constructNamespace() . implode('', $tmp); 44 | $this->bind($js); 45 | } 46 | 47 | /** 48 | * Bind variables to file 49 | * @param string $js JS variables 50 | * @return void 51 | */ 52 | protected function bind($js) 53 | { 54 | $file = VIEW_PATH . str_replace('.', '/', $this->file) . ".php"; 55 | $content = ''; 56 | file_put_contents($file, $content); 57 | $_SESSION['jsVariables'] = ""; 58 | } 59 | 60 | /** 61 | * Set namespace 62 | * @return string 63 | */ 64 | protected function constructNamespace() 65 | { 66 | if ($this->namespace == 'window') { 67 | return ''; 68 | } 69 | 70 | return "window.{$this->namespace} = window.{$this->namespace} || {};"; 71 | } 72 | 73 | /** 74 | * Initialize Variable 75 | * @param string $key 76 | * @param mixed $value 77 | * @return string 78 | */ 79 | protected function initializeVariable($key, $value) 80 | { 81 | return "{$this->namespace}.{$key} = {$this->convertToJavaScript($value)};"; 82 | } 83 | 84 | /** 85 | * Convert to JS 86 | * @param mixed $value 87 | * @return json 88 | */ 89 | protected function convertToJavaScript($value) 90 | { 91 | return json_encode($value); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/File/Image.php: -------------------------------------------------------------------------------- 1 | gd = new GD(); 26 | 27 | } 28 | 29 | /** 30 | * Upload Image 31 | * @param string $directory 32 | * @param array $file 33 | * @param string $fileName 34 | * @return void 35 | */ 36 | protected function upload(string $directory, array $file, string $fileName = null) 37 | { 38 | list($storagePath, $core, $imageType) = $this->process($directory, $file, $fileName); 39 | $this->gd->create($storagePath, $core, $imageType); 40 | return end(explode('/', $storagePath)); 41 | } 42 | 43 | /** 44 | * Upload and Resize 45 | * @param string $directory 46 | * @param array $file 47 | * @param array $size 48 | * @param string|null $fileName 49 | * @return void 50 | */ 51 | protected function uploadResize(string $directory, array $file, array $size, string $fileName = null) 52 | { 53 | if (empty($size)) { 54 | throw new ImageException(ImageException::ERR_MSG_BAD_REQUEST); 55 | } 56 | 57 | list($storagePath, $core, $imageType) = $this->process($directory, $file, $fileName); 58 | $this->gd->createAndResize($storagePath, $core, $imageType, $size); 59 | return end(explode('/', $storagePath)); 60 | } 61 | 62 | /** 63 | * Get Image Metadata 64 | * @param array $file 65 | * @return array 66 | */ 67 | protected function getMetadata(array $file) 68 | { 69 | $this->parse($file); 70 | $exif = exif_read_data($this->core(), 0, true); 71 | return $exif ? $exif : []; 72 | } 73 | 74 | /** 75 | * Delete file 76 | * @param string $fileName 77 | * @return void 78 | */ 79 | protected function delete(string $fileName) 80 | { 81 | if (false === file_exists($fileName)) { 82 | throw new ImageException(ImageException::ERR_MSG_FILE_NOT_EXIST); 83 | } 84 | $this->storage->remove($fileName); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Db/MySQL.php: -------------------------------------------------------------------------------- 1 | mysqli = new mysqli($host, $user, $password, $db, $port); 25 | if ($this->mysqli->connect_errno) { 26 | throw new DatabaseException(DatabaseException::ERR_MSG_CONNECTION_FAIL); 27 | } 28 | } 29 | 30 | /** 31 | * Execute MySQL query 32 | * @param string $query 33 | * @return void 34 | */ 35 | public function query(string $query) 36 | { 37 | return $this->mysqli->query($query); 38 | } 39 | 40 | /** 41 | * MySQL escapse 42 | * @param mixed $sql 43 | * @return mixed 44 | */ 45 | public function escape($sql) 46 | { 47 | return $this->mysqli->real_escape_string($sql); 48 | } 49 | 50 | /** 51 | * Return MySQL request to Array 52 | * @param mysql_result $result 53 | * @return array 54 | */ 55 | public function resultToArray($result) 56 | { 57 | $arr = []; 58 | while ($row = $result->fetch_assoc()) { 59 | $arr[] = $row; 60 | } 61 | 62 | return $arr; 63 | } 64 | 65 | /** 66 | * MySQL error 67 | * @return string 68 | */ 69 | public function error() 70 | { 71 | return $this->mysqli->error; 72 | } 73 | 74 | /** 75 | * MySQL Last Insert Id 76 | * @return int 77 | */ 78 | public function lastestInsertId() 79 | { 80 | return (int) $this->mysqli->insert_id; 81 | } 82 | 83 | /** 84 | * Begin transaction 85 | * @return bool 86 | */ 87 | public function beginTransaction() 88 | { 89 | return $this->mysqli->begin_transaction(); 90 | } 91 | 92 | /** 93 | * Commit transaction 94 | * @return bool 95 | */ 96 | public function commit() 97 | { 98 | return $this->mysqli->commit(); 99 | } 100 | 101 | /** 102 | * Roll back current transaction 103 | * @return bool 104 | */ 105 | public function rollBack() 106 | { 107 | return $this->mysqli->rollback(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Guard/Auth.php: -------------------------------------------------------------------------------- 1 | user; 25 | } 26 | return $user[0]; 27 | } catch (\Exception $e) { 28 | Globals::freeSession(); 29 | Response::redirect(config('app.auth.response.fail')); 30 | } 31 | } 32 | 33 | /** 34 | * Login 35 | * @param array $request 36 | * @param array $response 37 | * @return void 38 | */ 39 | public static function login(array $request, array $response = []) 40 | { 41 | $auth = config('app.auth'); 42 | $guards = explode(',', $auth['guard']); 43 | $checkGuard = array_diff($guards, array_keys($request)); 44 | if (false === empty($checkGuard)) { 45 | throw new GuardException(GuardException::ERR_MSG_INVALID_GUARD_KEYS); 46 | } 47 | 48 | list($guardId, $guardPasswd) = $guards; 49 | empty($response) ? extract(config('app.auth.response')) : extract($response); 50 | $condition = [ 51 | [$guardId, '=', $request[$guardId]], 52 | [$guardPasswd, '=', $request[$guardPasswd]] 53 | ]; 54 | 55 | $user = (new Database())->table($auth['table'])->select()->where($condition)->first(); 56 | if (empty($user)) { 57 | Globals::setSession('error', $error); 58 | Response::redirect($fail); 59 | } 60 | 61 | //Store guardId 62 | Globals::setSession($guardId, $request[$guardId]); 63 | //Store user 64 | Globals::setSession('user', $user); 65 | //Store user_token 66 | $payload = [ 67 | 'exp' => Token::expire(), 68 | 'iat' => strtotime('now'), 69 | 'user' => $user, 70 | ]; 71 | $token = Token::generate($payload); 72 | Globals::setSession('user_token', $token); 73 | 74 | if (empty($success)) { 75 | return Response::toJson(['Token' => $token]); 76 | } else { 77 | Response::redirect($success); 78 | } 79 | } 80 | 81 | /** 82 | * Check authorization 83 | * @return mixed 84 | */ 85 | public static function check() 86 | { 87 | try { 88 | $token = Token::get(); 89 | $payload = Token::decode($token); 90 | return; 91 | } catch (\Exception $e) { 92 | Globals::freeSession(); 93 | Response::redirect(config('app.auth.response.fail')); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/Container/Container.php: -------------------------------------------------------------------------------- 1 | instances[$abstract] = $concrete; 25 | } 26 | 27 | /** 28 | * @param $abstract 29 | * @param array $parameters 30 | * 31 | * @return mixed|null|object 32 | * @throws Exception 33 | */ 34 | public function get($abstract, $parameters = []) 35 | { 36 | // if we don't have it, just register it 37 | if (!isset($this->instances[$abstract])) { 38 | $this->set($abstract); 39 | } 40 | 41 | return $this->resolve($this->instances[$abstract], $parameters); 42 | } 43 | 44 | /** 45 | * Resolve single 46 | * 47 | * @param $concrete 48 | * @param $parameters 49 | * 50 | * @return mixed|object 51 | * @throws Exception 52 | */ 53 | public function resolve($concrete, $parameters = null) 54 | { 55 | if ($concrete instanceof Closure) { 56 | return $concrete($this, $parameters); 57 | } 58 | 59 | $reflector = new ReflectionClass($concrete); 60 | // check if class is instantiable 61 | if (!$reflector->isInstantiable()) { 62 | throw new ContainerException(vsprintf(ContainerException::ERR_MSG_CLASS_NOT_INSTANTIABLE, [$concrete])); 63 | } 64 | 65 | // get class constructor 66 | $constructor = $reflector->getConstructor(); 67 | if (is_null($constructor)) { 68 | // get new instance from class 69 | return $reflector->newInstance(); 70 | } 71 | 72 | // get constructor params 73 | $parameters = $constructor->getParameters(); 74 | $dependencies = $this->getDependencies($parameters); 75 | 76 | // get new instance with dependencies resolved 77 | return $reflector->newInstanceArgs($dependencies); 78 | } 79 | 80 | /** 81 | * Get all dependencies resolved 82 | * 83 | * @param $parameters 84 | * 85 | * @return array 86 | * @throws Exception 87 | */ 88 | public function getDependencies($parameters) 89 | { 90 | $dependencies = []; 91 | foreach ($parameters as $parameter) { 92 | // get the type hinted class 93 | $dependency = $parameter->getClass(); 94 | if ($dependency === NULL) { 95 | // check if default value for a parameter is available 96 | if ($parameter->isDefaultValueAvailable()) { 97 | // get default value of parameter 98 | $dependencies[] = $parameter->getDefaultValue(); 99 | } else { 100 | throw new ContainerException(vsprintf(ContainerException::ERR_MSG_DEPENDENCY_NOT_RESOLVE, [$parameter->name])); 101 | } 102 | } else { 103 | // get dependency resolved 104 | $dependencies[] = $this->get($dependency->name); 105 | } 106 | } 107 | 108 | return $dependencies; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Http/Globals.php: -------------------------------------------------------------------------------- 1 | $method(...$args); 83 | } 84 | 85 | /** 86 | * Start session 87 | */ 88 | public static function sessionStart() 89 | { 90 | session_start(); 91 | } 92 | 93 | /** 94 | * Get session id 95 | * @return string 96 | */ 97 | public static function sessionID() 98 | { 99 | static::checkSession(); 100 | 101 | return session_id(); 102 | } 103 | 104 | /** 105 | * Check is session status 106 | */ 107 | private static function checkSession() 108 | { 109 | if (session_status() == PHP_SESSION_NONE) { 110 | session_start(); 111 | } 112 | } 113 | 114 | /** 115 | * @param $name 116 | * @param $val 117 | */ 118 | public static function setSession($name = null, $val = null) 119 | { 120 | if ($name) { 121 | static::checkSession(); 122 | $_SESSION[$name] = $val; 123 | } 124 | } 125 | 126 | /** 127 | * @param string $name 128 | * @return bool|null 129 | */ 130 | public static function session($name = null) 131 | { 132 | static::checkSession(); 133 | if (!$name) { 134 | return $_SESSION; 135 | } 136 | if (isset($_SESSION[$name])) { 137 | return $_SESSION[$name]; 138 | } 139 | 140 | return false; 141 | } 142 | 143 | /** 144 | * Free all session variables 145 | * @return void 146 | */ 147 | public static function freeSession() 148 | { 149 | session_unset(); 150 | } 151 | 152 | /** 153 | * Destroy session variables 154 | * @return void 155 | */ 156 | public static function destroySession() 157 | { 158 | session_destroy(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/Traits/HasAttributes.php: -------------------------------------------------------------------------------- 1 | attributes[$key]; 21 | } 22 | 23 | /** 24 | * Assigns a value to the specified data 25 | * 26 | * @param string The data key to assign the value to 27 | * @param mixed The value to set 28 | * @access public 29 | */ 30 | public function __set($key,$value) { 31 | $this->attributes[$key] = $value; 32 | } 33 | 34 | /** 35 | * Whether or not an data exists by key 36 | * 37 | * @param string An data key to check for 38 | * @access public 39 | * @return boolean 40 | * @abstracting ArrayAccess 41 | */ 42 | public function __isset ($key) { 43 | return isset($this->attributes[$key]); 44 | } 45 | 46 | /** 47 | * Unsets an data by key 48 | * 49 | * @param string The key to unset 50 | * @access public 51 | */ 52 | public function __unset($key) { 53 | unset($this->attributes[$key]); 54 | } 55 | 56 | /** 57 | * Assigns a value to the specified offset 58 | * 59 | * @param string The offset to assign the value to 60 | * @param mixed The value to set 61 | * @access public 62 | * @abstracting ArrayAccess 63 | */ 64 | public function offsetSet($offset,$value) { 65 | if (is_null($offset)) { 66 | $this->attributes[] = $value; 67 | } else { 68 | $this->attributes[$offset] = $value; 69 | } 70 | } 71 | 72 | /** 73 | * Whether or not an offset exists 74 | * 75 | * @param string An offset to check for 76 | * @access public 77 | * @return boolean 78 | * @abstracting ArrayAccess 79 | */ 80 | public function offsetExists($offset) { 81 | return isset($this->attributes[$offset]); 82 | } 83 | 84 | /** 85 | * Unsets an offset 86 | * 87 | * @param string The offset to unset 88 | * @access public 89 | * @abstracting ArrayAccess 90 | */ 91 | public function offsetUnset($offset) { 92 | if ($this->offsetExists($offset)) { 93 | unset($this->attributes[$offset]); 94 | } 95 | } 96 | 97 | /** 98 | * Returns the value at specified offset 99 | * 100 | * @param string The offset to retrieve 101 | * @access public 102 | * @return mixed 103 | * @abstracting ArrayAccess 104 | */ 105 | public function offsetGet($offset) { 106 | return $this->offsetExists($offset) ? $this->attributes[$offset] : null; 107 | } 108 | 109 | /** 110 | * Map data to object attributes 111 | * 112 | * @param mixed $data [description] 113 | * 114 | * @return void 115 | */ 116 | public function mapAttributes($data) 117 | { 118 | $this->attributes = $data; 119 | } 120 | 121 | /** 122 | * Set attributes 123 | * 124 | * @param [type] $data [description] 125 | */ 126 | public function setAttributes($data) 127 | { 128 | $this->mapAttributes($data); 129 | } 130 | 131 | /** 132 | * Get attributes 133 | * 134 | * @return mixed 135 | */ 136 | public function getAttributes() 137 | { 138 | return $this->attributes; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Http/Middlewares/Middleware.php: -------------------------------------------------------------------------------- 1 | impMiddlewares = $impMiddlewares; 34 | $this->middlewares = config('middleware.routeMiddlewares'); 35 | $this->priorityMiddlewares = config('middleware.priorityMiddlewares'); 36 | } 37 | 38 | /** 39 | * Execute middlewares 40 | * @return void 41 | */ 42 | public function execute() 43 | { 44 | $this->checkMiddlewares(); 45 | $this->sortMiddlewares(); 46 | foreach ($this->impMiddlewares as $middlewareAlias) { 47 | $middlewareClass = $this->middlewares[$middlewareAlias]; 48 | $this->runMiddleware($middlewareClass); 49 | } 50 | } 51 | 52 | /** 53 | * Run middleware 54 | * @param string $class Middleware class name 55 | * @return void 56 | */ 57 | public function runMiddleware($class) 58 | { 59 | $middleware = $this->loadMiddlewares($class); 60 | $method = 'handle'; 61 | 62 | if (!method_exists($middleware, $method)) { 63 | throw new MiddlewareException(MiddlewareException::ERR_MSG_MIDDLEWARE_NOT_EXISTS); 64 | } 65 | 66 | $result = $middleware->$method(); 67 | 68 | if ($result === false) { 69 | throw new MiddlewareException(MiddlewareException::ERR_MSG_MIDDLEWARE_FAIL); 70 | } 71 | } 72 | 73 | /** 74 | * Load middleware class 75 | * @param string $class Middleware class name 76 | * @return object 77 | */ 78 | public function loadMiddlewares($class) 79 | { 80 | $file = MIDDLEWARE_PATH . $class . '.php'; 81 | if (!file_exists($file)) { 82 | throw new MiddlewareException(MiddlewareException::ERR_MSG_MIDDLEWARE_NOT_EXISTS); 83 | } 84 | $class = str_replace("/", "\\", $class); 85 | 86 | require_once($file); 87 | 88 | $middlewareClass = env('APP_NAMESPACE').'\\Middlewares\\'.$class; 89 | $middleware = new $middlewareClass(); 90 | 91 | if ($middleware) { 92 | return $middleware; 93 | } 94 | throw new MiddlewareException(MiddlewareException::ERR_MSG_MIDDLEWARE_FAIL); 95 | } 96 | 97 | /** 98 | * Check Middlewares 99 | * @return void 100 | */ 101 | public function checkMiddlewares() 102 | { 103 | if (empty($this->middlewares)) { 104 | throw new MiddlewareException(MiddlewareException::ERR_MSG_NO_MIDDLEWARES); 105 | } 106 | 107 | $diff = array_diff($this->impMiddlewares, array_keys($this->middlewares)); 108 | if (false === empty($diff)) { 109 | throw new MiddlewareException(MiddlewareException::ERR_MSG_INVALID_MIDDLEWARES); 110 | } 111 | } 112 | 113 | /** 114 | * Sort middleware 115 | * @return void 116 | */ 117 | public function sortMiddlewares() 118 | { 119 | $highPriority = array_intersect(array_keys($this->priorityMiddlewares), $this->impMiddlewares); 120 | $tmp = array_diff($this->impMiddlewares, $highPriority); 121 | $this->impMiddlewares = array_merge($highPriority, $tmp); 122 | unset($highPriority); 123 | unset($tmp); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Libs/Image/GD.php: -------------------------------------------------------------------------------- 1 | imageCreateFromType($imageType, $file); 22 | imagecopy($dstImg, $srcImg, 0, 0, 0, 0, $width, $height); 23 | $this->outputImageByType($dstImg, $storagePath, $imageType); 24 | } catch (\Exception $e) { 25 | throw new ImageException(ImageException::ERR_MSG_UPLOAD_FAIL); 26 | } 27 | } 28 | 29 | /** 30 | * Create and Resize Image 31 | * @param string $storagePath /storage/images/test.jpg 32 | * @param string $file 33 | * @param string $imageType 34 | * @param array $size 35 | * @return void 36 | */ 37 | public function createAndResize(string $storagePath, string $file, string $imageType, array $size) 38 | { 39 | list($width, $height, $widthOrg, $heightOrg) = $this->resizeInfo($file, $size); 40 | 41 | try { 42 | $dstImg = imagecreatetruecolor($width, $height); 43 | $srcImg = $this->imageCreateFromType($imageType, $file); 44 | imagecopyresampled($dstImg, $srcImg, 0, 0, 0, 0, $width, $height, $widthOrg, $heightOrg); 45 | $this->outputImageByType($dstImg, $storagePath, $imageType); 46 | } catch (\Exception $e) { 47 | throw new ImageException(ImageException::ERR_MSG_UPLOAD_FAIL); 48 | } 49 | } 50 | 51 | /** 52 | * Get Resize Info 53 | * @param string $file 54 | * @param array $size 55 | * @return array 56 | */ 57 | public function resizeInfo(string $file, array $size) 58 | { 59 | list($width, $height) = $size; 60 | if (false === is_numeric($width) || false === is_numeric($height)) { 61 | throw new ImageException(ImageException::ERR_MSG_BAD_REQUEST); 62 | } 63 | list($widthOrg, $heightOrg) = getimagesize($file); 64 | 65 | $ratioOrg = $widthOrg / $heightOrg; 66 | if ($width / $height > $ratioOrg) { 67 | $width = $height * $ratioOrg; 68 | } else { 69 | $height = $width / $ratioOrg; 70 | } 71 | 72 | return [$width, $height, $widthOrg, $heightOrg]; 73 | } 74 | 75 | /** 76 | * Create Image From Type 77 | * @param string $type 78 | * @param string $file 79 | * @return mixed 80 | */ 81 | protected function imageCreateFromType(string $type, string $file) 82 | { 83 | switch ($type) { 84 | case 'png': 85 | $image = imagecreatefrompng($file); 86 | break; 87 | case 'jpg': 88 | case 'jpeg': 89 | $image = imagecreatefromjpeg($file); 90 | break; 91 | case 'gif': 92 | $image = imagecreatefromgif($file); 93 | break; 94 | default: 95 | throw new ImageException(ImageException::ERR_MSG_UNKNOW_FILE); 96 | } 97 | 98 | return $image; 99 | } 100 | 101 | /** 102 | * Output Image By Type 103 | * @param mixed $image 104 | * @param string $path 105 | * @param string $type 106 | * @return void 107 | */ 108 | protected function outputImageByType($image, $path, $type) 109 | { 110 | switch ($type) { 111 | case 'png': 112 | imagepng($image, $path); 113 | break; 114 | case 'jpg': 115 | case 'jpeg': 116 | imagejpeg($image, $path); 117 | break; 118 | case 'gif': 119 | imagegif($image, $path); 120 | break; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/File/CSV.php: -------------------------------------------------------------------------------- 1 | 'Maximum stack depth exceeded', 18 | JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', 19 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 20 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', 21 | JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3 22 | ); 23 | throw new DomainException( 24 | isset($messages[$errno]) 25 | ? $messages[$errno] 26 | : 'Unknown JSON error: ' . $errno 27 | ); 28 | } 29 | 30 | /** 31 | * Convert to Json 32 | * @param mixed $values 33 | * @param int $code 34 | * @param int $option 35 | * @return json 36 | */ 37 | public static function toJson($values, $code = null, $option = JSON_UNESCAPED_UNICODE) 38 | { 39 | $json = json_encode($values, $option); 40 | if ($errno = json_last_error()) { 41 | static::handleJsonError($errno); 42 | } 43 | // static::responseCode($code); 44 | print_r($json); 45 | } 46 | 47 | /** 48 | * Redirect 49 | * @param string $uri 50 | * @param array $data 51 | * @return void 52 | */ 53 | public static function redirect(string $uri, array $data = []) 54 | { 55 | if (!is_array($data)) { 56 | throw new ResponseException(ResponseException::ERR_MSG_INVALID_ARGUMENTS); 57 | } 58 | $server = Globals::server(); 59 | $params = empty($data) ? '': '?'.http_build_query($data); 60 | $uri = rtrim(dirname($server["PHP_SELF"]), '/\\') . $uri; 61 | $url = $server['REQUEST_SCHEME'] . '://' . $server['HTTP_HOST'] . $uri . $params; 62 | header("Location: {$url}"); 63 | exit(); 64 | } 65 | 66 | /** 67 | * Set Http Response Code 68 | * @param int|null $code 69 | * @return void 70 | */ 71 | public static function responseCode($code = null) 72 | { 73 | if ($code !== null) { 74 | if (empty(static::$phrases[$code])) { 75 | throw new ResponseException(ResponseException::ERR_MSG_INVALID_HTTP_CODE); 76 | } 77 | 78 | http_response_code($code); 79 | } 80 | } 81 | 82 | /** 83 | * Http Codes 84 | * @var $phrases 85 | */ 86 | protected static $phrases = [ 87 | 100 => 'Continue', 88 | 101 => 'Switching Protocols', 89 | 102 => 'Processing', 90 | 200 => 'OK', 91 | 201 => 'Created', 92 | 202 => 'Accepted', 93 | 203 => 'Non-Authoritative Information', 94 | 204 => 'No Content', 95 | 205 => 'Reset Content', 96 | 206 => 'Partial Content', 97 | 207 => 'Multi-status', 98 | 208 => 'Already Reported', 99 | 300 => 'Multiple Choices', 100 | 301 => 'Moved Permanently', 101 | 302 => 'Found', 102 | 303 => 'See Other', 103 | 304 => 'Not Modified', 104 | 305 => 'Use Proxy', 105 | 306 => 'Switch Proxy', 106 | 307 => 'Temporary Redirect', 107 | 400 => 'Bad Request', 108 | 401 => 'Unauthorized', 109 | 402 => 'Payment Required', 110 | 403 => 'Forbidden', 111 | 404 => 'Not Found', 112 | 405 => 'Method Not Allowed', 113 | 406 => 'Not Acceptable', 114 | 407 => 'Proxy Authentication Required', 115 | 408 => 'Request Time-out', 116 | 409 => 'Conflict', 117 | 410 => 'Gone', 118 | 411 => 'Length Required', 119 | 412 => 'Precondition Failed', 120 | 413 => 'Request Entity Too Large', 121 | 414 => 'Request-URI Too Large', 122 | 415 => 'Unsupported Media Type', 123 | 416 => 'Requested range not satisfiable', 124 | 417 => 'Expectation Failed', 125 | 418 => 'I\'m a teapot', 126 | 422 => 'Unprocessable Entity', 127 | 423 => 'Locked', 128 | 424 => 'Failed Dependency', 129 | 425 => 'Unordered Collection', 130 | 426 => 'Upgrade Required', 131 | 428 => 'Precondition Required', 132 | 429 => 'Too Many Requests', 133 | 431 => 'Request Header Fields Too Large', 134 | 451 => 'Unavailable For Legal Reasons', 135 | 500 => 'Internal Server Error', 136 | 501 => 'Not Implemented', 137 | 502 => 'Bad Gateway', 138 | 503 => 'Service Unavailable', 139 | 504 => 'Gateway Time-out', 140 | 505 => 'HTTP Version not supported', 141 | 506 => 'Variant Also Negotiates', 142 | 507 => 'Insufficient Storage', 143 | 508 => 'Loop Detected', 144 | 511 => 'Network Authentication Required', 145 | ]; 146 | } 147 | -------------------------------------------------------------------------------- /src/Http/Url.php: -------------------------------------------------------------------------------- 1 | key = env('APP_KEY'); 24 | } 25 | 26 | /** 27 | * Get Http protocal 28 | * 29 | * @return string 30 | */ 31 | protected function protocol() 32 | { 33 | return stripos($_SERVER['SERVER_PROTOCOL'], 'http') === 0 ? 'http://' : 'https://'; 34 | } 35 | 36 | /** 37 | * Get Domain 38 | * 39 | * @return string 40 | */ 41 | protected function domain() 42 | { 43 | return $_SERVER['HTTP_HOST']; 44 | } 45 | 46 | /** 47 | * Get full url with query string 48 | * 49 | * @return string 50 | */ 51 | protected function full() 52 | { 53 | return $this->protocol() . $this->domain() . $_SERVER['REQUEST_URI']; 54 | } 55 | 56 | protected function extractUri() 57 | { 58 | return strtok($_SERVER["REQUEST_URI"], '?'); 59 | } 60 | 61 | /** 62 | * Get current path without query string 63 | * 64 | * @return string Ex: "https://abc.local/api/users" or "https://abc.local/users" 65 | */ 66 | protected function current() 67 | { 68 | return $this->protocol() . $this->domain() . $this->extractUri(); 69 | } 70 | 71 | protected function previous() 72 | { 73 | return $_SERVER['HTTP_REFERER']; 74 | } 75 | 76 | /** 77 | * Generate signed URL 78 | * 79 | * @param string $uri URI (ex: /users/add) 80 | * @param array $params Parameters 81 | * @param int|null $expiration Expiration (minutes) 82 | * 83 | * @return string 84 | */ 85 | protected function signedUrl(string $uri, array $params = [], int $expiration = null) 86 | { 87 | if (array_key_exists('signature', $params)) { 88 | throw new UrlException(UrlException::ERR_MSG_URL_INVALID_PARAMS); 89 | } 90 | 91 | if ($expiration) { 92 | $time = new DateTime(now()); 93 | $time->modify("+{$expiration} minutes"); 94 | $params = $params + ['expires' => $time->format("U")]; 95 | } 96 | 97 | $params = $params + ['signature' => $this->generateSignature($uri, $params)]; 98 | 99 | unset($time); 100 | return $this->protocol() . $this->domain() . $uri . '?' . http_build_query($params); 101 | 102 | } 103 | 104 | /** 105 | * Generate temporary Signed URL 106 | * 107 | * @param string $uri URI (ex: /users/add) 108 | * @param int $expiration Expiration (minutes) 109 | * @param array $params Parameters 110 | * 111 | * @return string 112 | */ 113 | protected function temporarySignedUrl(string $uri, int $expiration, array $params = []) 114 | { 115 | return $this->signedUrl($uri, $params, $expiration); 116 | } 117 | 118 | /** 119 | * Prepare Data 120 | * 121 | * @param string $uri URI 122 | * @param array $params Parameters 123 | * 124 | * @return string 125 | */ 126 | protected function prepareData(string $uri, array $params) 127 | { 128 | return http_build_query( 129 | $params + ['protocol' => $this->protocol(), 'domain' => $this->domain(), 'uri' => $uri] 130 | ); 131 | } 132 | 133 | /** 134 | * Generate Signature 135 | * 136 | * @param string $uri URI 137 | * @param array $params Parameters 138 | * 139 | * @return string 140 | */ 141 | protected function generateSignature(string $uri, array $params) 142 | { 143 | return hash_hmac('sha256', $this->prepareData($uri, $params), $this->key); 144 | } 145 | 146 | /** 147 | * Identify Signature 148 | * 149 | * @return boolean 150 | */ 151 | protected function identifySignature() 152 | { 153 | parse_str($_SERVER['QUERY_STRING'], $params); 154 | return $this->hasCorrectSignature($params['signature'], $params) && 155 | $this->isExpiredSignature($params['expires']); 156 | } 157 | 158 | /** 159 | * Check correct signature 160 | * 161 | * @param string $signature Signature 162 | * @param array $params Parameter 163 | * 164 | * @return boolean 165 | */ 166 | protected function hasCorrectSignature($signature, $params) 167 | { 168 | unset($params['signature']); 169 | return $signature == $this->generateSignature($this->extractUri(), $params); 170 | } 171 | 172 | /** 173 | * Signature is expired 174 | * 175 | * @param int $expires Expiration (Unix Timestamp) 176 | * 177 | * @return boolean 178 | */ 179 | protected function isExpiredSignature($expires) 180 | { 181 | $time = new DateTime(); 182 | $time->setTimestamp($expires); 183 | return $time->format("Y-m-d H:i:s") > now(); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | container = new Container(); 49 | $this->requestMethod = Globals::method(); 50 | } 51 | 52 | /** 53 | * Load controller 54 | * @param array $routeData Route data 55 | * @return void 56 | */ 57 | public function loadController($routeData) 58 | { 59 | try { 60 | list($class, $method) = $this->dispatchController($routeData); 61 | $this->controller = $this->init($class); 62 | $this->controller->callMethod($method); 63 | } catch (\Exception $e) { 64 | echo $e->getMessage(); 65 | } 66 | } 67 | 68 | /** 69 | * Initiate controller class 70 | * @param string $class 71 | * @return object 72 | */ 73 | public function init(string $class) 74 | { 75 | list($file, $class) = $this->parseController($class); 76 | 77 | require_once($file); 78 | 79 | $controllerClass = env('APP_NAMESPACE').'\\Controllers\\'.$class; 80 | $controller = $this->container->resolve($controllerClass); 81 | 82 | if ($controller) { 83 | return $controller; 84 | } 85 | throw new ControllerException(ControllerException::ERR_MSG_INVALID_CONTROLLER); 86 | } 87 | 88 | /** 89 | * Call method 90 | * @param string $method 91 | * @return void 92 | */ 93 | public function callMethod($method) 94 | { 95 | if (!method_exists($this, $method)) { 96 | throw new ControllerException(ControllerException::ERR_MSG_ACTION_FAIL); 97 | } 98 | 99 | $methodReflection = (new ReflectionObject($this))->getMethod($method); 100 | $dependencies = $this->container->getDependencies($methodReflection->getParameters()); 101 | $result = $methodReflection->invokeArgs($this, $dependencies); 102 | if ($result === false) { 103 | throw new ControllerException(ControllerException::ERR_MSG_ACTION_FAIL); 104 | } 105 | } 106 | 107 | /** 108 | * Parse controller 109 | * @param string $class 110 | * @return array 111 | */ 112 | public function parseController(string $class) 113 | { 114 | $file = CONTROLLER_PATH . $class . '.php'; 115 | if (!file_exists($file)) { 116 | throw new ControllerException(ControllerException::ERR_MSG_INVALID_CONTROLLER); 117 | } 118 | $class = str_replace("/", "\\", $class); 119 | 120 | return [$file, $class]; 121 | } 122 | 123 | /** 124 | * Dispatch Controller 125 | * @param array $routeData Data is gained from route file 126 | * @return array 127 | * @throws ControllerException 128 | */ 129 | public function dispatchController($routeData) 130 | { 131 | $actions = array_column($routeData, strtolower($this->requestMethod)); 132 | list($class, $function) = explode('@', $actions[0]); 133 | if (empty($class)) { 134 | throw new ControllerException(ControllerException::ERR_MSG_INVALID_CONTROLLER); 135 | } 136 | 137 | return [$class, $function]; 138 | } 139 | 140 | /** 141 | * Get Doctrine Entity Manager 142 | * 143 | * @return EntityManager | Exception 144 | */ 145 | public function getDoctrineEntityManager() 146 | { 147 | if (false === boolval(env('DBAL_IN_USE'))) { 148 | throw new ControllerException(ControllerException::ERR_MSG_DOCTRINE_NOT_USE); 149 | } 150 | // Create a simple "default" Doctrine ORM configuration for Annotations 151 | $config = Setup::createAnnotationMetadataConfiguration( 152 | [DOC_ROOT.env('DBAL_PATH_CONFIG')], 153 | (bool) env('DBAL_DEV_MODE'), 154 | env('DBAL_PROXY_DIR') ? env('DBAL_PROXY_DIR') : null, 155 | env('DBAL_CACHE') ? env('DBAL_CACHE') : null, 156 | (bool) env('DBAL_USE_SIMPLE_ANNO_READER') 157 | ); 158 | 159 | // database configuration parameters 160 | $conn = array( 161 | 'driver' => env('DB_DRIVER'), 162 | 'dbname' => env('DB_NAME'), 163 | 'user' => env('DB_USER'), 164 | 'password' => env('DB_PASSWORD'), 165 | 'host' => env('DB_HOST').':'.env('DB_PORT'), 166 | ); 167 | 168 | // obtaining the entity manager 169 | return EntityManager::create($conn, $config); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/Libs/ClientURL/ClientURL.php: -------------------------------------------------------------------------------- 1 | con = curl_init(); 27 | } 28 | 29 | /** 30 | * Set URL 31 | * @param string $url 32 | * @return $this 33 | */ 34 | public function url($url) 35 | { 36 | curl_setopt($this->con, CURLOPT_URL, $url); 37 | return $this; 38 | } 39 | 40 | /** 41 | * Set header 42 | * @param array $header 43 | * @return $this 44 | */ 45 | public function header($header) 46 | { 47 | curl_setopt($this->con, CURLOPT_HTTPHEADER, $header); 48 | return $this; 49 | } 50 | 51 | /** 52 | * Set Return Transfer 53 | * @param boolean $isReturn 54 | * @return $this 55 | */ 56 | public function returnTransfer($isReturn) 57 | { 58 | curl_setopt($this->con, CURLOPT_RETURNTRANSFER, $isReturn); 59 | return $this; 60 | } 61 | 62 | /** 63 | * Set Post fields 64 | * @param array|json $request 65 | * @return $this 66 | */ 67 | public function postFields($request) 68 | { 69 | curl_setopt($this->con, CURLOPT_POSTFIELDS, $request); 70 | return $this; 71 | } 72 | 73 | /** 74 | * Set port 75 | * @param string|integer $port 76 | * @return $this 77 | */ 78 | public function port($port) 79 | { 80 | curl_setopt($this->con, CURLOPT_PORT, $port); 81 | return $this; 82 | } 83 | 84 | /** 85 | * Set multiple options for a cURL transfer 86 | * @param array $options 87 | * @return $this 88 | */ 89 | public function setOptions(array $options) 90 | { 91 | curl_setopt_array($this->con, $options); 92 | return $this; 93 | } 94 | 95 | /** 96 | * Customize request method 97 | * @param string $method 98 | * @return $this 99 | */ 100 | public function customRequest($method) 101 | { 102 | if (in_array(strtoupper($method), $this->methods)) { 103 | curl_setopt($this->con, CURLOPT_CUSTOMREQUEST, $method); 104 | return $this; 105 | } 106 | throw new ClientURLException(ClientURLException::ERR_MSG_METHOD_NOT_EXIST); 107 | } 108 | 109 | /** 110 | * Set User-Agent header 111 | * @param string $agent 112 | * @return $this 113 | */ 114 | public function userAgent($agent) 115 | { 116 | curl_setopt($this->con, CURLOPT_USERAGENT, $agent); 117 | return $this; 118 | } 119 | 120 | /** 121 | * Set time-out 122 | * @param integer $seconds 123 | * @return $this 124 | */ 125 | public function timeOut($seconds) 126 | { 127 | curl_setopt($this->con, CURLOPT_TIMEOUT, $seconds); 128 | return $this; 129 | } 130 | 131 | /** 132 | * Set username and password 133 | * @param string $username 134 | * @param string $password 135 | * @return $this 136 | */ 137 | public function setUserPwd($username, $password) 138 | { 139 | curl_setopt($this->con, CURLOPT_USERPWD, $username . ":" . $password); 140 | return $this; 141 | } 142 | 143 | /** 144 | * Perform a cURL session 145 | * @return mixed 146 | */ 147 | public function exec() 148 | { 149 | $result = curl_exec($this->con); 150 | if($result === false || curl_errno($this->con)) 151 | { 152 | $result = curl_error($this->con); 153 | } 154 | $this->result = $result; 155 | return $this; 156 | } 157 | 158 | /** 159 | * Get information regarding a specific transfer 160 | * @return $this 161 | */ 162 | public function info() 163 | { 164 | $this->info = curl_getinfo($this->con); 165 | return $this; 166 | } 167 | 168 | /** 169 | * Gets information about the last transfer 170 | * @return array 171 | */ 172 | public function getInfo() 173 | { 174 | return $this->info; 175 | } 176 | 177 | /** 178 | * Get Result 179 | * @return mixed 180 | */ 181 | public function get() 182 | { 183 | return $this->exec(); 184 | } 185 | 186 | /** 187 | * Return array 188 | * @return array 189 | */ 190 | public function toArray() 191 | { 192 | return json_decode($this->result, true); 193 | } 194 | 195 | /** 196 | * Transfer a URL without user interaction. 197 | * @param string $url 198 | * @param array $param 199 | * @return void 200 | */ 201 | public function execCommand($url, array $param) 202 | { 203 | $curl = "curl -X POST -H 'Content-Type: application/json'"; 204 | $payload = json_encode($param); 205 | $curl .= " -d '$payload' '$url'"; 206 | $curl .= ' > /dev/null 2>&1 &'; 207 | exec($curl); 208 | } 209 | 210 | /** 211 | * Destruct 212 | */ 213 | public function __destruct() 214 | { 215 | curl_close($this->con); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/File/File.php: -------------------------------------------------------------------------------- 1 | type = $type ?? env('STORAGE_DRIVER'); 22 | $this->storage = (new StorageFactory($this->type))->init(); 23 | } 24 | 25 | /** 26 | * Parse File 27 | * @param array $file 28 | * @return $this 29 | */ 30 | protected function parse(array $file) 31 | { 32 | if (empty($file["tmp_name"])) { 33 | throw new FileException(FileException::ERR_MSG_FILE_TOO_LARGE); 34 | } 35 | $this->file = $file; 36 | return $this; 37 | } 38 | 39 | /** 40 | * Extract File Info 41 | * @param string $path 42 | * @param string|null $fileName 43 | * @return array 44 | */ 45 | protected function extractFileName(string $path, string $fileName = null) 46 | { 47 | list($name, $extension) = explode('.', $fileName); 48 | if (empty($fileName)) { 49 | $fileName = $this->name(); 50 | $extension = pathinfo($fileName)["extension"]; 51 | } 52 | 53 | if (empty($extension)) { 54 | throw new FileException(FileException::ERR_MSG_UNKNOW_FILE); 55 | } 56 | 57 | $fullDirectory = $this->storage->getFullUrl($path); 58 | $this->isExist($fullDirectory . $fileName); 59 | 60 | return [$fileName, strtolower($extension)]; 61 | } 62 | 63 | /** 64 | * File is existed 65 | * @param string $fileName 66 | * @return boolean 67 | */ 68 | protected function isExist(string $fileName) 69 | { 70 | if (file_exists($fileName)) { 71 | throw new FileException(FileException::ERR_MSG_FILENAME_ALREADY_USED); 72 | } 73 | return; 74 | } 75 | 76 | /** 77 | * Get Core 78 | * @return string 79 | */ 80 | protected function core() 81 | { 82 | return $this->file["tmp_name"]; 83 | } 84 | 85 | /** 86 | * Get Extension 87 | * @return string 88 | */ 89 | protected function extension() 90 | { 91 | return pathinfo($this->file["name"])["extension"]; 92 | } 93 | 94 | /** 95 | * Get Name 96 | * @return string 97 | */ 98 | protected function name() 99 | { 100 | return $this->file["name"]; 101 | } 102 | 103 | /** 104 | * Get Type 105 | * @return string 106 | */ 107 | protected function type() 108 | { 109 | return $this->file["type"]; 110 | } 111 | 112 | /** 113 | * Get Size 114 | * @return string 115 | */ 116 | protected function size() 117 | { 118 | return $this->file["size"]; 119 | } 120 | 121 | /** 122 | * Get Metadata 123 | * @return array 124 | */ 125 | protected function metadata() 126 | { 127 | $exif = exif_read_data($this->core(), 0, true); 128 | return $exif ? $exif : []; 129 | } 130 | 131 | /** 132 | * Encode file to base64 133 | * @param array $file 134 | * @return array 135 | */ 136 | protected function encode(array $file) 137 | { 138 | $this->parse($file); 139 | $binaryContent = file_get_contents($this->core()); 140 | return [ 141 | "name" => $this->name(), 142 | "type" => $this->type(), 143 | "metadata" => $this->metadata(), 144 | "content" => base64_encode($binaryContent), 145 | ]; 146 | } 147 | 148 | /** 149 | * Decode base64 150 | * @param string $content 151 | * @return string 152 | */ 153 | protected function decode(string $content) 154 | { 155 | return base64_decode($content); 156 | } 157 | 158 | /** 159 | * Decode and Save as 160 | * @param string $directory /storage/images/test.jpg 161 | * @param string $content 162 | * @return void 163 | */ 164 | protected function decodeSaveAs(string $directory, string $content) 165 | { 166 | try { 167 | $this->isExist($directory); 168 | $decode = $this->decode($content); 169 | file_put_contents($directory, $decode); 170 | } catch (\Exception $e) { 171 | throw new FileException(FileException::ERR_MSG_SAVE_FILE_FAIL . ': ' . $e->getMessage()); 172 | } 173 | } 174 | 175 | /** 176 | * Process File 177 | * @param string $directory /storage/images 178 | * @param array $file 179 | * @param string|null $fileName 180 | * @return array 181 | */ 182 | protected function process(string $directory, array $file, string $fileName = null) 183 | { 184 | $fullDirectory = $this->storage->getFullUrl($directory); 185 | $this->storage->checkDirectory($fullDirectory); 186 | 187 | $core = $this->parse($file)->core(); 188 | list($fileName, $imageType) = $this->extractFileName($directory, $fileName); 189 | $storagePath = $fullDirectory . '/' . $fileName; 190 | 191 | $this->isExist($storagePath); 192 | return [$storagePath, $core, $imageType]; 193 | } 194 | 195 | /** 196 | * Upload File 197 | * @param string $directory /storage/images 198 | * @param array $file 199 | * @param string|null $fileName 200 | * @return void 201 | */ 202 | protected function upload(string $directory, array $file, string $fileName = null) 203 | { 204 | list($storagePath, $core, $imageType) = $this->process($directory, $file, $fileName); 205 | $this->storage->upload($storagePath, $core); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/Db/PHPDataObjects.php: -------------------------------------------------------------------------------- 1 | connect(env('DB_HOST'), env('DB_USER'), env('DB_PASSWORD'), env('DB_NAME'), env('DB_PORT')); 22 | } 23 | 24 | /** 25 | * Connect to Database 26 | * 27 | * @param string $host DB Host 28 | * @param string $user DB User 29 | * @param string $password DB User's password 30 | * @param string $database DB Name 31 | * @param string $port DB Port 32 | * 33 | * @return void 34 | */ 35 | public function connect(string $host, string $user, string $password, string $database, string $port) 36 | { 37 | try { 38 | $mode = env('DB_CONNECTION') .'/'. $host .'/'. $database; 39 | if (isset(static::$con[$mode])) { 40 | $this->db = static::$con[$mode]; 41 | } 42 | if (empty(static::$con) && !isset(static::$con[$mode])) { 43 | $this->db = new PDO(env('DB_CONNECTION') . ':dbname=' . $database .';host=' . $host . ';port=' . $port, $user, $password); 44 | static::$con[$mode] = $this->db; 45 | } 46 | } catch (PDOException $e) { 47 | throw new \Exception(DatabaseException::ERR_MSG_CONNECTION_FAIL . ' => ' . $e->getMessage()); 48 | } 49 | } 50 | 51 | /** 52 | * Set params 53 | * 54 | * @param array $data Params 55 | */ 56 | public function setParams(array $data) 57 | { 58 | $this->params = array_merge($this->params, $data); 59 | } 60 | 61 | /** 62 | * Execute query 63 | * 64 | * @param string $query SQL query 65 | * 66 | * @return boolean 67 | */ 68 | public function execute($query) 69 | { 70 | $this->sth = $this->db->prepare($query); 71 | foreach ($this->params as $key => &$value) { 72 | $this->sth->bindParam(':' .$key, $value); 73 | } 74 | $this->sth->execute(); 75 | return $this->sth; 76 | } 77 | 78 | /** 79 | * Parse conditions 80 | * 81 | * @param array $condition Where conditions 82 | * 83 | * @return throw Exception | string 84 | */ 85 | public function parseConditions(array $condition) 86 | { 87 | switch (count($condition)) { 88 | case 1: 89 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 90 | break; 91 | case 2: 92 | list($key, $value) = $condition; 93 | $this->setParams([$key => $value]); 94 | return $key . ' = :' . $key; 95 | break; 96 | case 3: 97 | list($key, $operator, $value) = $condition; 98 | $operator = strtoupper($operator); 99 | $this->setParams([$key => $value]); 100 | return $key . ' '. $operator .' :' . $key; 101 | break; 102 | } 103 | } 104 | 105 | /** 106 | * Where conditions 107 | * 108 | * @param array $conditions Where conditions 109 | * 110 | * @return $this; 111 | */ 112 | public function where(array $conditions = []) 113 | { 114 | if (!is_array($conditions) || empty($conditions)) { 115 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 116 | } 117 | 118 | if (!is_array($conditions[0])) { 119 | $this->where[] = $this->parseConditions($conditions); 120 | } else { 121 | foreach ($conditions as $condition) { 122 | $this->where[] = $this->parseConditions($condition); 123 | } 124 | } 125 | 126 | return $this; 127 | } 128 | 129 | /** 130 | * Delete 131 | * 132 | * @return boolean 133 | */ 134 | public function delete() 135 | { 136 | $query = "DELETE FROM {$this->table}"; 137 | if (false === empty($this->where)) { 138 | $query .= " WHERE ". implode(' AND ', $this->where); 139 | } 140 | $this->execute($query); 141 | } 142 | 143 | /** 144 | * Update 145 | * 146 | * @param array $data Data 147 | * 148 | * @return boolean 149 | */ 150 | public function update(array $data) 151 | { 152 | $tmp = []; 153 | foreach ($data as $key => $value) { 154 | $tmp[] = "$key = :{$key}"; 155 | } 156 | $this->setParams($data); 157 | $query = "UPDATE {$this->table} SET ".implode(", ", $tmp); 158 | if (false === empty($this->where)) { 159 | $query .= " WHERE ". implode(' AND ', $this->where); 160 | } 161 | $this->execute($query); 162 | } 163 | 164 | /** 165 | * Insert data 166 | * 167 | * @param array $data Request data 168 | * 169 | * @return array 170 | */ 171 | public function insert(array $data) 172 | { 173 | $keys = array_keys($data); 174 | $callBack = function ($key) { 175 | return ":".$key; 176 | }; 177 | $query = "INSERT INTO {$this->table}(". implode(', ', $keys). ") VALUES(". implode(',', array_map($callBack, $keys)) .")"; 178 | $this->setParams($data); 179 | $this->execute($query); 180 | return ['id' => $this->getLastInsertId()]; 181 | } 182 | 183 | /** 184 | * Get data 185 | * 186 | * @return array 187 | */ 188 | public function get() 189 | { 190 | $query = "SELECT * FROM {$this->table}"; 191 | return $this->execute($query)->fetchAll(PDO::FETCH_ASSOC); 192 | } 193 | 194 | /** 195 | * Get last insert id 196 | * 197 | * @return int 198 | */ 199 | public function getLastInsertId() 200 | { 201 | return $this->db->lastInsertId(); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/Http/Request.php: -------------------------------------------------------------------------------- 1 | uri = Globals::uri(); 52 | $this->method = Globals::method(); 53 | $this->get = Globals::get(); 54 | $this->post = Globals::post(); 55 | $this->files = Globals::files(); 56 | $this->request = $this->collectParameters(); 57 | } 58 | 59 | /** 60 | * Convert request to array 61 | * @return array 62 | */ 63 | public function all() 64 | { 65 | return (array) $this->request; 66 | } 67 | 68 | /** 69 | * Get headers 70 | * @param string|null $key 71 | * @return string|array 72 | */ 73 | public function headers(string $key = null) 74 | { 75 | $headers = getHeaders(); 76 | return is_null($key) ? $headers : $headers[$key]; 77 | } 78 | 79 | /** 80 | * Collect parameters 81 | * @return object 82 | */ 83 | public function collectParameters() 84 | { 85 | $params = []; 86 | 87 | if (!empty($tmpParams = $this->extractUriParameters())) { 88 | $params = array_merge($params, $tmpParams); 89 | } 90 | 91 | if (!empty($tmpParams = $this->getParametersByMethod())) { 92 | $params = array_merge($params, $tmpParams); 93 | } 94 | 95 | if (!empty($tmpParams = $this->files)) { 96 | $params = array_merge($params, $tmpParams); 97 | } 98 | 99 | if (!empty($tmpParams = $this->getRawData())) { 100 | $params = array_merge($params, $tmpParams); 101 | } 102 | 103 | return $params; 104 | } 105 | 106 | /** 107 | * Get raw data 108 | * @return array 109 | */ 110 | public function getRawData() 111 | { 112 | if (empty($content = file_get_contents('php://input'))){ 113 | return []; 114 | } 115 | $data = json_decode($content, true); 116 | if (json_last_error() === JSON_ERROR_NONE) { 117 | return $data; 118 | } 119 | parse_str($content, $data); 120 | return $data; 121 | } 122 | 123 | /** 124 | * Get parameters by method 125 | */ 126 | public function getParametersByMethod() 127 | { 128 | switch ($this->method) { 129 | case 'GET': 130 | return $this->get; 131 | break; 132 | case 'POST': 133 | case 'PUT': 134 | case 'PATCH': 135 | return $this->post; 136 | break; 137 | } 138 | } 139 | 140 | /** 141 | * Extract uri parameters 142 | * @return mixed 143 | */ 144 | public function extractUriParameters() 145 | { 146 | parse_str(parse_url($this->uri, PHP_URL_QUERY), $params); 147 | return array_merge($params, $this->parseUriParams()); 148 | } 149 | 150 | /** 151 | * Parse parameters in URI 152 | * 153 | * @return array 154 | */ 155 | public function parseUriParams() 156 | { 157 | $path = Globals::path(); 158 | $patternCurrentUri = preg_replace("/[0-9]+/", '#', $path); 159 | $routers = isApi() ? route('api') : route('web'); 160 | foreach ($routers as $route => $data) { 161 | $tmp = preg_replace('/\{[a-zA-Z]+\}+/', '#', $route); 162 | if ($tmp == $patternCurrentUri) { 163 | return $this->mapParams($path, $route); 164 | } 165 | } 166 | } 167 | 168 | /** 169 | * Map key and value for Parameters 170 | * 171 | * @param [type] $current [description] 172 | * @param [type] $expect [description] 173 | * 174 | * @return [type] [description] 175 | */ 176 | public function mapParams($current, $expect) 177 | { 178 | $arrUri = explode('/', $current); 179 | $arrRoute = explode('/', $expect); 180 | $tmp = array_combine($arrRoute, $arrUri); 181 | $params = []; 182 | foreach($tmp as $key => $value) { 183 | if (preg_match('/^\{|\}$/', $key)) { 184 | $key = preg_replace('/^.|.$/','',$key); 185 | $params[$key] = $value; 186 | } 187 | } 188 | return $params; 189 | } 190 | 191 | /** 192 | * Get a data by key 193 | * 194 | * @param string The key data to retrieve 195 | * @access public 196 | */ 197 | public function &__get ($key) { 198 | return $this->request[$key]; 199 | } 200 | 201 | /** 202 | * Assigns a value to the specified data 203 | * 204 | * @param string The data key to assign the value to 205 | * @param mixed The value to set 206 | * @access public 207 | */ 208 | public function __set($key,$value) { 209 | $this->request[$key] = $value; 210 | } 211 | 212 | /** 213 | * Whether or not an data exists by key 214 | * 215 | * @param string An data key to check for 216 | * @access public 217 | * @return boolean 218 | * @abstracting ArrayAccess 219 | */ 220 | public function __isset ($key) { 221 | return isset($this->request[$key]); 222 | } 223 | 224 | /** 225 | * Unsets an data by key 226 | * 227 | * @param string The key to unset 228 | * @access public 229 | */ 230 | public function __unset($key) { 231 | unset($this->request[$key]); 232 | } 233 | 234 | /** 235 | * Assigns a value to the specified offset 236 | * 237 | * @param string The offset to assign the value to 238 | * @param mixed The value to set 239 | * @access public 240 | * @abstracting ArrayAccess 241 | */ 242 | public function offsetSet($offset,$value) { 243 | if (is_null($offset)) { 244 | $this->request[] = $value; 245 | } else { 246 | $this->request[$offset] = $value; 247 | } 248 | } 249 | 250 | /** 251 | * Whether or not an offset exists 252 | * 253 | * @param string An offset to check for 254 | * @access public 255 | * @return boolean 256 | * @abstracting ArrayAccess 257 | */ 258 | public function offsetExists($offset) { 259 | return isset($this->request[$offset]); 260 | } 261 | 262 | /** 263 | * Unsets an offset 264 | * 265 | * @param string The offset to unset 266 | * @access public 267 | * @abstracting ArrayAccess 268 | */ 269 | public function offsetUnset($offset) { 270 | if ($this->offsetExists($offset)) { 271 | unset($this->request[$offset]); 272 | } 273 | } 274 | 275 | /** 276 | * Returns the value at specified offset 277 | * 278 | * @param string The offset to retrieve 279 | * @access public 280 | * @return mixed 281 | * @abstracting ArrayAccess 282 | */ 283 | public function offsetGet($offset) { 284 | return $this->offsetExists($offset) ? $this->request[$offset] : null; 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/Helpers/helpers.php: -------------------------------------------------------------------------------- 1 | $value) { 24 | putenv("{$env}={$value}"); 25 | } 26 | } else { 27 | return obtainValue($searchFile, $keys); 28 | } 29 | } 30 | } 31 | 32 | if (!function_exists('route')) { 33 | /** 34 | * Load route file 35 | * @param string $params 36 | * @return mixed 37 | */ 38 | function route($params = null) 39 | { 40 | if (is_null($params)) { 41 | return; 42 | } 43 | 44 | $keys = explode('.', $params); 45 | $fileName = array_shift($keys); 46 | $filePath = ROUTE_PATH . $fileName . '.php'; 47 | 48 | return obtainValue($filePath, $keys); 49 | } 50 | } 51 | 52 | if (!function_exists('searchFile')) { 53 | 54 | function searchFile(string $path) 55 | { 56 | $searchFile = glob($path); 57 | if (empty($searchFile)) { 58 | throw new \Exception('File Is Not Existed'); 59 | } 60 | return $searchFile[0]; 61 | } 62 | } 63 | 64 | if (!function_exists('obtainValue')) { 65 | 66 | function obtainValue(string $file, array $keys) 67 | { 68 | if (!file_exists($file)) { 69 | throw new \Exception('File Is Not Existed'); 70 | } 71 | 72 | $data = require($file); 73 | foreach ($keys as $key) { 74 | $data = $data[$key]; 75 | } 76 | return $data; 77 | } 78 | } 79 | 80 | if (!function_exists('getHeaders')) 81 | { 82 | /** 83 | * Get HTTP Headers 84 | * @return array 85 | */ 86 | function getHeaders() 87 | { 88 | $headers = []; 89 | foreach ($_SERVER as $name => $value) 90 | { 91 | if (substr($name, 0, 5) == 'HTTP_') 92 | { 93 | $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; 94 | } 95 | } 96 | return $headers; 97 | } 98 | } 99 | 100 | if (!function_exists('isApi')) 101 | { 102 | function isApi() 103 | { 104 | $headers = getHeaders(); 105 | return (bool) strpos($_SERVER['REQUEST_URI'], 'api') || (isset($headers['Content-Type']) && $headers['Content-Type'] == 'application/json'); 106 | } 107 | } 108 | 109 | if (!function_exists('env')) 110 | { 111 | /** 112 | * Get variable's value 113 | * @param string|null $varName [description] 114 | * @return mixed 115 | */ 116 | function env(string $varName = null) 117 | { 118 | $value = getenv($varName); 119 | if (false === $value){ 120 | throw new \Exception('INVALID ENV VALUE'); 121 | } 122 | return $value; 123 | } 124 | } 125 | 126 | if (!function_exists('template')) 127 | { 128 | /** 129 | * Render view 130 | * @param string $tempKey Template Key 131 | * @param string $directory Directory (ex: 'admin.users.list') 132 | * @param array $data Data 133 | * @return void 134 | */ 135 | function template(string $tempKey, string $directory = "", array $data = []) 136 | { 137 | $viewTemplates = config('templates.'.$tempKey.'.template'); 138 | $filledTemp = array_search(null, $viewTemplates); 139 | 140 | if ($filledTemp !== false && empty($directory) === false) { 141 | $viewTemplates[$filledTemp] = $directory; 142 | } 143 | 144 | $html = (new Template($viewTemplates, $data))->render(); 145 | echo $html; 146 | exit(); 147 | } 148 | } 149 | 150 | if (!function_exists('view')) 151 | { 152 | /** 153 | * Include view file 154 | * @param string $directory Directory 155 | * @param array $data Data 156 | * @return void 157 | */ 158 | function view(string $directory, array $data = []) 159 | { 160 | $file = VIEW_PATH . str_replace('.', '/', $directory) . '.php'; 161 | if (!file_exists($file)) { 162 | throw new \Exception('Invalid Directory'); 163 | } 164 | extract($data); 165 | include $file; 166 | } 167 | } 168 | 169 | if (!function_exists('stripSpace')) 170 | { 171 | /** 172 | * Strip whitespace 173 | * @param string|null $string 174 | * @return string 175 | */ 176 | function stripSpace(string $string = null) 177 | { 178 | return str_replace(' ', '', $string); 179 | } 180 | } 181 | 182 | if (!function_exists('now')) 183 | { 184 | /** 185 | * Now 186 | * @return string 187 | */ 188 | function now() 189 | { 190 | return date("Y-m-d H:i:s"); 191 | } 192 | } 193 | 194 | if (!function_exists('today')) 195 | { 196 | /** 197 | * Today 198 | * @return string 199 | */ 200 | function today() 201 | { 202 | return date("Y-m-d"); 203 | } 204 | } 205 | 206 | if (!function_exists('json')) 207 | { 208 | /** 209 | * Convert to Json 210 | * @param $data 211 | * @param $option 212 | * @return json 213 | */ 214 | function json($data, $option = JSON_UNESCAPED_UNICODE) 215 | { 216 | $json = json_encode($data, $option); 217 | if (json_last_error()) { 218 | throw new \Exception("Invalid Json"); 219 | } 220 | return $json; 221 | } 222 | } 223 | 224 | if (!function_exists('storage_path')) 225 | { 226 | /** 227 | * Get Storage path 228 | * @param string $path 229 | * @return string 230 | */ 231 | function storage_path($path = '') 232 | { 233 | if (empty($path)) { 234 | return STORAGE_PATH; 235 | } 236 | if (false === file_exists(STORAGE_PATH . $path)) 237 | { 238 | throw new \Exception("Storage Directory Not Found"); 239 | } 240 | 241 | return STORAGE_PATH . $path; 242 | } 243 | } 244 | 245 | if (!function_exists('imageLocation')) 246 | { 247 | /** 248 | * Get Image Location 249 | * @param array $file 250 | * @return array 251 | */ 252 | function imageLocation(array $file) 253 | { 254 | $exif = exif_read_data($file["tmp_name"], 0, true); 255 | $location = []; 256 | if($exif && isset($exif['GPS'])){ 257 | $GPSLatitudeRef = $exif['GPS']['GPSLatitudeRef']; 258 | $GPSLatitude = $exif['GPS']['GPSLatitude']; 259 | $GPSLongitudeRef= $exif['GPS']['GPSLongitudeRef']; 260 | $GPSLongitude = $exif['GPS']['GPSLongitude']; 261 | 262 | $lat_degrees = count($GPSLatitude) > 0 ? gps2Num($GPSLatitude[0]) : 0; 263 | $lat_minutes = count($GPSLatitude) > 1 ? gps2Num($GPSLatitude[1]) : 0; 264 | $lat_seconds = count($GPSLatitude) > 2 ? gps2Num($GPSLatitude[2]) : 0; 265 | 266 | $lon_degrees = count($GPSLongitude) > 0 ? gps2Num($GPSLongitude[0]) : 0; 267 | $lon_minutes = count($GPSLongitude) > 1 ? gps2Num($GPSLongitude[1]) : 0; 268 | $lon_seconds = count($GPSLongitude) > 2 ? gps2Num($GPSLongitude[2]) : 0; 269 | 270 | $lat_direction = ($GPSLatitudeRef == 'W' or $GPSLatitudeRef == 'S') ? -1 : 1; 271 | $lon_direction = ($GPSLongitudeRef == 'W' or $GPSLongitudeRef == 'S') ? -1 : 1; 272 | 273 | $latitude = $lat_direction * ($lat_degrees + ($lat_minutes / 60) + ($lat_seconds / (60*60))); 274 | $longitude = $lon_direction * ($lon_degrees + ($lon_minutes / 60) + ($lon_seconds / (60*60))); 275 | 276 | $location = ['latitude' => $latitude, 'longitude' => $longitude]; 277 | } 278 | 279 | return $location; 280 | } 281 | } 282 | 283 | if (!function_exists('gps2Num')) 284 | { 285 | /** 286 | * Convert GPS coord part in float val 287 | * @param [type] $coordPart 288 | * @return [type] 289 | */ 290 | function gps2Num($coordPart){ 291 | $parts = explode('/', $coordPart); 292 | if(count($parts) <= 0) return 0; 293 | if(count($parts) == 1) return $parts[0]; 294 | return floatval($parts[0]) / floatval($parts[1]); 295 | } 296 | } 297 | 298 | if (!function_exists('resources_path')) 299 | { 300 | /** 301 | * Get Resource path 302 | * @param string $path 303 | * @return string 304 | */ 305 | function resources_path($path = '') 306 | { 307 | if (empty($path)) { 308 | return RESOURCES_PATH; 309 | } 310 | if (false === file_exists(RESOURCES_PATH . $path)) 311 | { 312 | throw new \Exception("Resource Directory Not Found"); 313 | } 314 | 315 | return RESOURCES_PATH . $path; 316 | } 317 | } 318 | 319 | if (!function_exists('assets')) 320 | { 321 | /** 322 | * Get assets path in Public folder 323 | * @param string $path 324 | * @return string 325 | */ 326 | function assets($path = '') 327 | { 328 | $assets = url() . '/assets'; 329 | if (empty($path)) { 330 | return $assets; 331 | } 332 | if (false === file_exists(ASSETS_PATH . $path)) 333 | { 334 | throw new \Exception("Asset Directory Not Found"); 335 | } 336 | 337 | return $assets . $path; 338 | } 339 | } 340 | 341 | if (!function_exists('public_path')) 342 | { 343 | /** 344 | * Get Public path 345 | * @param string $path 346 | * @return string 347 | */ 348 | function public_path($path = '') 349 | { 350 | if (empty($path)) { 351 | return DOC_ROOT; 352 | } 353 | if (false === file_exists(DOC_ROOT . $path)) 354 | { 355 | throw new \Exception("Public Directory Not Found"); 356 | } 357 | 358 | return DOC_ROOT. $path; 359 | } 360 | } 361 | 362 | if (!function_exists('url')) 363 | { 364 | /** 365 | * Get current url 366 | * @param string $path 367 | * @return string 368 | */ 369 | function url($path = '') 370 | { 371 | $url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]"; 372 | if (empty($path)) { 373 | return $url; 374 | } 375 | return $url . '/' . $path; 376 | } 377 | } 378 | 379 | if (!function_exists('back')) 380 | { 381 | /** 382 | * Go back 383 | * @param string $path 384 | * @return string 385 | */ 386 | function back() 387 | { 388 | header("Location: {$_SERVER['HTTP_REFERER']}"); 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /src/Validation/Validator.php: -------------------------------------------------------------------------------- 1 | $rule) { 28 | $rule = stripSpace($rule); 29 | preg_match('~\\brequired\\b~i', $rule, $required); 30 | preg_match('/required_if:(.+)\|/', $rule, $requiredIf); 31 | if (empty($required) && empty($input[$attribute])) { 32 | continue; 33 | } 34 | if ($requiredIf && !isset($input[$requiredIf[1]]) && empty($input[$attribute])) { 35 | continue; 36 | } 37 | static::$errors[$attribute] = static::checkValidation($input[$attribute], $rule, $attribute, $messages); 38 | } 39 | } 40 | 41 | /** 42 | * Set input 43 | * @param array $input 44 | */ 45 | public static function setInput(array $input) 46 | { 47 | static::$input = $input; 48 | } 49 | 50 | /** 51 | * Get input 52 | */ 53 | public static function getInput() 54 | { 55 | return static::$input; 56 | } 57 | 58 | /** 59 | * Get errors 60 | * @return array 61 | */ 62 | public static function errors() 63 | { 64 | $errors = array_filter(static::$errors); 65 | if (empty($errors)) { 66 | return []; 67 | } 68 | return $errors; 69 | } 70 | 71 | /** 72 | * Check Validation 73 | * @param mixed $value 74 | * @param string $rules 75 | * @param string $attribute 76 | * @param array $messages 77 | * @return array 78 | */ 79 | public static function checkValidation($value, $rules, $attribute, $messages) 80 | { 81 | $rules = array_filter(explode('|', $rules)); 82 | $errors = []; 83 | foreach ($rules as $rule) { 84 | list($rule, $params) = static::getRule($rule); 85 | $error = call_user_func_array([__NAMESPACE__.'\Validator', $rule], [$value, $attribute, $messages[$rule], $params]); 86 | if (!empty($error)) { 87 | $errors[] = $error; 88 | } 89 | } 90 | return $errors; 91 | } 92 | 93 | /** 94 | * Check Rules 95 | * @param array $rules 96 | * @return \Exception 97 | */ 98 | public static function checkRules(array $rules) 99 | { 100 | foreach ($rules as $inputKey => $rule) { 101 | $inputKey = stripSpace($inputKey); 102 | $rule = stripSpace($rule); 103 | array_push(static::$attributes, $inputKey); 104 | static::splitRules($rule); 105 | } 106 | if (array_diff(static::getInputRules(), static::rules())) { 107 | throw new ValidationException(ValidationException::ERR_MSG_INVALID_RULES); 108 | } 109 | } 110 | 111 | /** 112 | * Check Message 113 | * @param array $messages 114 | * @return \Exception 115 | */ 116 | public static function checkMessages(array $messages = []) 117 | { 118 | $messages = empty($messages) ? static::messages() : $messages; 119 | if (array_diff(static::getInputRules(), array_keys($messages))) { 120 | throw new ValidationException(ValidationException::ERR_MSG_NO_MESSAGES); 121 | } 122 | } 123 | 124 | /** 125 | * Split Rules 126 | * @param string $rules 127 | * @return void 128 | */ 129 | public static function splitRules(string $rules) 130 | { 131 | $rules = array_filter(explode('|', $rules)); 132 | foreach ($rules as $key => $rule) { 133 | list($inputRule, $params) = static::getRule($rule); 134 | static::setInputRules($inputRule); 135 | } 136 | } 137 | 138 | /** 139 | * Set input rules 140 | * @param string $inputRule 141 | */ 142 | public static function setInputRules(string $inputRule) 143 | { 144 | array_push(static::$inputRules, $inputRule); 145 | } 146 | 147 | /** 148 | * Get input rules 149 | */ 150 | public static function getInputRules() 151 | { 152 | return static::$inputRules; 153 | } 154 | 155 | /** 156 | * Get Rule 157 | * @param string $rule 158 | * @return array 159 | */ 160 | public static function getRule(string $rule) 161 | { 162 | preg_match("/(.+)\:(.+)/", $rule, $output); 163 | return $output && $output[1] ? [$output[1], $output[2]]: [$rule]; 164 | } 165 | 166 | /** 167 | * Default messages 168 | * @param string $rule 169 | * @return array|string 170 | */ 171 | public static function messages($rule = null) 172 | { 173 | $messages = [ 174 | 'required' => 'This %s must be required.', 175 | 'string' => 'This %s must be string.', 176 | 'email' => 'This %s is invalid format.', 177 | 'integer' => 'This %s must be integer.', 178 | 'between' => 'This %s must be between %s and %s', 179 | 'in_array' => 'This %s is invalid value', 180 | 'max' => 'This %s is greater than %s', 181 | 'min' => 'This %s is smaller than %s', 182 | 'array' => 'This %s must be array', 183 | 'date' => 'This %s must be format of date time', 184 | 'image' => 'This %s must be image', 185 | 'after' => 'This %s must be after %s', 186 | 'before' => 'This %s must be before %s', 187 | 'required_if' => 'This %s is required', 188 | 'date_format' => 'This %s must be presented as %s', 189 | ]; 190 | return is_null($rule) ? $messages : $messages[$rule]; 191 | } 192 | 193 | /** 194 | * Default Rules 195 | * @return array 196 | */ 197 | public static function rules() 198 | { 199 | return [ 200 | 'array', 'between', 'date', 'email', 'image', 'in_array', 'integer', 'max', 'min', 'required', 'string', 'after', 'before', 'required_if', 'date_format', 201 | ]; 202 | } 203 | 204 | /** 205 | * Date_format Validation 206 | * @return string 207 | */ 208 | public static function date_format() 209 | { 210 | list($value, $attribute, $message, $params) = func_get_args(); 211 | return date_create_from_format($params, $value) !== false ? '' : vsprintf($message, [$attribute, $params]); 212 | } 213 | 214 | /** 215 | * Required_if Validation 216 | * @return string 217 | */ 218 | public static function required_if() 219 | { 220 | list($value, $attribute, $message, $params) = func_get_args(); 221 | $input = static::getInput(); 222 | list($field, $fielValue) = explode(',', $params); 223 | return (!isset($input[$field])) ? '' : vsprintf($message, [$attribute, $params]); 224 | } 225 | 226 | /** 227 | * After Validation 228 | * @return string 229 | */ 230 | public static function after() 231 | { 232 | list($value, $attribute, $message, $params) = func_get_args(); 233 | return (strtotime($value) !== false) && (strtotime($value) > strtotime($params)) ? '' : vsprintf($message, [$attribute, $params]); 234 | } 235 | 236 | /** 237 | * Before Validation 238 | * @return string 239 | */ 240 | public static function before() 241 | { 242 | list($value, $attribute, $message, $params) = func_get_args(); 243 | return (strtotime($value) !== false) && (strtotime($value) < strtotime($params)) ? '' : vsprintf($message, [$attribute, $params]); 244 | } 245 | 246 | /** 247 | * Image Validation 248 | * @return string 249 | */ 250 | public static function image() 251 | { 252 | list($value, $attribute, $message, $params) = func_get_args(); 253 | $info = pathinfo($value['name']); 254 | return in_array($info["extension"], ['jpeg', 'png', 'bmp', 'gif', 'svg']) ? '' : vsprintf($message, [$attribute, $params]); 255 | } 256 | 257 | /** 258 | * Date Validation 259 | * @return string 260 | */ 261 | public static function date() 262 | { 263 | list($value, $attribute, $message, $params) = func_get_args(); 264 | return strtotime($value) !== false ? '' : vsprintf($message, [$attribute, $params]); 265 | } 266 | 267 | /** 268 | * Array Validation 269 | * @return string 270 | */ 271 | public static function array() 272 | { 273 | list($value, $attribute, $message, $params) = func_get_args(); 274 | return is_array($value) ? '' : vsprintf($message, [$attribute, $params]); 275 | } 276 | 277 | /** 278 | * Min Validation 279 | * @return string 280 | */ 281 | public static function min() 282 | { 283 | list($value, $attribute, $message, $params) = func_get_args(); 284 | return $params < $value ? '' : vsprintf($message, [$attribute, $params]); 285 | } 286 | 287 | /** 288 | * Max Validation 289 | * @return string 290 | */ 291 | public static function max() 292 | { 293 | list($value, $attribute, $message, $params) = func_get_args(); 294 | return $params > $value ? '' : vsprintf($message, [$attribute, $params]); 295 | } 296 | 297 | /** 298 | * In_array Validation 299 | * @return string 300 | */ 301 | public static function in_array() 302 | { 303 | list($value, $attribute, $message, $params) = func_get_args(); 304 | return in_array($value, explode(',', $params)) ? '' : vsprintf($message, [$attribute, $params]); 305 | } 306 | 307 | /** 308 | * Between Validation 309 | * @return string 310 | */ 311 | public static function between() 312 | { 313 | list($value, $attribute, $message, $params) = func_get_args(); 314 | list($min, $max) = explode(',', $params); 315 | return $value >= $min && $value <= $max ? '' : vsprintf($message, [$attribute, $min, $max]); 316 | } 317 | 318 | /** 319 | * Required Validation 320 | * @return string 321 | */ 322 | public static function required() 323 | { 324 | list($value, $attribute, $message, $params) = func_get_args(); 325 | return !empty($value) ? '' : vsprintf($message, [$attribute]); 326 | } 327 | 328 | /** 329 | * String Validation 330 | * @return string 331 | */ 332 | public static function string() 333 | { 334 | list($value, $attribute, $message, $params) = func_get_args(); 335 | return is_string($value) ? '' : vsprintf($message, [$attribute]); 336 | } 337 | 338 | /** 339 | * Email Validation 340 | * @return string 341 | */ 342 | public static function email() 343 | { 344 | list($value, $attribute, $message, $params) = func_get_args(); 345 | return (bool) filter_var($value, FILTER_VALIDATE_EMAIL) ? '' : vsprintf($message, [$attribute]); 346 | } 347 | 348 | /** 349 | * Integer Validation 350 | * @return string 351 | */ 352 | public static function integer() 353 | { 354 | list($value, $attribute, $message, $params) = func_get_args(); 355 | return filter_var($value, FILTER_VALIDATE_INT) ? '' : vsprintf($message, [$attribute]); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /src/Libs/JWT/JWT.php: -------------------------------------------------------------------------------- 1 | 19 | * @author Anant Narayanan 20 | * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 21 | * @link https://github.com/firebase/php-jwt 22 | */ 23 | class JWT 24 | { 25 | 26 | /** 27 | * When checking nbf, iat or expiration times, 28 | * we want to provide some extra leeway time to 29 | * account for clock skew. 30 | */ 31 | public static $leeway = 0; 32 | 33 | /** 34 | * Allow the current timestamp to be specified. 35 | * Useful for fixing a value within unit testing. 36 | * 37 | * Will default to PHP time() value if null. 38 | */ 39 | public static $timestamp = null; 40 | 41 | public static $supported_algs = array( 42 | 'ES256' => array('openssl', 'SHA256'), 43 | 'HS256' => array('hash_hmac', 'SHA256'), 44 | 'HS384' => array('hash_hmac', 'SHA384'), 45 | 'HS512' => array('hash_hmac', 'SHA512'), 46 | 'RS256' => array('openssl', 'SHA256'), 47 | 'RS384' => array('openssl', 'SHA384'), 48 | 'RS512' => array('openssl', 'SHA512'), 49 | ); 50 | 51 | /** 52 | * Decodes a JWT string into a PHP object. 53 | * 54 | * @param string $jwt The JWT 55 | * @param string|array $key The key, or map of keys. 56 | * If the algorithm used is asymmetric, this is the public key 57 | * @param array $allowed_algs List of supported verification algorithms 58 | * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' 59 | * 60 | * @return object The JWT's payload as a PHP object 61 | * 62 | * @throws UnexpectedValueException Provided JWT was invalid 63 | * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed 64 | * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf' 65 | * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat' 66 | * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim 67 | * 68 | * @uses jsonDecode 69 | * @uses urlsafeB64Decode 70 | */ 71 | public static function decode($jwt, $key, array $allowed_algs = array()) 72 | { 73 | $timestamp = is_null(static::$timestamp) ? time() : static::$timestamp; 74 | 75 | if (empty($key)) { 76 | throw new InvalidArgumentException('Key may not be empty'); 77 | } 78 | $tks = explode('.', $jwt); 79 | if (count($tks) != 3) { 80 | throw new UnexpectedValueException('Wrong number of segments'); 81 | } 82 | list($headb64, $bodyb64, $cryptob64) = $tks; 83 | if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) { 84 | throw new UnexpectedValueException('Invalid header encoding'); 85 | } 86 | if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) { 87 | throw new UnexpectedValueException('Invalid claims encoding'); 88 | } 89 | if (false === ($sig = static::urlsafeB64Decode($cryptob64))) { 90 | throw new UnexpectedValueException('Invalid signature encoding'); 91 | } 92 | if (empty($header->alg)) { 93 | throw new UnexpectedValueException('Empty algorithm'); 94 | } 95 | if (empty(static::$supported_algs[$header->alg])) { 96 | throw new UnexpectedValueException('Algorithm not supported'); 97 | } 98 | if (!in_array($header->alg, $allowed_algs)) { 99 | throw new UnexpectedValueException('Algorithm not allowed'); 100 | } 101 | if (is_array($key) || $key instanceof \ArrayAccess) { 102 | if (isset($header->kid)) { 103 | if (!isset($key[$header->kid])) { 104 | throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key'); 105 | } 106 | $key = $key[$header->kid]; 107 | } else { 108 | throw new UnexpectedValueException('"kid" empty, unable to lookup correct key'); 109 | } 110 | } 111 | 112 | // Check the signature 113 | if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) { 114 | throw new SignatureInvalidException('Signature verification failed'); 115 | } 116 | 117 | // Check the nbf if it is defined. This is the time that the 118 | // token can actually be used. If it's not yet that time, abort. 119 | if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) { 120 | throw new BeforeValidException( 121 | 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf) 122 | ); 123 | } 124 | 125 | // Check that this token has been created before 'now'. This prevents 126 | // using tokens that have been created for later use (and haven't 127 | // correctly used the nbf claim). 128 | if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) { 129 | throw new BeforeValidException( 130 | 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat) 131 | ); 132 | } 133 | 134 | // Check if this token has expired. 135 | if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) { 136 | throw new ExpiredException('Expired token'); 137 | } 138 | 139 | return $payload; 140 | } 141 | 142 | /** 143 | * Converts and signs a PHP object or array into a JWT string. 144 | * 145 | * @param object|array $payload PHP object or array 146 | * @param string $key The secret key. 147 | * If the algorithm used is asymmetric, this is the private key 148 | * @param string $alg The signing algorithm. 149 | * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' 150 | * @param mixed $keyId 151 | * @param array $head An array with header elements to attach 152 | * 153 | * @return string A signed JWT 154 | * 155 | * @uses jsonEncode 156 | * @uses urlsafeB64Encode 157 | */ 158 | public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null) 159 | { 160 | $header = array('typ' => 'JWT', 'alg' => $alg); 161 | if ($keyId !== null) { 162 | $header['kid'] = $keyId; 163 | } 164 | if (isset($head) && is_array($head)) { 165 | $header = array_merge($head, $header); 166 | } 167 | $segments = array(); 168 | $segments[] = static::urlsafeB64Encode(static::jsonEncode($header)); 169 | $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload)); 170 | $signing_input = implode('.', $segments); 171 | 172 | $signature = static::sign($signing_input, $key, $alg); 173 | $segments[] = static::urlsafeB64Encode($signature); 174 | 175 | return implode('.', $segments); 176 | } 177 | 178 | /** 179 | * Sign a string with a given key and algorithm. 180 | * 181 | * @param string $msg The message to sign 182 | * @param string|resource $key The secret key 183 | * @param string $alg The signing algorithm. 184 | * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' 185 | * 186 | * @return string An encrypted message 187 | * 188 | * @throws DomainException Unsupported algorithm was specified 189 | */ 190 | public static function sign($msg, $key, $alg = 'HS256') 191 | { 192 | if (empty(static::$supported_algs[$alg])) { 193 | throw new DomainException('Algorithm not supported'); 194 | } 195 | list($function, $algorithm) = static::$supported_algs[$alg]; 196 | switch($function) { 197 | case 'hash_hmac': 198 | return hash_hmac($algorithm, $msg, $key, true); 199 | case 'openssl': 200 | $signature = ''; 201 | $success = openssl_sign($msg, $signature, $key, $algorithm); 202 | if (!$success) { 203 | throw new DomainException("OpenSSL unable to sign data"); 204 | } else { 205 | return $signature; 206 | } 207 | } 208 | } 209 | 210 | /** 211 | * Verify a signature with the message, key and method. Not all methods 212 | * are symmetric, so we must have a separate verify and sign method. 213 | * 214 | * @param string $msg The original message (header and body) 215 | * @param string $signature The original signature 216 | * @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key 217 | * @param string $alg The algorithm 218 | * 219 | * @return bool 220 | * 221 | * @throws DomainException Invalid Algorithm or OpenSSL failure 222 | */ 223 | private static function verify($msg, $signature, $key, $alg) 224 | { 225 | if (empty(static::$supported_algs[$alg])) { 226 | throw new DomainException('Algorithm not supported'); 227 | } 228 | 229 | list($function, $algorithm) = static::$supported_algs[$alg]; 230 | switch($function) { 231 | case 'openssl': 232 | $success = openssl_verify($msg, $signature, $key, $algorithm); 233 | if ($success === 1) { 234 | return true; 235 | } elseif ($success === 0) { 236 | return false; 237 | } 238 | // returns 1 on success, 0 on failure, -1 on error. 239 | throw new DomainException( 240 | 'OpenSSL error: ' . openssl_error_string() 241 | ); 242 | case 'hash_hmac': 243 | default: 244 | $hash = hash_hmac($algorithm, $msg, $key, true); 245 | if (function_exists('hash_equals')) { 246 | return hash_equals($signature, $hash); 247 | } 248 | $len = min(static::safeStrlen($signature), static::safeStrlen($hash)); 249 | 250 | $status = 0; 251 | for ($i = 0; $i < $len; $i++) { 252 | $status |= (ord($signature[$i]) ^ ord($hash[$i])); 253 | } 254 | $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash)); 255 | 256 | return ($status === 0); 257 | } 258 | } 259 | 260 | /** 261 | * Decode a JSON string into a PHP object. 262 | * 263 | * @param string $input JSON string 264 | * 265 | * @return object Object representation of JSON string 266 | * 267 | * @throws DomainException Provided string was invalid JSON 268 | */ 269 | public static function jsonDecode($input) 270 | { 271 | if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) { 272 | /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you 273 | * to specify that large ints (like Steam Transaction IDs) should be treated as 274 | * strings, rather than the PHP default behaviour of converting them to floats. 275 | */ 276 | $obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING); 277 | } else { 278 | /** Not all servers will support that, however, so for older versions we must 279 | * manually detect large ints in the JSON string and quote them (thus converting 280 | *them to strings) before decoding, hence the preg_replace() call. 281 | */ 282 | $max_int_length = strlen((string) PHP_INT_MAX) - 1; 283 | $json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input); 284 | $obj = json_decode($json_without_bigints); 285 | } 286 | 287 | if ($errno = json_last_error()) { 288 | static::handleJsonError($errno); 289 | } elseif ($obj === null && $input !== 'null') { 290 | throw new DomainException('Null result with non-null input'); 291 | } 292 | return $obj; 293 | } 294 | 295 | /** 296 | * Encode a PHP object into a JSON string. 297 | * 298 | * @param object|array $input A PHP object or array 299 | * 300 | * @return string JSON representation of the PHP object or array 301 | * 302 | * @throws DomainException Provided object could not be encoded to valid JSON 303 | */ 304 | public static function jsonEncode($input) 305 | { 306 | $json = json_encode($input); 307 | if ($errno = json_last_error()) { 308 | static::handleJsonError($errno); 309 | } elseif ($json === 'null' && $input !== null) { 310 | throw new DomainException('Null result with non-null input'); 311 | } 312 | return $json; 313 | } 314 | 315 | /** 316 | * Decode a string with URL-safe Base64. 317 | * 318 | * @param string $input A Base64 encoded string 319 | * 320 | * @return string A decoded string 321 | */ 322 | public static function urlsafeB64Decode($input) 323 | { 324 | $remainder = strlen($input) % 4; 325 | if ($remainder) { 326 | $padlen = 4 - $remainder; 327 | $input .= str_repeat('=', $padlen); 328 | } 329 | return base64_decode(strtr($input, '-_', '+/')); 330 | } 331 | 332 | /** 333 | * Encode a string with URL-safe Base64. 334 | * 335 | * @param string $input The string you want encoded 336 | * 337 | * @return string The base64 encode of what you passed in 338 | */ 339 | public static function urlsafeB64Encode($input) 340 | { 341 | return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); 342 | } 343 | 344 | /** 345 | * Helper method to create a JSON error. 346 | * 347 | * @param int $errno An error number from json_last_error() 348 | * 349 | * @return void 350 | */ 351 | private static function handleJsonError($errno) 352 | { 353 | $messages = array( 354 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 355 | JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', 356 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 357 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', 358 | JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3 359 | ); 360 | throw new DomainException( 361 | isset($messages[$errno]) 362 | ? $messages[$errno] 363 | : 'Unknown JSON error: ' . $errno 364 | ); 365 | } 366 | 367 | /** 368 | * Get the number of bytes in cryptographic strings. 369 | * 370 | * @param string 371 | * 372 | * @return int 373 | */ 374 | private static function safeStrlen($str) 375 | { 376 | if (function_exists('mb_strlen')) { 377 | return mb_strlen($str, '8bit'); 378 | } 379 | return strlen($str); 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /src/Db/Database.php: -------------------------------------------------------------------------------- 1 | ', '>', '>=', '<', '<=', 'like', 'LIKE']; 62 | protected $conditions = ""; 63 | protected $groupBy; 64 | protected $having; 65 | protected $orderBy; 66 | protected $updateValues; 67 | protected $innerJoin; 68 | protected $leftJoin; 69 | protected $rightJoin; 70 | protected $where = []; 71 | 72 | const QUERY_SELECT = "SELECT"; 73 | const QUERY_INSERT = "INSERT"; 74 | const QUERY_UPDATE = "UPDATE"; 75 | const QUERY_DELETE = "DELETE"; 76 | const QUERY_TRUNCATE = "TRUNCATE"; 77 | const QUERY_INSERT_DUPLICATE = "INSERT_DUPLICATE"; 78 | 79 | /** 80 | * Database construct 81 | * 82 | * @param string|null $driver Database Driver 83 | * @param string|null $host Database Host 84 | * @param string|null $user Database User 85 | * @param string|null $password User Password 86 | * @param string|null $database Database Name 87 | * @param string|null $port Database Port 88 | */ 89 | public function __construct(string $driver = null, string $host = null, string $user = null, string $password = null, string $database = null, string $port = null) 90 | { 91 | $this->driver = $driver ?? env('DB_CONNECTION'); 92 | $this->host = $host ?? env('DB_HOST'); 93 | $this->user = $user ?? env('DB_USER'); 94 | $this->password = $password ?? env('DB_PASSWORD'); 95 | $this->database = $database ?? env('DB_NAME'); 96 | $this->port = $port ?? env('DB_PORT'); 97 | 98 | $mode = $this->driver.'/'.$this->host.'/'.$this->database; 99 | 100 | if (isset(static::$con[$mode])) { 101 | $this->db = static::$con[$mode]; 102 | } 103 | if (empty(static::$con) && !isset(static::$con[$mode])) { 104 | $this->db = (new Driver($this->driver, $this->host, $this->user, $this->password, $this->database, $this->port))->createConnection(); 105 | static::$con[$mode] = $this->db; 106 | } 107 | } 108 | 109 | /** 110 | * Prepare SQL query 111 | * @param string $type SQL query 112 | * @return string 113 | */ 114 | protected function buildQuery($type) 115 | { 116 | if (false === empty($this->where)) { 117 | $this->conditions .= " AND " . implode(' AND ', $this->where); 118 | } 119 | switch ($type) { 120 | case self::QUERY_TRUNCATE: 121 | $sql = "TRUNCATE {$this->table}"; 122 | break; 123 | case self::QUERY_UPDATE: 124 | $where = !boolval($this->conditions) ? "" : "WHERE TRUE ". $this->conditions; 125 | $sql = "UPDATE {$this->table} SET {$this->updateValues} {$where}"; 126 | unset($where); 127 | break; 128 | case self::QUERY_DELETE: 129 | $where = !boolval($this->conditions) ? "" : "WHERE TRUE ". $this->conditions; 130 | $sql = "DELETE FROM {$this->table} {$where}"; 131 | unset($where); 132 | break; 133 | case self::QUERY_INSERT: 134 | $sql = "INSERT INTO {$this->table}({$this->insertKeys}) VALUES {$this->insertValues}"; 135 | break; 136 | case self::QUERY_INSERT_DUPLICATE: 137 | $sql = "INSERT INTO {$this->table}({$this->insertKeys}) VALUES {$this->insertValues} ON DUPLICATE KEY UPDATE {$this->updateValues}"; 138 | break; 139 | case self::QUERY_SELECT: 140 | $select = self::QUERY_SELECT; 141 | $limit = ""; 142 | $where = !boolval($this->conditions) ? "" : "WHERE TRUE ". $this->conditions; 143 | $groupBy = $this->groupBy ? "GROUP BY ". $this->groupBy : ""; 144 | $having = $this->having ? "HAVING ". $this->having : ""; 145 | $orderBy = $this->orderBy ? "ORDER BY ". $this->orderBy : ""; 146 | $innerJoin = $this->innerJoin ?? ""; 147 | $leftJoin = $this->leftJoin ?? ""; 148 | $rightJoin = $this->rightJoin ?? ""; 149 | $join = $innerJoin . $leftJoin . $rightJoin; 150 | if (is_numeric($this->limit)) { 151 | $offset = is_numeric($this->offset) ? $this->offset : 0; 152 | $limit = " LIMIT {$offset}, {$this->limit}"; 153 | } 154 | 155 | $columns = ($this->selectCols) ? $this->selectCols : "*"; 156 | $sql = "{$select} {$columns} FROM {$this->table} {$join} {$where} {$groupBy} {$having} {$orderBy} {$limit}"; 157 | unset($where); 158 | unset($groupBy); 159 | unset($having); 160 | unset($orderBy); 161 | unset($innerJoin); 162 | unset($leftJoin); 163 | unset($rightJoin); 164 | unset($join); 165 | unset($columns); 166 | unset($limit); 167 | break; 168 | } 169 | 170 | $this->reset(); 171 | 172 | return $sql; 173 | } 174 | 175 | /** 176 | * TRUNCATE query 177 | * @return void 178 | */ 179 | public function truncate() 180 | { 181 | if (false === $this->checkTable()) { 182 | return false; 183 | } 184 | $sql = $this->buildQuery(self::QUERY_TRUNCATE); 185 | $this->query($sql); 186 | } 187 | 188 | /** 189 | * DELETE query 190 | * @return void 191 | */ 192 | public function delete() 193 | { 194 | if (false === $this->checkTable()) { 195 | return false; 196 | } 197 | $sql = $this->buildQuery(self::QUERY_DELETE); 198 | $this->query($sql); 199 | } 200 | 201 | /** 202 | * UPDATE query 203 | * @param array $data 204 | * @return void 205 | */ 206 | public function update(array $data) 207 | { 208 | if (false === $this->checkTable()) { 209 | return false; 210 | } 211 | 212 | if (!is_array($data)) { 213 | throw new DatabaseException(DatabaseException::ERR_MSQ_BAD_REQUEST, DatabaseException::ERR_CODE_BAD_REQUEST); 214 | } 215 | $this->parseUpdateValue($data); 216 | $sql = $this->buildQuery(self::QUERY_UPDATE); 217 | $this->query($sql); 218 | } 219 | 220 | /** 221 | * Parse values for Update 222 | * @param array $data [description] 223 | * @return [type] [description] 224 | */ 225 | public function parseUpdateValue(array $data) 226 | { 227 | $values = []; 228 | foreach ($data as $key => $value) { 229 | list($key, $value) = $this->parseRawValue($key, $value); 230 | $values[] = "`{$key}`" .' = '.$value; 231 | } 232 | $this->updateValues = implode(' , ', $values); 233 | unset($values); 234 | } 235 | 236 | /** 237 | * INSERT statement 238 | * @param array $request 239 | * @return void 240 | */ 241 | public function insert(array $request) 242 | { 243 | if (false === $this->checkTable()) { 244 | return false; 245 | } 246 | 247 | if (!is_array($request) || (isset($request[0]) && is_array($request[0]))) { 248 | throw new DatabaseException(DatabaseException::ERR_MSQ_BAD_REQUEST, DatabaseException::ERR_CODE_BAD_REQUEST); 249 | } 250 | $this->parseValues($request); 251 | $sql = $this->buildQuery(self::QUERY_INSERT); 252 | $this->query($sql); 253 | return $this->db->lastestInsertId(); 254 | } 255 | 256 | /** 257 | * INSERT MANY 258 | * @param array $requests 259 | * @return void 260 | */ 261 | public function insertMany(array $requests) 262 | { 263 | if (false === $this->checkTable()) { 264 | return false; 265 | } 266 | 267 | $tmp = []; 268 | if (empty($this->fillable) || !is_array($this->fillable)) { 269 | throw new DatabaseException(DatabaseException::ERR_MSQ_BAD_REQUEST, DatabaseException::ERR_CODE_BAD_REQUEST); 270 | } 271 | foreach ($requests as $request) { 272 | if (!is_array($request)) { 273 | throw new DatabaseException(DatabaseException::ERR_MSQ_BAD_REQUEST, DatabaseException::ERR_CODE_BAD_REQUEST); 274 | } 275 | if (empty($request)) { 276 | continue; 277 | } 278 | $this->parseValues($request); 279 | $tmp[] = $this->insertValues; 280 | } 281 | $this->insertKeys = implode(',', $this->fillable); 282 | $this->insertValues = implode(',', $tmp); 283 | unset($tmp); 284 | $sql = $this->buildQuery(self::QUERY_INSERT); 285 | $this->query($sql); 286 | } 287 | 288 | /** 289 | * INSERT DUPLICATE KEY 290 | * @param array $request 291 | * @return void 292 | */ 293 | public function insertDuplicate(array $request) 294 | { 295 | if (false === $this->checkTable()) { 296 | return false; 297 | } 298 | 299 | if (!is_array($request) || is_array($request[0])) { 300 | throw new DatabaseException(DatabaseException::ERR_MSQ_BAD_REQUEST, DatabaseException::ERR_CODE_BAD_REQUEST); 301 | } 302 | 303 | $this->parseValues($request, false); 304 | $this->parseUpdateValue($request); 305 | $sql = $this->buildQuery(self::QUERY_INSERT_DUPLICATE); 306 | $this->query($sql); 307 | } 308 | 309 | /** 310 | * INNER JOIN 311 | * @return $this 312 | */ 313 | public function innerJoin() 314 | { 315 | if (func_num_args() != 3) { 316 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 317 | } 318 | list($joinTable, $tableCond, $joinCond) = func_get_args(); 319 | $this->innerJoin = " INNER JOIN {$this->escape($joinTable)} ON {$this->escape($tableCond)} = {$this->escape($joinCond)}"; 320 | return $this; 321 | } 322 | 323 | /** 324 | * LEFT JOIN 325 | * @return $this 326 | */ 327 | public function leftJoin() 328 | { 329 | if (func_num_args() != 3) { 330 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 331 | } 332 | list($joinTable, $tableCond, $joinCond) = func_get_args(); 333 | $this->leftJoin = " LEFT JOIN {$this->escape($joinTable)} ON {$this->escape($tableCond)} = {$this->escape($joinCond)}"; 334 | return $this; 335 | } 336 | 337 | /** 338 | * RIGHT JOIN 339 | * @return $this 340 | */ 341 | public function rightJoin() 342 | { 343 | if (func_num_args() != 3) { 344 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 345 | } 346 | list($joinTable, $tableCond, $joinCond) = func_get_args(); 347 | $this->rightJoin = " RIGHT JOIN {$this->escape($joinTable)} ON {$this->escape($tableCond)} = {$this->escape($joinCond)}"; 348 | return $this; 349 | } 350 | 351 | /** 352 | * Set WHERE conditions 353 | * @param mixed $conditions 354 | * @return $this 355 | */ 356 | public function where($conditions = []) 357 | { 358 | if (!is_array($conditions) || empty($conditions)) { 359 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 360 | } 361 | 362 | if (!is_array($conditions[0])) { 363 | $this->where[] = $this->parseConditions($conditions); 364 | } else { 365 | foreach ($conditions as $condition) { 366 | $this->where[] = $this->parseConditions($condition); 367 | } 368 | } 369 | 370 | return $this; 371 | } 372 | 373 | /** 374 | * Parse conditions 375 | * @param array $condition 376 | * @return mixed 377 | */ 378 | public function parseConditions(array $condition) 379 | { 380 | switch (count($condition)) { 381 | case 1: 382 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 383 | break; 384 | case 2: 385 | list($key, $value) = $condition; 386 | list($key, $escapeValue) = $this->parseRawValue($key, $value); 387 | return "{$this->escape($key)}" . ' = ' . $escapeValue; 388 | break; 389 | case 3: 390 | list($key, $operator, $value) = $condition; 391 | list($key, $escapeValue) = $this->parseRawValue($key, $value); 392 | $operator = strtoupper($operator); 393 | return !in_array($operator, $this->whereOperators) ?: "{$this->escape($key)}" . " {$this->escape($operator)} " . $escapeValue; 394 | break; 395 | } 396 | } 397 | 398 | /** 399 | * Parse raw value 400 | * @param string $key 401 | * @param mixed $value 402 | * @return string 403 | */ 404 | public function parseRawValue($key, $value) 405 | { 406 | preg_match("/\#(.+)/", $key, $output); 407 | return $output && $output[1] ? [substr($key, 1), "{$this->escape($value)}"] : [$key, "'{$this->escape($value)}'"]; 408 | } 409 | 410 | /** 411 | * Set OR where condition 412 | * @return $this; 413 | */ 414 | public function orWhere() 415 | { 416 | if (!is_array(func_get_args()[0]) || empty(func_get_args()[0])) { 417 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 418 | } 419 | 420 | $this->conditions .= " OR ".$this->parseConditions(func_get_args()[0]); 421 | 422 | return $this; 423 | 424 | } 425 | 426 | /** 427 | * Set BETWEEN condition 428 | * @return $this 429 | */ 430 | public function whereBetween(...$params) 431 | { 432 | list($key, $values) = $this->parseWhereCondition($params); 433 | $this->conditions .= " AND " . "{$this->escape($key)} BETWEEN " . implode(' AND ', $values); 434 | 435 | return $this; 436 | } 437 | 438 | /** 439 | * Set NOT BETWEEN condition 440 | * @return $this 441 | */ 442 | public function whereNotBetween(...$params) 443 | { 444 | list($key, $values) = $this->parseWhereCondition($params); 445 | $this->conditions .= " AND " . "{$this->escape($key)} NOT BETWEEN " . implode(' AND ', $values); 446 | 447 | return $this; 448 | } 449 | 450 | /** 451 | * Set NULL condition 452 | * @return $this 453 | */ 454 | public function whereNull($key) 455 | { 456 | $this->conditions .= " AND " . "`{$this->escape($key)}` IS NULL "; 457 | 458 | return $this; 459 | } 460 | 461 | /** 462 | * Set NOT NULL condition 463 | * @return $this 464 | */ 465 | public function whereNotNull($key) 466 | { 467 | $this->conditions .= " AND " . "`{$this->escape($key)}` IS NOT NULL "; 468 | 469 | return $this; 470 | } 471 | 472 | /** 473 | * Parse Where condition 474 | * @return array 475 | */ 476 | public function parseWhereCondition($condition) 477 | { 478 | list($key, $values) = $condition; 479 | if (count($condition) != 2 || !is_array($values)) { 480 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 481 | } 482 | $values = array_map(function ($value) { 483 | return "'{$this->escape($value)}'"; 484 | }, $values); 485 | 486 | return [$key, $values]; 487 | } 488 | 489 | /** 490 | * Set IN condition 491 | * @param [type] $params [description] 492 | * @return [type] [description] 493 | */ 494 | public function whereIn(...$params) 495 | { 496 | list($key, $values) = $this->parseWhereCondition($params); 497 | $this->conditions .= " AND " . "{$this->escape($key)} IN (" . implode(', ', $values) . ")"; 498 | 499 | return $this; 500 | } 501 | 502 | /** 503 | * Set NOT IN condition 504 | * @param [type] $params [description] 505 | * @return [type] [description] 506 | */ 507 | public function whereNotIn(...$params) 508 | { 509 | list($key, $values) = $this->parseWhereCondition($params); 510 | $this->conditions .= " AND " . "{$this->escape($key)} NOT IN (" . implode(', ', $values) . ")"; 511 | 512 | return $this; 513 | } 514 | 515 | /** 516 | * Check table 517 | * @return boolean 518 | */ 519 | public function checkTable() 520 | { 521 | return (bool) $this->table; 522 | } 523 | 524 | /** 525 | * Set table 526 | * @param string $table Table 527 | * @return void 528 | */ 529 | public function table(string $table) 530 | { 531 | $this->table = $table; 532 | 533 | return $this; 534 | } 535 | 536 | /** 537 | * SQL ESCAPE 538 | * @param mixed $data 539 | * @return string 540 | */ 541 | public function escape($data) 542 | { 543 | if (is_array($data)) { 544 | return array_map(function ($item) { 545 | return $this->db->escape($item); 546 | }, $data); 547 | } 548 | return $this->db->escape($data); 549 | } 550 | 551 | /** 552 | * SQL error 553 | * @return string 554 | */ 555 | public function error() 556 | { 557 | return $this->db->error(); 558 | } 559 | 560 | /** 561 | * EXECUTE SQL query 562 | * @param string $sql 563 | * @return mixed 564 | */ 565 | public function query(string $sql) 566 | { 567 | if ($this->enableQueryLog) { 568 | $this->queryLog[] = $sql; 569 | } 570 | if (!$result = $this->db->query(trim($sql))) { 571 | throw new DatabaseException($this->error()); 572 | } 573 | 574 | return $result; 575 | } 576 | 577 | /** 578 | * Set SELECT columns 579 | * @param array $cols 580 | * @return $this 581 | */ 582 | public function select($cols = []) 583 | { 584 | $columns = $this->parseRawKey($cols); 585 | $this->selectCols = implode(',', $columns); 586 | 587 | return $this; 588 | } 589 | 590 | /** 591 | * Parse raw key 592 | * @param array $keys 593 | * @return array 594 | */ 595 | public function parseRawKey(array $keys) 596 | { 597 | return array_map(function ($key) { 598 | preg_match("/\#(.+)/", $key, $output); 599 | return $output && $output[1] ? "{$this->escape($output[1])}" : "`{$this->escape($key)}`"; 600 | }, $keys); 601 | } 602 | 603 | /** 604 | * Set OFFSET 605 | * @param mixed $offset 606 | * @return $this 607 | */ 608 | public function offset($offset) 609 | { 610 | if (!is_int($offset)) { 611 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 612 | } 613 | $this->offset = $offset; 614 | return $this; 615 | } 616 | 617 | /** 618 | * Set LIMIT 619 | * @param mixed $limit 620 | * @return $this 621 | */ 622 | public function limit($limit) 623 | { 624 | if (!is_int($limit)) { 625 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 626 | } 627 | $this->limit = $limit; 628 | return $this; 629 | } 630 | 631 | /** 632 | * Set GROUP BY 633 | * @param string $key 634 | * @return mixed 635 | */ 636 | public function groupBy($key) 637 | { 638 | if (is_string($key)) { 639 | $this->groupBy = $key; 640 | return $this; 641 | } 642 | return false; 643 | } 644 | 645 | /** 646 | * Set HAVING 647 | * @return $this 648 | */ 649 | public function having() 650 | { 651 | list($key, $operator, $value) = func_get_args(); 652 | if (func_num_args() != 3 || !in_array($operator, ['>', '=', '<'])) { 653 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 654 | } 655 | 656 | preg_match("/\#(.+)/", $key, $output); 657 | $escapeKey = $output && $output[1] ? "{$this->escape($output[1])}" : "`{$this->escape($key)}`"; 658 | $this->having = $escapeKey . " {$this->escape($operator)} " . "{$this->escape($value)}"; 659 | unset($escapeKey); 660 | return $this; 661 | } 662 | 663 | /** 664 | * Set ORDER BY 665 | * @return $this 666 | */ 667 | public function orderBy() 668 | { 669 | list($key, $sort) = func_get_args(); 670 | if (func_num_args() != 2 || !in_array(strtoupper($sort), ['ASC', 'DESC'])) { 671 | throw new DatabaseException(DatabaseException::ERR_MSG_INVALID_ARGUMENTS); 672 | } 673 | 674 | $this->orderBy = " {$this->escape($key)} " . "{$this->escape(strtoupper($sort))}"; 675 | 676 | return $this; 677 | } 678 | 679 | /** 680 | * Return array responses 681 | * @param int|null $limit 682 | * @return array 683 | */ 684 | public function get($limit = null) 685 | { 686 | if (false === $this->checkTable()) { 687 | return false; 688 | } 689 | 690 | $this->limit = $limit ?? $this->limit; 691 | $sql = $this->buildQuery(self::QUERY_SELECT); 692 | $result = $this->query($sql); 693 | return $this->resultToArray($result); 694 | } 695 | 696 | /** 697 | * Get FIRST row 698 | * @return array 699 | */ 700 | public function first() 701 | { 702 | if (method_exists($this, 'mapAttributes')) { 703 | $this->mapAttributes($this->get(1)[0]); 704 | return $this; 705 | } else { 706 | return $this->get(1)[0]; 707 | } 708 | } 709 | 710 | /** 711 | * Convert to array 712 | */ 713 | public function resultToArray($result) 714 | { 715 | return $this->db->resultToArray($result); 716 | } 717 | 718 | /** 719 | * Parse request to keys & values 720 | * @param array $request 721 | * @param boolean $checkFillable 722 | * @return void 723 | */ 724 | public function parseValues(array $request, $checkFillable = true) 725 | { 726 | $parse = $request; 727 | if ($checkFillable && $this->hasFillable()) { 728 | $tmp = array_fill_keys($this->fillable, null); 729 | $parse = array_intersect_key($request, $tmp); 730 | //If total keys of request and fillable is different, keys are replace with null values 731 | $parse = array_replace($tmp, $parse); 732 | } 733 | 734 | $values = array_map(function ($value) { 735 | return !is_null($value) ? "{$this->escape($value)}" : $value; 736 | }, array_values($parse)); 737 | 738 | $this->insertKeys = implode(',', array_keys($parse)); 739 | $tmpValues = "('" . implode("','", $values) . "')"; 740 | $this->insertValues = str_replace("''", 'null', $tmpValues); 741 | unset($tmp); 742 | unset($tmpValues); 743 | unset($parse); 744 | } 745 | 746 | /** 747 | * Enable query log 748 | * @return void 749 | */ 750 | public function enableQueryLog() 751 | { 752 | return $this->enableQueryLog = true; 753 | } 754 | 755 | /** 756 | * Get query log 757 | * @return array 758 | */ 759 | public function getQueryLog() 760 | { 761 | return $this->queryLog; 762 | } 763 | 764 | /** 765 | * Check $fillable 766 | * @return boolean 767 | */ 768 | public function hasFillable() 769 | { 770 | return boolval($this->fillable); 771 | } 772 | 773 | /** 774 | * Chunk data rows 775 | * @param int $count 776 | * @param callable $callback 777 | * @return bool 778 | */ 779 | public function chunk($count, $callback) 780 | { 781 | $page = 1; 782 | 783 | do { 784 | $results = $this->limit($count)->offset(($page - 1) * $count)->get(); 785 | $countResults = count($results); 786 | 787 | if ($countResults == 0) { 788 | break; 789 | } 790 | 791 | if ($callback($results, $page) === false) { 792 | return false; 793 | } 794 | 795 | unset($results); 796 | $page++; 797 | } while ($countResults == $count); 798 | 799 | return true; 800 | } 801 | 802 | /** 803 | * Begin transaction 804 | * @return bool 805 | */ 806 | public function beginTransaction() 807 | { 808 | return $this->db->beginTransaction(); 809 | } 810 | 811 | /** 812 | * Commit transaction 813 | * @return bool 814 | */ 815 | public function commit() 816 | { 817 | return $this->db->commit(); 818 | } 819 | 820 | /** 821 | * Roll back current transaction 822 | * @return bool 823 | */ 824 | public function rollBack() 825 | { 826 | return $this->db->rollback(); 827 | } 828 | 829 | /** 830 | * Set Fillable 831 | * 832 | * @param array $fillable Fillable 833 | * 834 | * @return $this 835 | */ 836 | public function setFillable(array $fillable) 837 | { 838 | $this->fillable = $fillable; 839 | return $this; 840 | } 841 | 842 | /** 843 | * Destruct 844 | */ 845 | public function __destruct() 846 | { 847 | unset($this->db); 848 | } 849 | 850 | /** 851 | * Reset values 852 | */ 853 | public function reset() 854 | { 855 | unset($this->conditions); 856 | unset($this->groupBy); 857 | unset($this->having); 858 | unset($this->orderBy); 859 | unset($this->innerJoin); 860 | unset($this->leftJoin); 861 | unset($this->rightJoin); 862 | unset($this->limit); 863 | unset($this->offset); 864 | unset($this->where); 865 | unset($this->selectCols); 866 | unset($this->fillable); 867 | unset($this->insertKeys); 868 | unset($this->insertValues); 869 | unset($this->updateValues); 870 | } 871 | 872 | } 873 | --------------------------------------------------------------------------------