├── .gitignore ├── LICENSE ├── README.md ├── TODO ├── composer.json ├── src ├── ActiveRecord │ ├── ActiveRecord.php │ ├── Entity.php │ └── EntityCollection.php ├── Agent │ ├── Agent.php │ ├── AgentCrud.php │ ├── AgentException.php │ ├── AgentInterface.php │ ├── Mysql.php │ └── Pgsql.php ├── Autoload.php ├── Batch │ ├── Batch.php │ ├── BatchInterface.php │ ├── Mysql.php │ └── Pgsql.php ├── Cache.php ├── Config.php ├── Database.php ├── Exception │ ├── ConnectionException.php │ ├── Error.php │ ├── InvalidConfigException.php │ ├── InvalidKeyException.php │ ├── InvalidQueryException.php │ ├── InvalidResourceException.php │ ├── InvalidValueException.php │ ├── MethodException.php │ ├── QueryException.php │ └── SqlException.php ├── Link │ ├── Link.php │ ├── LinkException.php │ ├── LinkTrait.php │ └── Linker.php ├── Logger.php ├── Mapper.php ├── Oppa.php ├── OppaException.php ├── Profiler.php ├── Query │ ├── Builder.php │ ├── BuilderException.php │ ├── BuilderJsonTrait.php │ ├── BuilderTrait.php │ ├── Identifier.php │ ├── RawSql.php │ ├── Result │ │ ├── Mysql.php │ │ ├── Pgsql.php │ │ ├── Result.php │ │ └── ResultInterface.php │ └── Sql.php ├── Resource.php ├── SqlState │ ├── Mysql.php │ ├── MysqlClientError.php │ ├── MysqlServerError.php │ ├── Pgsql.php │ └── SqlState.php └── Util.php └── test ├── _inc.php ├── a.php ├── active-record.php ├── batch.php ├── connection.php ├── crud.php ├── error.php ├── gets.php ├── logger.php ├── profiler.php ├── query-builder.php ├── query.php ├── result.php ├── simple-oop.php ├── simple-procedural.php └── test.sql /.gitignore: -------------------------------------------------------------------------------- 1 | .etc/ 2 | .logs/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015 Kerem Güneş 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 furnished 10 | 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTICE: This library is archived for the sake of [froq-database](https://github.com/froq/froq-database), use that library instead. 2 | 3 | ## Oppa 4 | 5 | Providing an easy interface, aims to simplify database CRUD operations/transactions that you tire. Oppa has also an Active Record implementation interface that sometimes make the things easier for you. 6 | 7 | Secures user inputs sharply, carries results of your queries gently, handles errors smoothly, makes batch transactions/commits carefully and profiles your all query processes optionally for you. Oppa also provides a powerful logging mechanizm to report events that you may wonder about. 8 | 9 | You will be enjoying while using it, promise.. :) 10 | 11 | Before beginning; 12 | 13 | - Set your autoloader properly 14 | - Use PHP >= 7.1 (older versions here: [v1](https://github.com/k-gun/oppa/tree/1.26.4), [v2 (7.0)](https://github.com/k-gun/oppa/tree/2.3.2)) 15 | - Use try/catch blocks 16 | - You can use `test.sql` in test folder 17 | - Wiki updated for v2, v3 18 | - Supports [MySQLi](http://php.net/manual/en/book.mysqli.php) and [PgSQL](http://php.net/manual/en/book.pgsql.php) 19 | 20 | You can see wiki pages for more doc: https://github.com/k-gun/oppa/wiki 21 | 22 | ### Autoloading / Using Libraries 23 | 24 | ```bash 25 | # composer 26 | ~$ composer require k-gun/oppa 27 | ``` 28 | 29 | ```php 30 | // manual 31 | $autoload = require('/src/Autoload.php'); 32 | $autoload->register(); 33 | ``` 34 | 35 | ### Config 36 | 37 | ```php 38 | // simply for single databases, see wiki for more 39 | $cfg = [ 40 | 'agent' => 'mysql', 41 | 'database' => [ 42 | 'host' => 'localhost', 'name' => 'test', 43 | 'username' => 'test', 'password' => '********', 44 | 'charset' => 'utf8', 'timezone' => '+00:00', 45 | ] 46 | ]; 47 | ``` 48 | 49 | ### Simple Usage 50 | 51 | ```php 52 | $db = new Oppa\Database($cfg); 53 | $db->connect(); 54 | 55 | $agent = $db->getLink()->getAgent(); 56 | $agent->query('select * from `users` where `old` > ?', [25]); 57 | dump $agent->rowsCount(); 58 | ``` 59 | 60 | ### Holy CRUD Stuffs 61 | 62 | ```php 63 | // raw queries 64 | $result = $agent->query('select * from `users`'); 65 | if ($result->hasData()) 66 | // if ($result->count() > 0) 67 | foreach ($result as $user) 68 | dump $user->name; 69 | 70 | // or 71 | if ($agent->rowsCount()) 72 | foreach ($agent->getResult() as $user) 73 | // or foreach ($agent->getResult()->getData() as $user) 74 | dump $user->name; 75 | 76 | // fetch one 77 | $user = $agent->get('select * from `users` where `old` > ?', [50]); 78 | dump $user->name; 79 | // fetch all 80 | $users = $agent->getAll('select * from `users` where `old` > ?', [50]); 81 | foreach ($users as $user) { 82 | dump $user->name; 83 | } 84 | 85 | // or shorcut methods 86 | 87 | // get one user 88 | $result = $agent->select('users'); 89 | // get one users if old greater than 50 90 | $result = $agent->select('users', '*', 'old > ?', [50]); 91 | // get many users 92 | $result = $agent->selectAll('users'); 93 | // get many users if old greater than 50 94 | $result = $agent->selectAll('users', '*', 'old > ?', [50]); 95 | 96 | // insert a user 97 | $result = $agent->insert('user', ['name' => 'Ali', 'old' => 30]); // int: last insert id 98 | // insert many users 99 | $result = $agent->insertAll('user', [['name' => 'Ali', 'old' => 30], ...]); // int[]: last insert ids 100 | 101 | // update a user 102 | $result = $agent->update('user', ['old' => 30], 'id = ?', [123]); // int: affected rows 103 | // update many users 104 | $result = $agent->updateAll('user', ['old' => 30], 'id > ?', [123]); // int: affected rows 105 | 106 | // delete a user 107 | $result = $agent->delete('user', 'id = ?', [123]); // int: affected rows 108 | // delete many users 109 | $result = $agent->deleteAll('user', 'id > ?', [123]); // int: affected rows 110 | ``` 111 | 112 | ### Query Builder 113 | 114 | ```php 115 | // use and init with exists $db 116 | use Oppa\Query\Builder as Query; 117 | 118 | $query = new Query($db->getLink()); 119 | // set target table 120 | $query->setTable('users u'); 121 | 122 | // build query 123 | $query->select('u.*') 124 | ->aggregate('sum', 'us.score', 'sum_score') 125 | ->join('users_score us', 'us.user_id=u.id') 126 | ->selectMore('us.score') 127 | ->joinLeft('users_login ul', 'ul.user_id=u.id') 128 | ->selectMore('ul.login') 129 | ->whereIn('u.id', [1,2,3]) 130 | ->whereBetween('u.old', [30,50]) 131 | ->whereNotNull('ul.login') 132 | ->groupBy('u.id') 133 | ->orderBy('u.old') 134 | ->having('sum_score <= ?', [30]) 135 | ->limit(0,10) 136 | ; 137 | ``` 138 | Gives the result below. 139 | ```sql 140 | SELECT 141 | u.* 142 | , us.score 143 | , ul.login 144 | , sum(us.score) AS sum_score 145 | FROM users u 146 | JOIN users_score us ON (us.user_id=u.id) 147 | LEFT JOIN users_login ul ON (ul.user_id=u.id) 148 | WHERE (u.id IN(1,2,3) AND u.old BETWEEN 30 AND 50 AND ul.login IS NOT NULL) 149 | GROUP BY u.id 150 | HAVING (sum_score <= 30) 151 | ORDER BY old 152 | LIMIT 0,10 153 | ``` 154 | 155 | 156 | ### Batch Actions (also Transactions) 157 | 158 | **Single Transaction** 159 | 160 | ```php 161 | // get batch object 162 | $batch = $agent->getBatch(); 163 | 164 | // set autocommit=0 (not needed for pgsql) 165 | $batch->lock(); 166 | try { 167 | // commit 168 | $batch->doQuery('insert into `users` values(null,?,?)', ['John', 25]); 169 | } catch (\Throwable $e) { 170 | // rollback 171 | $batch->undo(); 172 | } 173 | // set autocommit=1 (not needed for pgsql) 174 | $batch->unlock(); 175 | 176 | // get last insert id if success 177 | $result = $batch->getResult(); 178 | if ($result) { 179 | dump $result->getId(); 180 | } 181 | 182 | // remove query queue and empty result array 183 | $batch->reset(); 184 | ``` 185 | 186 | **Bulk Transaction** 187 | 188 | ```php 189 | // get batch object 190 | $batch = $agent->getBatch(); 191 | 192 | // set autocommit=0 (not needed for pgsql) 193 | $batch->lock(); 194 | try { 195 | $batch->queue('insert into `users` values(null,?,?)', ['John', 25]); 196 | $batch->queue('insert into `users` values(null,?,?)', ['Boby', 35]); 197 | $batch->queue('insert into `uzerz` values(null,?,?)', ['Eric', 15]); // boom! 198 | // commit 199 | $batch->do(); 200 | } catch (\Throwable $e) { 201 | // rollback 202 | $batch->undo(); 203 | } 204 | // set autocommit=1 (not needed for pgsql) 205 | $batch->unlock(); 206 | 207 | // get insert ids if success 208 | foreach ($batch->getResults() as $result) { 209 | dump $result->getId(); 210 | } 211 | 212 | // remove query queue and empty result array 213 | $batch->reset(); 214 | ``` 215 | 216 | ### Active Record 217 | 218 | ```php 219 | class Users extends Oppa\ActiveRecord\ActiveRecord { 220 | protected $table = 'users'; 221 | protected $tablePrimary = 'id'; 222 | } 223 | 224 | // init active record object 225 | $users = new Users($db); 226 | 227 | // find one that id=1 228 | $user = $users->find(1); 229 | dump $user; 230 | 231 | // check user found? 232 | if ($user->isFound()) { 233 | dump $user->name; 234 | } 235 | 236 | // find all 237 | $users = $users->findAll(); 238 | // find many (id=1,2,3) 239 | $users = $users->findAll([1,2,3]); 240 | $users = $users->findAll('id in(?)', [[1,2,3]]); 241 | $users = $users->findAll('id in(?,?,?)', [1,2,3]); 242 | dump $users; 243 | 244 | foreach ($users as $user) { 245 | dump $user->name; 246 | } 247 | 248 | $users = $users->findAll([-1,null,'foo']); 249 | dump $users->hasData(); // false 250 | 251 | // insert a user 252 | $user = $users->entity(); 253 | $user->name = 'Ali'; 254 | $user->old = 40; 255 | dump $user->save(); 256 | // or $user = $users->save($user); 257 | // here we see "id" will be filled with last insert id 258 | dump $user; 259 | 260 | // update a user (id=1) 261 | $user = $users->entity(); 262 | $user->id = 1; 263 | $user->old = 55; 264 | dump $user->save(); 265 | // or $users->save($user); 266 | 267 | // update a user that already exists (id=1) 268 | $user = $users->find(1); 269 | if ($user->isFound()) { 270 | $user->old = 100; 271 | dump $user->save(); 272 | } 273 | 274 | // remove a user (id=1) 275 | $user = $users->entity(); 276 | $user->id = 1; 277 | dump $users->remove(1); 278 | // or $users->remove($user); 279 | 280 | // remove a user that already exists (id=1) 281 | $user = $users->find(1); 282 | if ($user->isFound()) { 283 | dump $user->remove(); 284 | } 285 | 286 | // remove users (id=1,2,3) 287 | dump $users->removeAll([1,2,3]); 288 | ``` 289 | 290 | See wiki pages for more doc: https://github.com/k-gun/oppa/wiki 291 | 292 | ### Name (Oppa) 293 | 294 | Actually, I did not know what it means but some after doing some search for its meaining, I found [this](https://www.quora.com/Korean-language-1/What-does-Oppa-mean-in-Oppa-Gangnam-Style) and must say apriciated by naming this open source project with a honorific word that means **older brother**.. :) 295 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Todos for v/4.0; 2 | - use php/7.4 (?) 3 | - lower namespace 4 | - drop SqlState\{MysqlClientError,MysqlServerError} 5 | - change abstract => final SqlState\{*} 6 | - drop Exception\Error 7 | - drop all methods that use Util::generateDeprecatedMessage(), drop generateDeprecatedMessage() 8 | - extend Exception\{*} from OppaException (?) 9 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "k-gun/oppa", 3 | "type": "library", 4 | "homepage": "http://github.com/k-gun/oppa", 5 | "description": "Oppa: Database abstraction, query building and active record implementation with PHP.", 6 | "keywords": ["mysql", "pgsql", "abstraction layer", "database", "active record"], 7 | "license": "MIT", 8 | "authors": [ 9 | {"name": "Kerem Güneş", "email": "k-gun@mail.com"} 10 | ], 11 | "require": { 12 | "php": "^7.1" 13 | }, 14 | "autoload": { 15 | "classmap": ["src/"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/ActiveRecord/ActiveRecord.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\ActiveRecord; 28 | 29 | use Oppa\Database; 30 | use Oppa\Query\Result\Result; 31 | use Oppa\Query\Builder as QueryBuilder; 32 | use Oppa\Exception\InvalidValueException; 33 | 34 | /** 35 | * @package Oppa 36 | * @object Oppa\ActiveRecord\ActiveRecord 37 | * @author Kerem Güneş 38 | */ 39 | abstract class ActiveRecord 40 | { 41 | /** 42 | * Database. 43 | * @var Oppa\Database 44 | */ 45 | private $db; 46 | 47 | /** 48 | * Table. 49 | * @var string 50 | */ 51 | protected $table; 52 | 53 | /** 54 | * Table primary. 55 | * @var string 56 | */ 57 | protected $tablePrimary; 58 | 59 | /** 60 | * Table info. 61 | * @var array 62 | */ 63 | private static $tableInfo = []; 64 | 65 | /** 66 | * Constructor. 67 | * @param Oppa\Database $db 68 | * @throws Oppa\Exception\InvalidValueException 69 | */ 70 | public function __construct(Database $db) 71 | { 72 | // check for table, primary key 73 | if (!isset($this->table, $this->tablePrimary)) { 74 | throw new InvalidValueException("You need to specify both 'table' and 'tablePrimary' properties!"); 75 | } 76 | 77 | $this->db = $db; 78 | $this->db->connect(); 79 | 80 | // set table info for once 81 | if (empty(self::$tableInfo)) { 82 | $result = $this->db->getLink()->getAgent() 83 | ->get("SELECT * FROM {$this->table} LIMIT 1"); 84 | 85 | // set field names as shorcut 86 | self::$tableInfo['@fields'] = array_keys((array) $result); 87 | } 88 | } 89 | 90 | /** 91 | * Entity. 92 | * @param array $data 93 | * @return Oppa\ActiveRecord\Entity 94 | */ 95 | public final function entity(array $data = []): Entity 96 | { 97 | $entity = new Entity($this, $data); 98 | if (method_exists($this, 'onEntity')) { 99 | $this->onEntity($entity); 100 | } 101 | 102 | return $entity; 103 | } 104 | 105 | /** 106 | * Find. 107 | * @param any $param 108 | * @return Oppa\ActiveRecord\Entity 109 | * @throws Oppa\Exception\InvalidValueException 110 | */ 111 | public final function find($param): Entity 112 | { 113 | if ($param === null || $param === '') { 114 | throw new InvalidValueException('You need to pass a parameter for select action!'); 115 | } 116 | 117 | $queryBuilder = new QueryBuilder($this->db->getLink()); 118 | $queryBuilder->setTable($this->table); 119 | 120 | $queryBuilder->select("{$this->table}.*"); 121 | 122 | if (method_exists($this, 'onFind')) { 123 | $queryBuilder = $this->onFind($queryBuilder); 124 | if (!$queryBuilder || !($queryBuilder instanceof QueryBuilder)) { 125 | throw new InvalidValueException('You should return query builder back from onFind()!'); 126 | } 127 | } 128 | 129 | $queryBuilder->where("{$this->table}.{$this->tablePrimary} = ?", [$param]) 130 | ->limit(1); 131 | 132 | $result = $queryBuilder->run()->itemFirst(); 133 | 134 | $entity = new Entity($this, (array) $result); 135 | if (method_exists($this, 'onEntity')) { 136 | $this->onEntity($entity); 137 | } 138 | 139 | return $entity; 140 | } 141 | 142 | /** 143 | * Find all. 144 | * @param any $query 145 | * @param array $queryParams 146 | * @param array|int $limit 147 | * @return Oppa\ActiveRecord\EntityCollection 148 | */ 149 | public final function findAll($query = null, array $queryParams = null, $limit = null): EntityCollection 150 | { 151 | $queryBuilder = new QueryBuilder($this->db->getLink()); 152 | $queryBuilder->setTable($this->table); 153 | 154 | $queryBuilder->select("{$this->table}.*"); 155 | 156 | if (method_exists($this, 'onFind')) { 157 | $queryBuilder = $this->onFind($queryBuilder); 158 | if (!$queryBuilder || !($queryBuilder instanceof QueryBuilder)) { 159 | throw new InvalidValueException('You should return query builder back from onFind()!'); 160 | } 161 | } 162 | 163 | $isEmptyQuery = empty($query); 164 | $isEmptyQueryParams = empty($queryParams); 165 | 166 | // fetch all rows, oh ohh.. 167 | // e.g: findAll() 168 | if ($isEmptyQuery) { 169 | // nothing to do.. 170 | } 171 | // fetch all rows by primary key with given params 172 | // e.g: findAll([1,2,3]) 173 | elseif (!$isEmptyQuery && $isEmptyQueryParams) { 174 | $queryBuilder->where("{$this->table}.{$this->tablePrimary} IN(?)", [$query]); 175 | } 176 | // fetch all rows with given params and params 177 | // e.g: findAll('id IN (?)', [[1,2,3]]) 178 | // e.g: findAll('id IN (?,?,?)', [1,2,3]) 179 | elseif (!$isEmptyQuery && !$isEmptyQueryParams) { 180 | // now, it is user's responsibility to append table(s) before field(s) 181 | $queryBuilder->where($query, $queryParams); 182 | } 183 | 184 | @ [$limitStart, $limitStop] = (array) $limit; 185 | if ($limitStart !== null) { 186 | $queryBuilder->limit((int) $limitStart, $limitStop); 187 | } 188 | 189 | $hasOnEntity = method_exists($this, 'onEntity'); 190 | 191 | $entityCollection = new EntityCollection(); 192 | foreach ($queryBuilder->run() as $result) { 193 | $entity = new Entity($this, (array) $result); 194 | if ($hasOnEntity) { 195 | $this->onEntity($entity); 196 | } 197 | $entityCollection->addEntity($entity); 198 | } 199 | 200 | return $entityCollection; 201 | } 202 | 203 | /** 204 | * Save. 205 | * @param Oppa\ActiveRecord\Entity $entity 206 | * @return ?int On insert: last insert id. 207 | * @return int On update: affected rows. 208 | * @throws Oppa\Exception\InvalidValueException 209 | */ 210 | public final function save(Entity $entity): ?int 211 | { 212 | $data = $entity->toArray(); 213 | if (empty($data)) { 214 | throw new InvalidValueException('There is no data enough on entity for save action!'); 215 | } 216 | 217 | // use only owned fields 218 | $data = array_intersect_key($data, array_flip(self::$tableInfo['@fields'])); 219 | 220 | $return = null; 221 | 222 | // insert action 223 | if (!$entity->hasPrimaryValue()) { 224 | $return = $this->db->getLink()->getAgent()->insert($this->table, $data); 225 | // set primary value 226 | $entity->setPrimaryValue($return); 227 | } else { 228 | // update action 229 | $return = $this->db->getLink()->getAgent()->update($this->table, $data, 230 | "{$this->tablePrimary} = ?", [$entity->getPrimaryValue()]); 231 | } 232 | 233 | if (method_exists($this, 'onSave')) { 234 | $this->onSave($return); 235 | } 236 | 237 | return $return; 238 | } 239 | 240 | /** 241 | * Remove. 242 | * @param Oppa\ActiveRecord\Entity $entity 243 | * @return int 244 | * @throws Oppa\Exception\InvalidValueException 245 | */ 246 | public final function remove(Entity $entity): int 247 | { 248 | $primaryValue = $entity->getPrimaryValue(); 249 | if ($primaryValue === null) { 250 | throw new InvalidValueException('Primary value not found on entity for delete action!'); 251 | } 252 | 253 | return $this->removeAll($primaryValue); 254 | } 255 | 256 | /** 257 | * Remove all. 258 | * @param any $whereParams 259 | * @return int 260 | * @throws Oppa\Exception\InvalidValueException 261 | */ 262 | public final function removeAll($whereParams): int 263 | { 264 | $whereParams = [$whereParams]; 265 | if ($whereParams[0] === null || $whereParams[0] === '') { 266 | throw new InvalidValueException('You need to pass a parameter for delete action!'); 267 | } 268 | 269 | $return = $this->db->getLink()->getAgent() 270 | ->deleteAll($this->table, "{$this->tablePrimary} IN(?)", $whereParams); 271 | 272 | if (method_exists($this, 'onRemove')) { 273 | $this->onRemove($return); 274 | } 275 | 276 | return $return; 277 | } 278 | 279 | /** 280 | * Count. 281 | * @param string $query 282 | * @param array $queryParams 283 | * @return ?int 284 | */ 285 | public final function count(string $query = null, array $queryParams = null): ?int 286 | { 287 | $queryBuilder = new QueryBuilder($this->db->getLink()); 288 | $queryBuilder->setTable($this->table); 289 | 290 | if ($query || $queryParams) { 291 | $queryBuilder->where($query, $queryParams); 292 | } 293 | 294 | return $queryBuilder->count(); 295 | } 296 | 297 | /** 298 | * Get table. 299 | * @return string 300 | */ 301 | public final function getTable(): string 302 | { 303 | return $this->table; 304 | } 305 | 306 | /** 307 | * Get table primary. 308 | * @return string 309 | */ 310 | public final function getTablePrimary(): string 311 | { 312 | return $this->tablePrimary; 313 | } 314 | 315 | /** 316 | * Get table info. 317 | * @return array 318 | */ 319 | public final function getTableInfo(): array 320 | { 321 | return self::$tableInfo; 322 | } 323 | 324 | /** 325 | * Set database. 326 | * @param Oppa\Database $db 327 | * @return void 328 | */ 329 | public final function setDatabase(Database $db): void 330 | { 331 | $this->db = $db; 332 | } 333 | 334 | /** 335 | * Get database. 336 | * @return Oppa\Database 337 | */ 338 | public final function getDatabase(): Database 339 | { 340 | return $this->db; 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /src/ActiveRecord/Entity.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\ActiveRecord; 28 | 29 | use Oppa\Util; 30 | use Oppa\Exception\InvalidKeyException; 31 | 32 | /** 33 | * @package Oppa 34 | * @object Oppa\ActiveRecord\Entity 35 | * @author Kerem Güneş 36 | */ 37 | final class Entity 38 | { 39 | /** 40 | * Data. 41 | * @var array 42 | */ 43 | private $data = []; 44 | 45 | /** 46 | * Methods. 47 | * @var array 48 | */ 49 | private $methods = []; 50 | 51 | /** 52 | * ActiveRecord. 53 | * @var Oppa\ActiveRecord\ActiveRecord 54 | */ 55 | private $activeRecord; 56 | 57 | /** 58 | * Constructor. 59 | * @param Oppa\ActiveRecord\ActiveRecord $activeRecord 60 | * @param array $data 61 | */ 62 | public function __construct(ActiveRecord $activeRecord, array $data = []) 63 | { 64 | // set owner active record 65 | $this->activeRecord = $activeRecord; 66 | 67 | $this->setData($data); 68 | } 69 | 70 | /** 71 | * Call. 72 | * @param string $methodName 73 | * @param array $methodArgs 74 | * @return any 75 | * @throws \BadMethodCallException 76 | */ 77 | public function __call(string $methodName, array $methodArgs) 78 | { 79 | $methodClosure = $this->getMethod($methodName); 80 | if ($methodClosure) { 81 | return call_user_func_array($methodClosure, $methodArgs); 82 | } 83 | 84 | throw new \BadMethodCallException("Method '{$methodName}' does not exists on this entity!"); 85 | } 86 | 87 | /** 88 | * Set. 89 | * @param string $key 90 | * @param any $value 91 | * @return void 92 | */ 93 | public function __set(string $key, $value) 94 | { 95 | $this->data[$key] = $value; 96 | } 97 | 98 | /** 99 | * Get. 100 | * @param string $key 101 | * @return any 102 | * @throws Oppa\Exception\InvalidKeyException 103 | */ 104 | public function __get(string $key) 105 | { 106 | if (array_key_exists($key, $this->data)) { 107 | return $this->data[$key]; 108 | } 109 | 110 | // check for camel-cased keys 111 | $keyCC = Util::upperToSnake($key); 112 | if (array_key_exists($keyCC, $this->data)) { 113 | return $this->data[$keyCC]; 114 | } 115 | 116 | throw new InvalidKeyException("Given '{$key}' key is not found on this entity!"); 117 | } 118 | 119 | /** 120 | * Isset. 121 | * @param string $key 122 | * @return bool 123 | */ 124 | public function __isset(string $key) 125 | { 126 | return array_key_exists($key, $this->data); 127 | } 128 | 129 | /** 130 | * Unset. 131 | * @param string $key 132 | * @return void 133 | */ 134 | public function __unset(string $key) 135 | { 136 | unset($this->data[$key]); 137 | } 138 | 139 | /** 140 | * Set data. 141 | * @param array $data 142 | * @return void 143 | */ 144 | public function setData(array $data): void 145 | { 146 | foreach ($data as $key => $value) { 147 | $this->data[$key] = $value; 148 | } 149 | } 150 | 151 | /** 152 | * Get data. 153 | * @return array 154 | */ 155 | public function getData(): array 156 | { 157 | return $this->data; 158 | } 159 | 160 | /** 161 | * To array. 162 | * @return array 163 | */ 164 | public function toArray(): array 165 | { 166 | return $this->data; 167 | } 168 | 169 | /** 170 | * To object. 171 | * @return \stdClass 172 | */ 173 | public function toObject(): \stdClass 174 | { 175 | return (object) $this->data; 176 | } 177 | 178 | /** 179 | * Is found. 180 | * @return bool 181 | */ 182 | public function isFound(): bool 183 | { 184 | return !empty($this->data); 185 | } 186 | 187 | /** 188 | * Is empty. 189 | * @return bool 190 | */ 191 | public function isEmpty(): bool 192 | { 193 | return empty($this->data); 194 | } 195 | 196 | /** 197 | * Save. 198 | * @return ?int 199 | */ 200 | public function save(): ?int 201 | { 202 | return $this->activeRecord->save($this); 203 | } 204 | 205 | /** 206 | * Remove. 207 | * @return int 208 | */ 209 | public function remove(): int 210 | { 211 | return $this->activeRecord->remove($this); 212 | } 213 | 214 | /** 215 | * Add method. 216 | * @param string $methodName 217 | * @param callable $methodClosure 218 | * @return void 219 | */ 220 | public function addMethod(string $methodName, callable $methodClosure): void 221 | { 222 | $this->methods[strtolower($methodName)] = $methodClosure->bindTo($this); 223 | } 224 | 225 | /** 226 | * Get method. 227 | * @param string $methodName 228 | * @return ?callable 229 | */ 230 | public function getMethod(string $methodName): ?callable 231 | { 232 | return $this->methods[strtolower($methodName)] ?? null; 233 | } 234 | 235 | /** 236 | * Get methods. 237 | * @return array 238 | */ 239 | public function getMethods(): array 240 | { 241 | return $this->methods; 242 | } 243 | 244 | /** 245 | * Get active record. 246 | * @return Oppa\ActiveRecord\ActiveRecord 247 | */ 248 | public function getActiveRecord(): ActiveRecord 249 | { 250 | return $this->activeRecord; 251 | } 252 | 253 | /** 254 | * Has primary value. 255 | * @return bool 256 | */ 257 | public function hasPrimaryValue(): bool 258 | { 259 | return isset($this->data[$this->activeRecord->getTablePrimary()]); 260 | } 261 | 262 | /** 263 | * Set primary value. 264 | * @param int|string $primaryValue 265 | * @return void 266 | */ 267 | public function setPrimaryValue($primaryValue): void 268 | { 269 | $this->data[$this->activeRecord->getTablePrimary()] = $primaryValue; 270 | } 271 | 272 | /** 273 | * Get primary value. 274 | * @return int|string|null 275 | */ 276 | public function getPrimaryValue() 277 | { 278 | return $this->data[$this->activeRecord->getTablePrimary()] ?? null; 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/ActiveRecord/EntityCollection.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\ActiveRecord; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\ActiveRecord\EntityCollection 32 | * @author Kerem Güneş 33 | */ 34 | final class EntityCollection implements \Countable, \IteratorAggregate 35 | { 36 | /** 37 | * Collection. 38 | * @var array 39 | */ 40 | private $collection = []; 41 | 42 | /** 43 | * Constructor. 44 | */ 45 | public function __construct() {} 46 | 47 | /** 48 | * Add. 49 | * @param Oppa\ActiveRecord\ActiveRecord $activeRecord 50 | * @param array $data 51 | * @return void 52 | */ 53 | public function add(ActiveRecord $activeRecord, array $data = []): void 54 | { 55 | $this->collection[] = new Entity($activeRecord, $data); 56 | } 57 | 58 | /** 59 | * Add entity. 60 | * @param Oppa\ActiveRecord\Entity $entity 61 | * @return void 62 | */ 63 | public function addEntity(Entity $entity): void 64 | { 65 | $this->collection[] = $entity; 66 | } 67 | 68 | /** 69 | * Remove. 70 | * @param int $i 71 | * @return void 72 | */ 73 | public function remove(int $i): void 74 | { 75 | unset($this->collection[$i]); 76 | } 77 | 78 | /** 79 | * Get. 80 | * @param int $i 81 | * @return ?Oppa\ActiveRecord\Entity 82 | */ 83 | public function item($i): ?Entity 84 | { 85 | return $this->collection[$i] ?? null; 86 | } 87 | 88 | /** 89 | * Item first. 90 | * @return ?Oppa\ActiveRecord\Entity 91 | */ 92 | public function itemFirst(): ?Entity 93 | { 94 | return $this->item(0); 95 | } 96 | 97 | /** 98 | * Item last. 99 | * @return ?Oppa\ActiveRecord\Entity 100 | */ 101 | public function itemLast(): ?Entity 102 | { 103 | return $this->item(count($this->collection) - 1); 104 | } 105 | 106 | /** 107 | * Is empty. 108 | * @return bool 109 | */ 110 | public function isEmpty(): bool 111 | { 112 | return empty($this->collection); 113 | } 114 | 115 | /** 116 | * Count. 117 | * @return int 118 | */ 119 | public function count(): int 120 | { 121 | return count($this->collection); 122 | } 123 | 124 | /** 125 | * Get iterator. 126 | * @return \ArrayIterator 127 | */ 128 | public function getIterator(): \ArrayIterator 129 | { 130 | return new \ArrayIterator($this->collection); 131 | } 132 | 133 | /** 134 | * Get collection. 135 | * @return array 136 | */ 137 | public function getCollection(): array 138 | { 139 | return $this->collection; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/Agent/AgentCrud.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Agent; 28 | 29 | use Oppa\Config; 30 | use Oppa\Batch\BatchInterface; 31 | use Oppa\Query\Result\ResultInterface; 32 | use Oppa\Exception\InvalidValueException; 33 | 34 | /** 35 | * @package Oppa 36 | * @object Oppa\Agent\AgentCrud 37 | * @author Kerem Güneş 38 | */ 39 | abstract class AgentCrud 40 | { 41 | /** 42 | * @inheritDoc Oppa\Agent\AgentInterface 43 | */ 44 | public final function select(string $table, $fields = null, $where = null, $whereParams = null, 45 | $order = null, $fetchType = null) 46 | { 47 | $return = $this->selectAll($table, $fields, $where, $whereParams, $order, 1, $fetchType); 48 | 49 | return $return[0] ?? null; 50 | } 51 | 52 | /** 53 | * @inheritDoc Oppa\Agent\AgentInterface 54 | */ 55 | public final function selectAll(string $table, $fields = null, $where = null, $whereParams = null, 56 | $order = null, $limit = null, $fetchType = null): ?array 57 | { 58 | if ($fields == null) { 59 | $fields = '*'; 60 | } 61 | 62 | $query = sprintf('SELECT %s FROM %s %s %s %s', 63 | $this->escapeIdentifier($fields), 64 | $this->escapeIdentifier($table), 65 | $this->where($where, (array) $whereParams), 66 | $this->order($order), 67 | $this->limit($limit) 68 | ); 69 | 70 | $return = $this->query($query, null, null, $fetchType)->getData(); 71 | 72 | return $return ?: null; 73 | } 74 | 75 | /** 76 | * @inheritDoc Oppa\Agent\AgentInterface 77 | */ 78 | public final function insert(string $table, array $data): ?int 79 | { 80 | $return = $this->insertAll($table, [$data]); 81 | 82 | return $return[0] ?? null; 83 | } 84 | 85 | /** 86 | * @inheritDoc Oppa\Agent\AgentInterface 87 | */ 88 | public final function insertAll(string $table, array $data): ?array 89 | { 90 | $keys = array_keys((array) @ $data[0]); 91 | $values = []; 92 | foreach ($data as $dat) { 93 | $values[] = '('. $this->escape(array_values((array) $dat)) .')'; 94 | } 95 | 96 | if (empty($keys) || empty($values)) { 97 | throw new InvalidValueException('Empty keys or/and values given!'); 98 | } 99 | 100 | $query = sprintf('INSERT INTO %s (%s) VALUES %s', 101 | $this->escapeIdentifier($table), 102 | $this->escapeIdentifier($keys), 103 | join(',', $values) 104 | ); 105 | 106 | $return = $this->query($query)->getIds(); 107 | 108 | return $return ?: null; 109 | } 110 | 111 | /** 112 | * @inheritDoc Oppa\Agent\AgentInterface 113 | */ 114 | public final function update(string $table, array $data, $where = null, $whereParams = null): int 115 | { 116 | return $this->updateAll($table, $data, $where, $whereParams, 1); 117 | } 118 | 119 | /** 120 | * @inheritDoc Oppa\Agent\AgentInterface 121 | */ 122 | public final function updateAll(string $table, array $data, $where = null, $whereParams = null, 123 | int $limit = null): int 124 | { 125 | if (empty($data)) { 126 | throw new InvalidValueException('Empty data given!'); 127 | } 128 | 129 | $set = []; 130 | foreach ($data as $key => $value) { 131 | $set[] = sprintf('%s = %s', $this->escapeIdentifier($key), $this->escape($value)); 132 | } 133 | 134 | $query = sprintf('UPDATE %s SET %s %s', 135 | $this->escapeIdentifier($table), 136 | join(', ', $set), 137 | $this->where($where, (array) $whereParams) 138 | ); 139 | 140 | // only mysql 141 | if ($limit != null && $this->isMysql()) { 142 | $query .= ' '. $this->limit($limit); 143 | } 144 | 145 | return $this->query($query)->getRowsAffected(); 146 | } 147 | 148 | /** 149 | * @inheritDoc Oppa\Agent\AgentInterface 150 | */ 151 | public final function delete(string $table, $where = null, $whereParams = null): int 152 | { 153 | return $this->deleteAll($table, $where, $whereParams, 1); 154 | } 155 | 156 | /** 157 | * @inheritDoc Oppa\Agent\AgentInterface 158 | */ 159 | public final function deleteAll(string $table, $where = null, $whereParams = null, 160 | $limit = null): int 161 | { 162 | $query = sprintf( 163 | 'DELETE FROM %s %s', 164 | $this->escapeIdentifier($table), 165 | $this->where($where, (array) $whereParams) 166 | ); 167 | 168 | // only mysql 169 | if ($limit != null && $this->isMysql()) { 170 | $query .= ' '. $this->limit($limit); 171 | } 172 | 173 | return $this->query($query)->getRowsAffected(); 174 | } 175 | 176 | /** 177 | * Get. 178 | * @param string $query 179 | * @param array $queryParams 180 | * @param string $fetchClass 181 | * @return object|array|null 182 | * @throws Oppa\Exception\{InvalidQueryException, InvalidResourceException, QueryException} 183 | */ 184 | public final function get(string $query, array $queryParams = null, string $fetchClass = null) 185 | { 186 | return $this->query($query, $queryParams, 1, $fetchClass)->getData(0); 187 | } 188 | 189 | /** 190 | * Get all. 191 | * @param string $query 192 | * @param array $queryParams 193 | * @param string $fetchClass 194 | * @return array 195 | * @throws Oppa\Exception\{InvalidQueryException, InvalidResourceException, QueryException} 196 | */ 197 | public final function getAll(string $query, array $queryParams = null, string $fetchClass = null): array 198 | { 199 | return $this->query($query, $queryParams, null, $fetchClass)->getData(); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/Agent/AgentException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Agent; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Agent\AgentException 32 | * @author Kerem Güneş 33 | */ 34 | final class AgentException extends \Exception 35 | {} 36 | -------------------------------------------------------------------------------- /src/Agent/AgentInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Agent; 28 | 29 | use Oppa\Config; 30 | use Oppa\Query\Result\ResultInterface; 31 | 32 | /** 33 | * @package Oppa 34 | * @object Oppa\Agent\AgentInterface 35 | * @author Kerem Güneş 36 | */ 37 | interface AgentInterface 38 | { 39 | /** 40 | * Init. 41 | * @param Oppa\Config $config 42 | * @return void 43 | * @throws Oppa\Agent\AgentException 44 | */ 45 | public function init(Config $config): void; 46 | 47 | /** 48 | * Connect. 49 | * @return void 50 | */ 51 | public function connect(): void; 52 | 53 | /** 54 | * Disconnect. 55 | * @return void 56 | */ 57 | public function disconnect(): void; 58 | 59 | /** 60 | * Is connected. 61 | * @return bool 62 | */ 63 | public function isConnected(): bool; 64 | 65 | /** 66 | * Query. 67 | * @param string $query 68 | * @param array $queryParams 69 | * @return Oppa\Query\Result\ResultInterface 70 | */ 71 | public function query(string $query, array $queryParams = null): ResultInterface; 72 | 73 | /** 74 | * Select. 75 | * @param string $table 76 | * @param string|array|null $fields 77 | * @param string|array|null $where 78 | * @param any|null $whereParams 79 | * @param array|string|null $order 80 | * @param int|string|null $fetchType 81 | * @return any 82 | */ 83 | public function select(string $table, $fields = null, $where = null, $whereParams = null, 84 | $order = null, $fetchType = null); 85 | 86 | /** 87 | * Select all. 88 | * @param string $table 89 | * @param string|array|null $fields 90 | * @param string|array|null $where 91 | * @param any|null $whereParams 92 | * @param array|string|null $order 93 | * @param int|array|null $limit 94 | * @param int|string|null $fetchType 95 | * @return ?array 96 | */ 97 | public function selectAll(string $table, $fields = null, $where = null, $whereParams = null, 98 | $order = null, $limit = null, $fetchType = null): ?array; 99 | 100 | /** 101 | * Insert. 102 | * @param string $table 103 | * @param array $data 104 | * @return ?int 105 | * @throws Oppa\Exception\InvalidValueException 106 | */ 107 | public function insert(string $table, array $data): ?int; 108 | 109 | /** 110 | * Insert all. 111 | * @param string $table 112 | * @param array $data 113 | * @return ?array 114 | * @throws Oppa\Exception\InvalidValueException 115 | */ 116 | public function insertAll(string $table, array $data): ?array; 117 | 118 | /** 119 | * Update. 120 | * @param string $table 121 | * @param array $data 122 | * @param string|array|null $where 123 | * @param any|null $whereParams 124 | * @return int 125 | * @throws Oppa\Exception\InvalidValueException 126 | */ 127 | public function update(string $table, array $data, $where = null, $whereParams = null): int; 128 | 129 | /** 130 | * Update all. 131 | * @param string $table 132 | * @param array $data 133 | * @param string|array|null $where 134 | * @param any|null $whereParams 135 | * @param int|null $limit 136 | * @return int 137 | * @throws Oppa\Exception\InvalidValueException 138 | */ 139 | public function updateAll(string $table, array $data, $where = null, $whereParams = null, 140 | int $limit = null): int; 141 | 142 | /** 143 | * Delete. 144 | * @param string $table 145 | * @param string|array|null where 146 | * @param any|null $whereParams 147 | * @return int 148 | */ 149 | public function delete(string $table, $where = null, $whereParams = null): int; 150 | 151 | /** 152 | * Delete all. 153 | * @param string $table 154 | * @param string|array|null $where 155 | * @param any|null $whereParams 156 | * @param int|array|null $limit 157 | * @return int 158 | */ 159 | public function deleteAll(string $table, $where = null, $whereParams = null, 160 | $limit = null): int; 161 | 162 | /** 163 | * Count. 164 | * @param string $table 165 | * @param string|array|null where 166 | * @param any|null $whereParams 167 | * @return ?int 168 | */ 169 | public function count(string $table, $where = null, $whereParams = null): ?int; 170 | } 171 | -------------------------------------------------------------------------------- /src/Autoload.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Autoload 32 | * @author Kerem Güneş 33 | */ 34 | final class Autoload 35 | { 36 | /** 37 | * Singleton stuff. 38 | * @var self 39 | */ 40 | private static $instance; 41 | 42 | /** 43 | * Forbidding idle init & copy actions. 44 | */ 45 | private function __construct() {} 46 | private function __clone() {} 47 | 48 | /** 49 | * Init. 50 | * @return self 51 | */ 52 | public static function init(): self 53 | { 54 | if (self::$instance == null) { 55 | self::$instance = new self(); 56 | } 57 | 58 | return self::$instance; 59 | } 60 | 61 | /** 62 | * Register. 63 | * @return bool 64 | * @throws \RuntimeException 65 | */ 66 | public function register(): bool 67 | { 68 | return spl_autoload_register(function($objectName) { 69 | // ensure first namespace separator 70 | if ($objectName[0] != '\\') { 71 | $objectName = '\\'. $objectName; 72 | } 73 | 74 | // only Oppa stuff 75 | if (1 !== strpos($objectName, __namespace__)) { 76 | return; 77 | } 78 | 79 | $objectFile = strtr(sprintf('%s/%s.php', __dir__, $objectName), [ 80 | '\\' => '/', 81 | __namespace__ => '' 82 | ]); 83 | 84 | if (!is_file($objectFile)) { 85 | throw new \RuntimeException("Object file '{$objectFile}' not found!"); 86 | } 87 | 88 | require $objectFile; 89 | }); 90 | } 91 | } 92 | 93 | // shorcut for require 94 | return Autoload::init(); 95 | -------------------------------------------------------------------------------- /src/Batch/Batch.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Batch; 28 | 29 | use Oppa\{Util, Resource}; 30 | use Oppa\Agent\AgentInterface; 31 | use Oppa\Query\Result\ResultInterface; 32 | 33 | /** 34 | * @package Oppa 35 | * @object Oppa\Batch\Batch 36 | * @author Kerem Güneş 37 | */ 38 | abstract class Batch implements BatchInterface 39 | { 40 | /** 41 | * Agent. 42 | * @var Oppa\Agent\AgentInterface 43 | */ 44 | protected $agent; 45 | 46 | /** 47 | * Query queue. 48 | * @var array 49 | */ 50 | protected $queue = []; 51 | 52 | /** 53 | * Query results. 54 | * @var array 55 | */ 56 | protected $results = []; 57 | 58 | /** 59 | * Total transaction time. 60 | * @var float 61 | */ 62 | protected $totalTime = 0.00; 63 | 64 | /** 65 | * Get agent. 66 | * @return Oppa\Agent\AgentInterface 67 | */ 68 | public final function getAgent(): AgentInterface 69 | { 70 | return $this->agent; 71 | } 72 | 73 | /** 74 | * Get queue. 75 | * @return array 76 | */ 77 | public final function getQueue(): array 78 | { 79 | return $this->queue; 80 | } 81 | 82 | /** 83 | * Get result. 84 | * @param int $i 85 | * @return ?Oppa\Query\Result\ResultInterface 86 | */ 87 | public final function getResult(int $i = 0): ?ResultInterface 88 | { 89 | return $this->results[$i] ?? null; 90 | } 91 | 92 | /** 93 | * Get result id. 94 | * @param int $i 95 | * @return ?int 96 | */ 97 | public final function getResultId(int $i): ?int 98 | { 99 | return ($result = $this->getResult($i)) ? $result->getId() : null; 100 | } 101 | 102 | /** 103 | * Get result ids. 104 | * @param int $i 105 | * @return array 106 | */ 107 | public final function getResultIds(int $i): array 108 | { 109 | return ($result = $this->getResult($i)) ? $result->getIds() : []; 110 | } 111 | 112 | /** 113 | * Get results. 114 | * @return array 115 | */ 116 | public final function getResults(): array 117 | { 118 | return $this->results; 119 | } 120 | 121 | /** 122 | * Get results ids. 123 | * @param bool $merge 124 | * @return array 125 | */ 126 | public final function getResultsIds(bool $merge = true): array 127 | { 128 | $return = []; 129 | if (!empty($this->results)) { 130 | if ($merge) { 131 | foreach ($this->results as $result) { 132 | $return = array_merge($return, $result->getIds()); 133 | } 134 | } else { 135 | foreach ($this->results as $result) { 136 | $return[] = $result->getIds(); 137 | } 138 | } 139 | } 140 | 141 | return $return; 142 | } 143 | 144 | /** 145 | * Get total time. 146 | * @return float 147 | */ 148 | public final function getTotalTime(): float 149 | { 150 | return $this->totalTime; 151 | } 152 | 153 | /** 154 | * Reset. 155 | * @return void 156 | */ 157 | public final function reset(): void 158 | { 159 | $this->queue = []; 160 | $this->results = []; 161 | $this->totalTime = 0.00; 162 | } 163 | 164 | /** 165 | * Queue. 166 | * @param string $query 167 | * @param array|null $queryParams 168 | * @return self 169 | */ 170 | public final function queue(string $query, array $queryParams = null): BatchInterface 171 | { 172 | $this->queue[] = $this->agent->prepare($query, $queryParams); 173 | 174 | return $this; 175 | } 176 | 177 | /** 178 | * Do. 179 | * @return Oppa\Batch\BatchInterface 180 | */ 181 | public final function do(): BatchInterface 182 | { 183 | // no need to get excited 184 | if (empty($this->queue)) { 185 | return $this; 186 | } 187 | 188 | // check transaction status 189 | $resource = $this->agent->getResource(); 190 | /* if ($resource->getType() == Resource::TYPE_MYSQL_LINK) { 191 | // nope.. 192 | } else */ if ($resource->getType() == Resource::TYPE_PGSQL_LINK) { 193 | $resource = $resource->getObject(); 194 | $resourceStatus = pg_transaction_status($resource); 195 | if ($resourceStatus !== PGSQL_TRANSACTION_IDLE) do { 196 | time_nanosleep(0, 100000); 197 | $resourceStatus = pg_transaction_status($resource); 198 | } while ($resourceStatus === PGSQL_TRANSACTION_ACTIVE); 199 | } 200 | 201 | $startTime = microtime(true); 202 | 203 | $this->start(); // begin 204 | 205 | foreach ($this->queue as $query) { 206 | // @important (clone) 207 | $result = clone $this->agent->query($query); 208 | 209 | if ($result->getRowsAffected() > 0) { 210 | $this->results[] = $result; 211 | } 212 | 213 | unset($result); 214 | } 215 | 216 | $this->end(); // commit, go go go! 217 | 218 | $this->totalTime = (float) number_format(microtime(true) - $startTime, 10); 219 | 220 | $this->agent->getResult()->reset(); 221 | 222 | return $this; 223 | } 224 | 225 | /** 226 | * Do query. 227 | * @param string $query 228 | * @param array|null $queryParams 229 | * @return Oppa\Batch\BatchInterface 230 | */ 231 | public final function doQuery(string $query, array $queryParams = null): BatchInterface 232 | { 233 | return $this->queue($query, $queryParams)->do(); 234 | } 235 | 236 | /** 237 | * Run. 238 | * @deprecated 239 | */ 240 | public final function run(...$args) 241 | { 242 | Util::generateDeprecatedMessage($this, 'run()', 'do()'); 243 | 244 | return call_user_func_array([$this, 'do'], $args); 245 | 246 | } 247 | 248 | /** 249 | * Run query. 250 | * @deprecated 251 | */ 252 | public final function runQuery(...$args) 253 | { 254 | Util::generateDeprecatedMessage($this, 'runQuery()', 'doQuery()'); 255 | 256 | return call_user_func_array([$this, 'doQuery'], $args); 257 | } 258 | 259 | /** 260 | * Cancel. 261 | * @deprecated 262 | */ 263 | public final function cancel(...$args) 264 | { 265 | Util::generateDeprecatedMessage($this, 'cancel()', 'undo()'); 266 | 267 | return call_user_func_array([$this, 'undo'], $args); 268 | 269 | } 270 | 271 | /** 272 | * Start. 273 | * @return void 274 | */ 275 | abstract protected function start(): void; 276 | 277 | /** 278 | * End. 279 | * @return void 280 | */ 281 | abstract protected function end(): void; 282 | } 283 | -------------------------------------------------------------------------------- /src/Batch/BatchInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Batch; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Batch\BatchInterface 32 | * @author Kerem Güneş 33 | */ 34 | interface BatchInterface 35 | { 36 | /** 37 | * Lock. 38 | * @return bool 39 | */ 40 | public function lock(): bool; 41 | 42 | /** 43 | * Unlock. 44 | * @return bool 45 | */ 46 | public function unlock(): bool; 47 | 48 | /** 49 | * Do. 50 | * @return Oppa\Batch\BatchInterface 51 | */ 52 | public function do(): BatchInterface; 53 | 54 | /** 55 | * Do query. 56 | * @param string $query 57 | * @param array|null $queryParams 58 | * @return Oppa\Batch\BatchInterface 59 | */ 60 | public function doQuery(string $query, array $queryParams = null): BatchInterface; 61 | 62 | /** 63 | * Undo. 64 | * @return void 65 | */ 66 | public function undo(): void; 67 | } 68 | -------------------------------------------------------------------------------- /src/Batch/Mysql.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Batch; 28 | 29 | use Oppa\Agent; 30 | 31 | /** 32 | * @package Oppa 33 | * @object Oppa\Batch\Mysql 34 | * @author Kerem Güneş 35 | */ 36 | final class Mysql extends Batch 37 | { 38 | /** 39 | * Constructor. 40 | * @param Oppa\Agent\Mysql $agent 41 | */ 42 | public function __construct(Agent\Mysql $agent) 43 | { 44 | $this->agent = $agent; 45 | } 46 | 47 | /** 48 | * Lock. 49 | * @return bool 50 | */ 51 | public function lock(): bool 52 | { 53 | return $this->agent->getResource()->getObject()->autocommit(false); 54 | } 55 | 56 | /** 57 | * Unlock. 58 | * @return bool 59 | */ 60 | public function unlock(): bool 61 | { 62 | return $this->agent->getResource()->getObject()->autocommit(true); 63 | } 64 | 65 | /** 66 | * Start. 67 | * @return void 68 | */ 69 | protected function start(): void 70 | { 71 | $this->agent->getResource()->getObject()->begin_transaction(MYSQLI_TRANS_START_READ_WRITE); 72 | } 73 | 74 | /** 75 | * End. 76 | * @return void 77 | */ 78 | protected function end(): void 79 | { 80 | $this->agent->getResource()->getObject()->commit(); 81 | } 82 | 83 | /** 84 | * Undo. 85 | * @return void 86 | */ 87 | public function undo(): void 88 | { 89 | // mayday mayday 90 | $this->agent->getResource()->getObject()->rollback(); 91 | 92 | $this->reset(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Batch/Pgsql.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Batch; 28 | 29 | use Oppa\Agent; 30 | 31 | /** 32 | * @package Oppa 33 | * @object Oppa\Batch\Pgsql 34 | * @author Kerem Güneş 35 | */ 36 | final class Pgsql extends Batch 37 | { 38 | /** 39 | * Constructor. 40 | * @param Oppa\Agent\Pgsql $agent 41 | */ 42 | public function __construct(Agent\Pgsql $agent) 43 | { 44 | $this->agent = $agent; 45 | } 46 | 47 | /** 48 | * Lock. 49 | * @return bool 50 | */ 51 | public function lock(): bool 52 | { 53 | return true; 54 | } 55 | 56 | /** 57 | * Unlock. 58 | * @return bool 59 | */ 60 | public function unlock(): bool 61 | { 62 | return true; 63 | } 64 | 65 | /** 66 | * Start. 67 | * @return void 68 | */ 69 | protected function start(): void 70 | { 71 | pg_query($this->agent->getResource()->getObject(), 'BEGIN'); 72 | } 73 | 74 | /** 75 | * End. 76 | * @return void 77 | */ 78 | protected function end(): void 79 | { 80 | pg_query($this->agent->getResource()->getObject(), 'COMMIT'); 81 | } 82 | 83 | /** 84 | * Undo. 85 | * @return void 86 | */ 87 | public function undo(): void 88 | { 89 | // mayday mayday 90 | pg_query($this->agent->getResource()->getObject(), 'ROLLBACK'); 91 | 92 | $this->reset(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Cache.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Cache 32 | * @author Kerem Güneş 33 | */ 34 | final class Cache 35 | { 36 | /** 37 | * Ttl. 38 | * @const int 39 | */ 40 | public const TTL = 3600; 41 | 42 | /** 43 | * Directory. 44 | * @var string 45 | */ 46 | private $directory; 47 | 48 | /** 49 | * Constructor. 50 | * @param string $directory null 51 | */ 52 | public function __construct(string $directory = null) 53 | { 54 | $this->setDirectory($directory ?? sys_get_temp_dir() .'/oppa'); 55 | } 56 | 57 | /** 58 | * Set directory. 59 | * @param string $directory 60 | * @return void 61 | */ 62 | public function setDirectory(string $directory): void 63 | { 64 | $this->directory = $directory; 65 | } 66 | 67 | /** 68 | * Read. 69 | * @param string $file 70 | * @param any &$contents 71 | * @param bool $json 72 | * @param int|null $ttl 73 | * @return bool 74 | */ 75 | public function read(string $file, &$contents = null, bool $json = true, int $ttl = null): bool 76 | { 77 | $ok = $this->checkDirectory() && $this->checkFile($file); 78 | if (!$ok) { 79 | return false; 80 | } 81 | 82 | if (filemtime($file) < time() - ($ttl ?? self::TTL)) { 83 | $contents = null; 84 | 85 | unlink($file); // gc 86 | 87 | return false; 88 | } 89 | 90 | $contents = file_get_contents($file); 91 | if ($json && $contents !== false) { 92 | $contents = json_decode($contents, true); 93 | } 94 | 95 | return ($contents !== false); 96 | } 97 | 98 | /** 99 | * Write. 100 | * @param string $file 101 | * @param any $contents 102 | * @param bool $json 103 | * @return bool 104 | */ 105 | public function write(string $file, $contents, bool $json = true): bool 106 | { 107 | $ok = $this->checkDirectory() && $this->checkFile($file); 108 | if (!$ok) { 109 | return false; 110 | } 111 | 112 | if ($json) { 113 | $contents = json_encode($contents); 114 | } 115 | 116 | return (bool) file_put_contents($file, (string) $contents, LOCK_EX); 117 | } 118 | 119 | /** 120 | * Check directory. 121 | * @return bool 122 | */ 123 | private function checkDirectory(): bool 124 | { 125 | return file_exists($this->directory) || mkdir($this->directory, 0700, true); 126 | } 127 | 128 | /** 129 | * Check file. 130 | * @param string &$file 131 | * @return bool 132 | */ 133 | private function checkFile(string &$file): bool 134 | { 135 | $file = "{$this->directory}/{$file}"; 136 | 137 | return file_exists($file) || (touch($file) && chmod($file, 0600)); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Config 32 | * @author Kerem Güneş 33 | */ 34 | final class Config implements \ArrayAccess 35 | { 36 | /** 37 | * Options. 38 | * @var array 39 | */ 40 | private $options = []; 41 | 42 | /** 43 | * Constructor. 44 | * @param array $options 45 | */ 46 | public function __construct(array $options = []) 47 | { 48 | if (!empty($options)) { 49 | foreach ($options as $key => $value) { 50 | $this->options[$key] = $value; 51 | } 52 | } 53 | } 54 | 55 | /** 56 | * Set. 57 | * @param string $key 58 | * @param any $value 59 | * @return self 60 | */ 61 | public function __set(string $key, $value) 62 | { 63 | return $this->set($key, $value); 64 | } 65 | 66 | /** 67 | * Get. 68 | * @param string $key 69 | * @return any 70 | */ 71 | public function __get(string $key) 72 | { 73 | return $this->get($key); 74 | } 75 | 76 | 77 | /** 78 | * Isset. 79 | * @param string $key 80 | * @return bool 81 | */ 82 | public function __isset(string $key) 83 | { 84 | return isset($this->options[$key]); 85 | } 86 | 87 | /** 88 | * Unset. 89 | * @param string $key 90 | * @return void 91 | */ 92 | public function __unset(string $key) 93 | { 94 | unset($this->options[$key]); 95 | } 96 | 97 | 98 | /** 99 | * Set. 100 | * @param string $key 101 | * @param any $value 102 | * @return self 103 | */ 104 | public function set(string $key, $value): self 105 | { 106 | $this->options[$key] = $value; 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * Get. 113 | * @param string $key 114 | * @param any $value 115 | * @return any 116 | */ 117 | public function get(string $key, $value = null) 118 | { 119 | if ($this->__isset($key)) { 120 | $value = $this->options[$key]; 121 | } 122 | 123 | return $value; 124 | } 125 | 126 | /** 127 | * Set. 128 | * @param int|string $key 129 | * @param any $value 130 | * @return self 131 | */ 132 | public function offsetSet($key, $value) 133 | { 134 | return $this->set($key, $value); 135 | } 136 | 137 | /** 138 | * Get. 139 | * @param int|string $key 140 | * @return any 141 | */ 142 | public function offsetGet($key) 143 | { 144 | return $this->get($key); 145 | } 146 | 147 | /** 148 | * Check. 149 | * @param int|string $key 150 | * @return bool 151 | */ 152 | public function offsetExists($key) 153 | { 154 | return $this->__isset($key); 155 | } 156 | 157 | /** 158 | * Remove. 159 | * @param int|string $key 160 | * @return void 161 | */ 162 | public function offsetUnset($key) 163 | { 164 | $this->__unset($key); 165 | } 166 | 167 | /** 168 | * To array. 169 | * @return array 170 | */ 171 | public function toArray(): array 172 | { 173 | return $this->options; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/Database.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | use Oppa\Link\Linker; 30 | 31 | /** 32 | * @package Oppa 33 | * @object Oppa\Database 34 | * @author Kerem Güneş 35 | */ 36 | final class Database 37 | { 38 | /** 39 | * Info. @wait 40 | * @var array 41 | */ 42 | private $info; 43 | 44 | /** 45 | * Linker. 46 | * @var Oppa\Link\Linker 47 | */ 48 | private $linker; 49 | 50 | /** 51 | * Linker methods. 52 | * @var array 53 | */ 54 | private $linkerMethods = []; 55 | 56 | /** 57 | * Constructor. 58 | * @param array $config 59 | */ 60 | public function __construct(array $config) 61 | { 62 | $this->linker = new Linker($this, new Config($config)); 63 | // provide some speed instead using method_exists() each __call() exec 64 | $this->linkerMethods = array_fill_keys(get_class_methods($this->linker), true); 65 | } 66 | 67 | /** 68 | * Call magic (forwards all non-exists methods to Linker). 69 | * @link Proxy pattern 70 | * @param string $method 71 | * @param array|null $methodArgs 72 | * @return any 73 | * @throws Oppa\OppaException 74 | */ 75 | public function __call(string $method, array $methodArgs = null) 76 | { 77 | if (!isset($this->linkerMethods[$method])) { 78 | throw new OppaException(sprintf("No method such '%s()' on '%s' or '%s' objects!", 79 | $method, Database::class, Linker::class)); 80 | } 81 | 82 | return call_user_func_array([$this->linker, $method], $methodArgs); 83 | } 84 | 85 | /** 86 | * Get info. 87 | * @param string|null $key 88 | * @return any 89 | */ 90 | public function getInfo(string $key = null) 91 | { 92 | if ($this->info == null) { 93 | $resource = $this->linker->getLink()->getAgent()->getResource(); 94 | $resourceType = $resource->getType(); 95 | 96 | $serverVersion = $clientVersion = null; 97 | 98 | if ($resourceType == Resource::TYPE_MYSQL_LINK) { 99 | $object = $resource->getObject(); 100 | foreach (get_class_vars(get_class($object)) as $var => $_) { 101 | $this->info[$var] = $object->{$var}; 102 | } 103 | $serverVersion = preg_replace('~\s*([^ ]+).*~', '\1', $object->server_info); 104 | $clientVersion = preg_replace('~(?:[a-z]+\s+)?([^ ]+).*~i', '\1', $object->client_info); 105 | } elseif ($resourceType == Resource::TYPE_PGSQL_LINK) { 106 | $this->info = pg_version($resource->getObject()); 107 | $serverVersion = preg_replace('~\s*([^ ]+).*~', '\1', $this->info['server']); 108 | $clientVersion = preg_replace('~\s*([^ ]+).*~', '\1', $this->info['client']); 109 | } 110 | 111 | $this->info['serverVersion'] = $serverVersion; 112 | $this->info['clientVersion'] = $clientVersion; 113 | } 114 | 115 | return ($key == null) ? $this->info : $this->info[$key] ?? null; 116 | } 117 | 118 | /** 119 | * Get linker. 120 | * @return Oppa\Link\Linker 121 | */ 122 | public function getLinker(): Linker 123 | { 124 | return $this->linker; 125 | } 126 | 127 | /** 128 | * Get linker methods. 129 | * @return array 130 | */ 131 | public function getLinkerMethods(): array 132 | { 133 | return $this->linkerMethods; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Exception/ConnectionException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\ConnectionException 32 | * @author Kerem Güneş 33 | */ 34 | final class ConnectionException extends SqlException 35 | {} 36 | -------------------------------------------------------------------------------- /src/Exception/Error.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\Error 32 | * @author Kerem Güneş 33 | */ 34 | final class Error extends \Error 35 | {} 36 | -------------------------------------------------------------------------------- /src/Exception/InvalidConfigException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\InvalidConfigException 32 | * @author Kerem Güneş 33 | */ 34 | final class InvalidConfigException extends \InvalidArgumentException 35 | {} 36 | -------------------------------------------------------------------------------- /src/Exception/InvalidKeyException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\InvalidKeyException 32 | * @author Kerem Güneş 33 | */ 34 | final class InvalidKeyException extends \InvalidArgumentException 35 | {} 36 | -------------------------------------------------------------------------------- /src/Exception/InvalidQueryException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\InvalidQueryException 32 | * @author Kerem Güneş 33 | */ 34 | final class InvalidQueryException extends \InvalidArgumentException 35 | {} 36 | -------------------------------------------------------------------------------- /src/Exception/InvalidResourceException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\InvalidResourceException 32 | * @author Kerem Güneş 33 | */ 34 | final class InvalidResourceException extends \InvalidArgumentException 35 | {} 36 | -------------------------------------------------------------------------------- /src/Exception/InvalidValueException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\InvalidValueException 32 | * @author Kerem Güneş 33 | */ 34 | final class InvalidValueException extends \InvalidArgumentException 35 | {} 36 | -------------------------------------------------------------------------------- /src/Exception/MethodException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\MethodException 32 | * @author Kerem Güneş 33 | */ 34 | class MethodException extends \Exception 35 | {} 36 | -------------------------------------------------------------------------------- /src/Exception/QueryException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\QueryException 32 | * @author Kerem Güneş 33 | */ 34 | final class QueryException extends SqlException 35 | {} 36 | -------------------------------------------------------------------------------- /src/Exception/SqlException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Exception; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Exception\SqlException 32 | * @author Kerem Güneş 33 | */ 34 | class SqlException extends \Exception 35 | { 36 | /** 37 | * Sql state. 38 | * @var ?string 39 | */ 40 | protected $sqlState; 41 | 42 | /** 43 | * Constructor. 44 | * @param ?string $message 45 | * @param ?int $code 46 | * @param ?string $sqlState 47 | * @param ?\Throwable $previous 48 | */ 49 | public final function __construct(?string $message = '', ?int $code = 0, ?string $sqlState = null, 50 | ?\Throwable $previous = null) 51 | { 52 | // set state 53 | $this->sqlState = $sqlState; 54 | 55 | // prepend state to message 56 | if ($this->sqlState) { 57 | $message = sprintf('SQLSTATE[%s]: %s', $this->sqlState, $message); 58 | } 59 | 60 | parent::__construct((string) $message, (int) $code, $previous); 61 | } 62 | 63 | /** 64 | * Get sql state. 65 | * @return ?string 66 | */ 67 | public final function getSqlState(): ?string 68 | { 69 | return $this->sqlState; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Link/Link.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Link; 28 | 29 | use Oppa\{Database, Config}; 30 | use Oppa\Agent\{AgentInterface, Mysql, Pgsql}; 31 | 32 | /** 33 | * @package Oppa 34 | * @object Oppa\Link\Link 35 | * @author Kerem Güneş 36 | */ 37 | final class Link 38 | { 39 | /** 40 | * Database agent (aka worker, adapter etc.) names. 41 | * @const string 42 | */ 43 | public const AGENT_PGSQL = 'pgsql', 44 | AGENT_MYSQL = 'mysql'; 45 | 46 | /** 47 | * Link statuses. 48 | * @const int 49 | */ 50 | public const STATUS_CONNECTED = 1, 51 | STATUS_DISCONNECTED = 0; 52 | 53 | /** 54 | * Link types. 55 | * @const string 56 | */ 57 | public const TYPE_SINGLE = 'single', 58 | TYPE_MASTER = 'master', 59 | TYPE_SLAVE = 'slave'; 60 | 61 | /** 62 | * Link trait. 63 | * @object Oppa\Link\LinkTrait 64 | */ 65 | use LinkTrait; 66 | 67 | /** 68 | * Database. 69 | * @var Oppa\Database 70 | */ 71 | protected $database; 72 | 73 | /** 74 | * Config. 75 | * @var Oppa\Config 76 | */ 77 | protected $config; 78 | 79 | /** 80 | * Type. 81 | * @var string 82 | */ 83 | protected $type; 84 | 85 | /** 86 | * Host. 87 | * @var string 88 | */ 89 | protected $host; 90 | 91 | /** 92 | * Agent. 93 | * @var Oppa\Agent\AgentInterface 94 | */ 95 | protected $agent; 96 | 97 | /** 98 | * Agent name. 99 | * @var string 100 | */ 101 | protected $agentName; 102 | 103 | /** 104 | * Constructor. 105 | * @param Oppa\Database $database 106 | * @param Oppa\Config $config 107 | * @param string $type 108 | * @param string $host 109 | */ 110 | public function __construct(Database $database, Config $config, string $type, string $host) 111 | { 112 | $this->database = $database; 113 | $this->config = $config; 114 | $this->type = $type; 115 | $this->host = $host; 116 | 117 | // attach agent 118 | $this->attachAgent(); 119 | } 120 | 121 | /** 122 | * Destructor. 123 | */ 124 | public function __destruct() 125 | { 126 | $this->detachAgent(); 127 | } 128 | 129 | /** 130 | * Get type. 131 | * @return string 132 | */ 133 | public function getType(): string 134 | { 135 | return $this->type; 136 | } 137 | 138 | /** 139 | * Get host. 140 | * @return string 141 | */ 142 | public function getHost(): string 143 | { 144 | return $this->host; 145 | } 146 | 147 | /** 148 | * Get agent. 149 | * @return ?Oppa\Agent\AgentInterface 150 | */ 151 | public function getAgent(): ?AgentInterface 152 | { 153 | return $this->agent; 154 | } 155 | 156 | /** 157 | * Get agent name. 158 | * @return ?string 159 | */ 160 | public function getAgentName(): ?string 161 | { 162 | return $this->agentName; 163 | } 164 | 165 | /** 166 | * Open. 167 | * @return void 168 | */ 169 | public function open(): void 170 | { 171 | $this->agent->connect(); 172 | } 173 | 174 | /** 175 | * Close. 176 | * @return void 177 | */ 178 | public function close(): void 179 | { 180 | $this->agent->disconnect(); 181 | } 182 | 183 | /** 184 | * Status. 185 | * @return int If agent exists. 186 | * @return null If agent not exists. 187 | */ 188 | public function status(): ?int 189 | { 190 | if ($this->agent != null) { 191 | return $this->agent->isConnected() 192 | ? self::STATUS_CONNECTED : self::STATUS_DISCONNECTED; 193 | } 194 | 195 | return null; 196 | } 197 | 198 | /** 199 | * Attach agent. 200 | * @return void 201 | * @throws Oppa\Link\LinkException 202 | */ 203 | private function attachAgent(): void 204 | { 205 | $this->agentName = strtolower((string) $this->config['agent']); 206 | switch ($this->agentName) { 207 | case self::AGENT_MYSQL: 208 | $this->agent = new Mysql($this->config); 209 | break; 210 | case self::AGENT_PGSQL: 211 | $this->agent = new Pgsql($this->config); 212 | break; 213 | default: 214 | throw new LinkException("Sorry, but '{$this->agentName}' agent not implemented!"); 215 | } 216 | } 217 | 218 | /** 219 | * Detach agent. 220 | * @return void 221 | */ 222 | private function detachAgent(): void 223 | { 224 | $this->agent = null; 225 | $this->agentName = null; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/Link/LinkException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Link; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Link\LinkException 32 | * @author Kerem Güneş 33 | */ 34 | final class LinkException extends \Exception 35 | {} 36 | -------------------------------------------------------------------------------- /src/Link/LinkTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Link; 28 | 29 | use Oppa\{Database, Config}; 30 | 31 | /** 32 | * @package Oppa 33 | * @object Oppa\Link\LinkTrait 34 | * @author Kerem Güneş 35 | */ 36 | trait LinkTrait 37 | { 38 | /** 39 | * Get database. 40 | * @return Oppa\Database 41 | */ 42 | public function getDatabase(): Database 43 | { 44 | return $this->database; 45 | } 46 | 47 | /** 48 | * Get config. 49 | * @return Oppa\Config 50 | */ 51 | public function getConfig(): Config 52 | { 53 | return $this->config; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Link/Linker.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Link; 28 | 29 | use Oppa\{Util, Database, Config}; 30 | use Oppa\Exception\InvalidConfigException; 31 | 32 | /** 33 | * @package Oppa 34 | * @object Oppa\Link\Linker 35 | * @author Kerem Güneş 36 | */ 37 | final class Linker 38 | { 39 | /** 40 | * Link trait. 41 | * @object Oppa\Link\LinkTrait 42 | */ 43 | use LinkTrait; 44 | 45 | /** 46 | * Database. 47 | * @var Oppa\Database 48 | */ 49 | protected $database; 50 | 51 | /** 52 | * Config. 53 | * @var Oppa\Config 54 | */ 55 | protected $config; 56 | 57 | /** 58 | * Stack. 59 | * @var array 60 | */ 61 | protected $links = []; 62 | 63 | /** 64 | * Constructor. 65 | * @param Oppa\Database $database 66 | * @param Oppa\Config $config 67 | * @note For all methods in this object, config `host` parameter is important, 68 | * cos it is used as a key to prevent to create new links in excessive way. Thus, 69 | * host will be always set, even user does not pass/provide it. 70 | */ 71 | public function __construct(Database $database, Config $config) 72 | { 73 | $this->database = $database; 74 | $this->config = $config; 75 | } 76 | 77 | /** 78 | * Connect. 79 | * @param string|null $host 80 | * @return self 81 | * @throws Oppa\Exception\InvalidConfigException 82 | */ 83 | public function connect(string $host = null): self 84 | { 85 | // link is already active? 86 | if ($host && isset($this->links[$host])) { 87 | return $this; 88 | } 89 | 90 | // set type as single as default 91 | $type = Link::TYPE_SINGLE; 92 | 93 | // get config array, and database directives from config 94 | $config = $this->config->toArray(); 95 | $database = $config['database'] ?? []; 96 | 97 | // is master/slave active? 98 | if ($this->config->get('sharding') === true) { 99 | $master = $database['master'] ?? []; 100 | $slaves = $database['slaves'] ?? []; 101 | switch ($host) { 102 | // master as default 103 | case null: 104 | case Link::TYPE_MASTER: 105 | $type = Link::TYPE_MASTER; 106 | $database = array_merge($database, $master); 107 | break; 108 | // slave 109 | case Link::TYPE_SLAVE: 110 | $type = Link::TYPE_SLAVE; 111 | if ($slaves != null) { 112 | $database = array_merge($database, Util::arrayRand($slaves)); 113 | } 114 | break; 115 | default: 116 | // given host is master's host? 117 | if ($host == ($master['host'] ?? '')) { 118 | $type = Link::TYPE_MASTER; 119 | $database = array_merge($database, $master); 120 | } else { 121 | // or given host is slaves's host? 122 | $type = Link::TYPE_SLAVE; 123 | foreach ($slaves as $slave) { 124 | if (isset($slave['host']) && $slave['host'] == $host) { 125 | $database = array_merge($database, $slave); 126 | break; 127 | } 128 | } 129 | } 130 | } 131 | } 132 | 133 | // remove unused parts 134 | unset($config['database'], $database['master'], $database['slaves']); 135 | 136 | // merge configs 137 | $config = array_merge($config, $database); 138 | if (!isset($config['host'], $config['name'], $config['username'], $config['password'])) { 139 | throw new InvalidConfigException( 140 | 'Please specify all needed credentials (host, name, username, password) for a link!'); 141 | } 142 | 143 | // use host as a key for link stack 144 | $host = $config['host']; 145 | 146 | // create a new link if not exists 147 | if (!isset($this->links[$host])) { 148 | $link = new Link($this->database, new Config($config), $type, $host); 149 | $link->open(); 150 | // add link to links stack 151 | $this->setLink($host, $link); 152 | } 153 | 154 | return $this; 155 | } 156 | 157 | /** 158 | * Disconnect. 159 | * @param string|null $host 160 | * @return void 161 | */ 162 | public function disconnect(string $host = null): void 163 | { 164 | // link exists? 165 | if ($host && isset($this->links[$host])) { 166 | $this->links[$host]->close(); 167 | unset($this->links[$host]); 168 | } else { 169 | // check by host 170 | switch (trim((string) $host)) { 171 | // remove all links 172 | case '': 173 | case '*': 174 | foreach ($this->links as $i => $link) { 175 | $link->close(); 176 | unset($this->links[$i]); 177 | } 178 | break; 179 | // remove master link 180 | case Link::TYPE_MASTER: 181 | foreach ($this->links as $i => $link) { 182 | if ($link->getType() == Link::TYPE_MASTER) { 183 | $link->close(); 184 | unset($this->links[$i]); 185 | break; 186 | } 187 | } 188 | break; 189 | // remove slave links 190 | case Link::TYPE_SLAVE: 191 | foreach ($this->links as $i => $link) { 192 | if ($link->getType() == Link::TYPE_SLAVE) { 193 | $link->close(); 194 | unset($this->links[$i]); 195 | } 196 | } 197 | break; 198 | } 199 | } 200 | } 201 | 202 | /** 203 | * Is linked. 204 | * @param string|null $host 205 | * @return bool 206 | */ 207 | public function isLinked(string $host = null): bool 208 | { 209 | // link exists? 210 | // e.g: isLinked('localhost') 211 | if ($host && isset($this->links[$host])) { 212 | return ($this->links[$host]->status() === Link::STATUS_CONNECTED); 213 | } 214 | 215 | // without master/slave directives 216 | // e.g: isLinked() 217 | if (true !== $this->config->get('sharding')) { 218 | foreach ($this->links as $link) { 219 | return ($link->status() === Link::STATUS_CONNECTED); 220 | } 221 | } 222 | 223 | // with master/slave directives, check by host 224 | switch (trim((string) $host)) { 225 | // e.g: isLinked(), isLinked('master') 226 | case '': 227 | case Link::TYPE_MASTER: 228 | foreach ($this->links as $link) { 229 | if ($link->getType() == Link::TYPE_MASTER) { 230 | return ($link->status() === Link::STATUS_CONNECTED); 231 | } 232 | } 233 | break; 234 | // e.g: isLinked('slave1.mysql.local'), isLinked('slave') 235 | case Link::TYPE_SLAVE: 236 | foreach ($this->links as $link) { 237 | if ($link->getType() == Link::TYPE_SLAVE) { 238 | return ($link->status() === Link::STATUS_CONNECTED); 239 | } 240 | } 241 | break; 242 | } 243 | 244 | return false; 245 | } 246 | 247 | /** 248 | * Set link. 249 | * @param string $host 250 | * @param Oppa\Link\Link $link 251 | * @return void 252 | */ 253 | public function setLink(string $host, Link $link): void 254 | { 255 | $this->links[$host] = $link; 256 | } 257 | 258 | /** 259 | * Get link. 260 | * @param string|null $host 261 | * @return ?Oppa\Link\Link 262 | */ 263 | public function getLink(string $host = null): ?Link 264 | { 265 | // link exists? 266 | // e.g: getLink('localhost') 267 | if ($host && isset($this->links[$host])) { 268 | return $this->links[$host]; 269 | } 270 | 271 | $host = trim((string) $host); 272 | // with master/slave directives 273 | if ($this->config->get('sharding') === true) { 274 | // e.g: getLink(), getLink('master'), getLink('master.mysql.local') 275 | if ($host == '' || $host == Link::TYPE_MASTER) { 276 | return Util::arrayRand(array_filter($this->links, function($link) { 277 | return ($link->getType() == Link::TYPE_MASTER); 278 | })); 279 | } 280 | // e.g: getLink(), getLink('slave'), getLink('slave1.mysql.local') 281 | elseif ($host == Link::TYPE_SLAVE) { 282 | return Util::arrayRand(array_filter($this->links, function($link) { 283 | return ($link->getType() == Link::TYPE_SLAVE); 284 | })); 285 | } 286 | } else { 287 | // e.g: getLink() 288 | if ($host == '') { 289 | return Util::arrayRand(array_filter($this->links, function($link) { 290 | return ($link->getType() == Link::TYPE_SINGLE); 291 | })); 292 | } 293 | } 294 | } 295 | 296 | /** 297 | * Get config. 298 | * @return Oppa\Config 299 | */ 300 | public function getConfig(): Config 301 | { 302 | return $this->config; 303 | } 304 | 305 | /** 306 | * Get links. 307 | * @return array 308 | */ 309 | public function getLinks(): array 310 | { 311 | return $this->links; 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /src/Logger.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | use Oppa\Exception\InvalidConfigException; 30 | 31 | /** 32 | * @package Oppa 33 | * @object Oppa\Logger 34 | * @author Kerem Güneş 35 | */ 36 | final class Logger 37 | { 38 | /** 39 | * Levels. 40 | * @const int 41 | */ 42 | public const ALL = 30, // FAIL | WARN | INFO | DEBUG, 43 | FAIL = 2, 44 | WARN = 4, 45 | INFO = 8, 46 | DEBUG = 16; 47 | 48 | /** 49 | * Level. 50 | * @var int 51 | */ 52 | private $level = 0; // default=disabled 53 | 54 | /** 55 | * Directory. 56 | * @var string 57 | */ 58 | private $directory; 59 | 60 | /** 61 | * Directory checked. 62 | * @var bool 63 | */ 64 | private static $directoryChecked = false; 65 | 66 | /** 67 | * Constructor. 68 | */ 69 | public function __construct() {} 70 | 71 | /** 72 | * Set level. 73 | * @param int $level 74 | * @return void 75 | */ 76 | public function setLevel(int $level): void 77 | { 78 | $this->level = $level; 79 | } 80 | 81 | /** 82 | * Get level. 83 | * @return int 84 | */ 85 | public function getLevel(): int 86 | { 87 | return $this->level; 88 | } 89 | 90 | /** 91 | * Set directory. 92 | * @param string $directory 93 | * @return void 94 | */ 95 | public function setDirectory(string $directory): void 96 | { 97 | $this->directory = $directory; 98 | } 99 | 100 | /** 101 | * Get directory. 102 | * @return ?string 103 | */ 104 | public function getDirectory(): ?string 105 | { 106 | return $this->directory; 107 | } 108 | 109 | /** 110 | * Check directory (if not exists create it). 111 | * @return bool 112 | * @throws Oppa\Exception\InvalidConfigException 113 | */ 114 | public function checkDirectory(): bool 115 | { 116 | if (empty($this->directory)) { 117 | throw new InvalidConfigException( 118 | "Log directory is not defined in given configuration, ". 119 | "define it using 'query_log_directory' key to activate logging."); 120 | } 121 | 122 | // provide some performance escaping to call "is_dir" and "mkdir" functions 123 | self::$directoryChecked = self::$directoryChecked ?: is_dir($this->directory); 124 | if (!self::$directoryChecked) { 125 | self::$directoryChecked = mkdir($this->directory, 0644, true); 126 | 127 | // !!! notice !!! 128 | // set your log dir secure 129 | file_put_contents($this->directory .'/index.php', 130 | ""); 131 | // this action is for only apache, see nginx configuration here: 132 | // http://nginx.org/en/docs/http/ngx_http_access_module.html 133 | file_put_contents($this->directory .'/.htaccess', 134 | "Order deny,allow\r\nDeny from all"); 135 | } 136 | 137 | return self::$directoryChecked; 138 | } 139 | 140 | /** 141 | * Log. 142 | * @param int $level 143 | * @param string $message 144 | * @return void|bool 145 | */ 146 | public function log(int $level, string $message) 147 | { 148 | // no log command 149 | if (!$level || ($level & $this->level) == 0) { 150 | return; 151 | } 152 | 153 | // ensure log directory 154 | $this->checkDirectory(); 155 | 156 | // prepare message prepend 157 | $levelText = ''; 158 | switch ($level) { 159 | case self::FAIL: 160 | $levelText = 'FAIL'; 161 | break; 162 | case self::INFO: 163 | $levelText = 'INFO'; 164 | break; 165 | case self::WARN: 166 | $levelText = 'WARN'; 167 | break; 168 | case self::DEBUG: 169 | $levelText = 'DEBUG'; 170 | break; 171 | } 172 | 173 | // prepare message & message file 174 | $message = sprintf('[%s] %s >> %s', $levelText, date('D, d M Y H:i:s O'), trim($message) ."\n"); 175 | $messageFile = sprintf('%s/%s.log', $this->directory, date('Y-m-d')); 176 | 177 | return error_log($message, 3, $messageFile); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Mapper.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Mapper 32 | * @author Kerem Güneş 33 | */ 34 | final class Mapper 35 | { 36 | /** 37 | * Type constants. 38 | * @const string 39 | */ 40 | public const 41 | // ints 42 | DATA_TYPE_INT = 'int', 43 | DATA_TYPE_BIGINT = 'bigint', 44 | DATA_TYPE_TINYINT = 'tinyint', 45 | DATA_TYPE_SMALLINT = 'smallint', 46 | DATA_TYPE_MEDIUMINT = 'mediumint', 47 | DATA_TYPE_INTEGER = 'integer', 48 | DATA_TYPE_SERIAL = 'serial', 49 | DATA_TYPE_BIGSERIAL = 'bigserial', 50 | // floats 51 | DATA_TYPE_FLOAT = 'float', 52 | DATA_TYPE_DECIMAL = 'decimal', 53 | DATA_TYPE_DOUBLE = 'double', 54 | DATA_TYPE_DOUBLEP = 'double precision', 55 | DATA_TYPE_REAL = 'real', 56 | DATA_TYPE_NUMERIC = 'numeric', 57 | // boolean 58 | DATA_TYPE_BOOLEAN = 'boolean', 59 | // bit 60 | DATA_TYPE_BIT = 'bit'; 61 | 62 | /** 63 | * Map. 64 | * @var array 65 | */ 66 | protected $map = []; 67 | 68 | /** 69 | * Map options. 70 | * @var array 71 | */ 72 | protected $mapOptions = [ 73 | 'bool' => false, // converts bits & tinyints to booleans 74 | ]; 75 | 76 | /** 77 | * Constructor. 78 | * @param array $mapOptions 79 | */ 80 | public function __construct(array $mapOptions = []) 81 | { 82 | $this->setMapOptions($mapOptions); 83 | } 84 | 85 | /** 86 | * Set map. 87 | * @param array $map 88 | * @return void 89 | */ 90 | public function setMap(array $map): void 91 | { 92 | $this->map = $map; 93 | } 94 | 95 | /** 96 | * Get map. 97 | * @return array 98 | */ 99 | public function getMap(): array 100 | { 101 | return $this->map; 102 | } 103 | 104 | /** 105 | * Set map options. 106 | * @param array $mapOptions 107 | * @return void 108 | */ 109 | public function setMapOptions(array $mapOptions): void 110 | { 111 | $this->mapOptions = array_merge($this->mapOptions, $mapOptions); 112 | } 113 | 114 | /** 115 | * Get map options. 116 | * @return array 117 | */ 118 | public function getMapOptions(): array 119 | { 120 | return $this->mapOptions; 121 | } 122 | 123 | /** 124 | * Map (given data by key). 125 | * @param string $key Table name actually, @see Oppa\Query\Result\Mysql:process() 126 | * @param array $data 127 | * @return array 128 | */ 129 | public function map(string $key, array $data): array 130 | { 131 | $fields = $this->map[$key] ?? null; 132 | if (empty($fields) || empty($data)) { 133 | return $data; 134 | } 135 | 136 | foreach ($fields as $fieldName => $fieldProperties) { 137 | foreach ($data as &$dat) { 138 | foreach ($dat as $name => $value) { 139 | if ($name == $fieldName) { // match field? 140 | if (is_array($dat)) { 141 | $dat[$name] = $this->cast($value, $fieldProperties); 142 | } elseif (is_object($dat)) { 143 | $dat->{$name} = $this->cast($value, $fieldProperties); 144 | } 145 | } 146 | } 147 | } 148 | } 149 | 150 | return $data; 151 | } 152 | 153 | /** 154 | * Cast (type-cast by data type). 155 | * @param scalar|null $value 156 | * @param array $properties 157 | * @return scalar|null 158 | */ 159 | public function cast($value, array $properties) 160 | { 161 | [$type, $length, $nullable] = $properties; 162 | 163 | // nullable? 164 | if ($value === null && $nullable) { 165 | return $value; 166 | } 167 | 168 | // int, float, bool 169 | switch ($type) { 170 | case 'int': return (int) $value; 171 | case 'float': return (float) $value; 172 | case 'bool': return ($value === 't') ? true : false; 173 | } 174 | 175 | // bit 176 | if ($this->mapOptions['bool'] && $type == 'bit' && $length == 1) { 177 | // bool cast 178 | $value = (string) $value; 179 | if ($value === '0' || $value === '1') { // @important 180 | return (bool) $value; 181 | } 182 | } 183 | 184 | return $value; 185 | } 186 | 187 | /** 188 | * Normalize type. 189 | * @param string $type 190 | * @return string 191 | */ 192 | public static function normalizeType(string $type): string 193 | { 194 | switch ($type) { 195 | case self::DATA_TYPE_INT: 196 | case self::DATA_TYPE_BIGINT: 197 | case self::DATA_TYPE_TINYINT: 198 | case self::DATA_TYPE_SMALLINT: 199 | case self::DATA_TYPE_MEDIUMINT: 200 | case self::DATA_TYPE_INTEGER: 201 | case self::DATA_TYPE_SERIAL: 202 | case self::DATA_TYPE_BIGSERIAL: 203 | return 'int'; 204 | case self::DATA_TYPE_FLOAT: 205 | case self::DATA_TYPE_DECIMAL: 206 | case self::DATA_TYPE_DOUBLE: 207 | case self::DATA_TYPE_DOUBLEP: 208 | case self::DATA_TYPE_REAL: 209 | case self::DATA_TYPE_NUMERIC: 210 | return 'float'; 211 | case self::DATA_TYPE_BOOLEAN: 212 | return 'bool'; 213 | case self::DATA_TYPE_BIT: 214 | return 'bit'; 215 | } 216 | 217 | // all others 218 | return 'string'; 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/Oppa.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Oppa 32 | * @author Kerem Güneş 33 | */ 34 | final class Oppa 35 | { 36 | /** 37 | * Instance. 38 | * @var Oppa\Database 39 | */ 40 | private static $instance; 41 | 42 | /** 43 | * Get instance. 44 | * @param array $config 45 | * @param bool $connect 46 | * @return Oppa\Database 47 | */ 48 | public static function getInstance(array $config, bool $connect = true): Database 49 | { 50 | if (self::$instance == null) { 51 | self::$instance = new Database($config); 52 | if ($connect) { 53 | self::$instance->connect(); 54 | } 55 | } 56 | return self::$instance; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/OppaException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\OppaException 32 | * @author Kerem Güneş 33 | */ 34 | final class OppaException extends \Exception 35 | {} 36 | -------------------------------------------------------------------------------- /src/Profiler.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | use Oppa\Exception\InvalidKeyException; 30 | 31 | /** 32 | * @package Oppa 33 | * @object Oppa\Profiler 34 | * @author Kerem Güneş 35 | */ 36 | final class Profiler 37 | { 38 | /** 39 | * Profiles. 40 | * @var array 41 | */ 42 | private $profiles = []; 43 | 44 | /** 45 | * Query count. 46 | * @var int 47 | */ 48 | private $queryCount = 0; 49 | 50 | /** 51 | * Constructor. 52 | */ 53 | public function __construct() {} 54 | 55 | /** 56 | * Get profile. 57 | * @param string $key 58 | * @return array 59 | * @throws Oppa\Exception\InvalidKeyException 60 | */ 61 | public function getProfile(string $key): array 62 | { 63 | if (isset($this->profiles[$key])) { 64 | return $this->profiles[$key]; 65 | } 66 | 67 | throw new InvalidKeyException("Could not find a profile with given '{$key}' key!"); 68 | } 69 | 70 | /** 71 | * Get profiles. 72 | * @return array 73 | */ 74 | public function getProfiles(): array 75 | { 76 | return $this->profiles; 77 | } 78 | 79 | /** 80 | * Add query. 81 | * @param string $query 82 | * @return void 83 | */ 84 | public function addQuery(string $query): void 85 | { 86 | $this->profiles['query'][++$this->queryCount]['string'] = $query; 87 | } 88 | 89 | /** 90 | * Get query. 91 | * @param int $i 92 | * @return ?array 93 | */ 94 | public function getQuery(int $i): ?array 95 | { 96 | return $this->profiles['query'][$i] ?? null; 97 | } 98 | 99 | /** 100 | * Get query string. 101 | * @param int $i 102 | * @return ?string 103 | */ 104 | public function getQueryString(int $i): ?string 105 | { 106 | return $this->profiles['query'][$i]['string'] ?? null; 107 | } 108 | 109 | /** 110 | * Get query time. 111 | * @param int $i 112 | * @return ?float 113 | */ 114 | public function getQueryTime(int $i): ?float 115 | { 116 | return $this->profiles['query'][$i]['time'] ?? null; 117 | } 118 | 119 | /** 120 | * Get query count. 121 | * @return int 122 | */ 123 | public function getQueryCount(): int 124 | { 125 | return $this->queryCount; 126 | } 127 | 128 | /** 129 | * Get last query. 130 | * @param string|null $key 131 | * @return array|string|float|null 132 | */ 133 | public function getLastQuery(string $key = null) 134 | { 135 | return $key ? $this->profiles['query'][$this->queryCount][$key] ?? null 136 | : $this->profiles['query'][$this->queryCount] ?? null; 137 | } 138 | 139 | /** 140 | * Get last query string. 141 | * @return ?string 142 | */ 143 | public function getLastQueryString(): ?string 144 | { 145 | return $this->getLastQuery('string'); 146 | } 147 | 148 | /** 149 | * Get last query time. 150 | * @return ?float 151 | */ 152 | public function getLastQueryTime(): ?float 153 | { 154 | return $this->getLastQuery('time'); 155 | } 156 | 157 | /** 158 | * Get total time. 159 | * @param bool $timeOnly 160 | * @return float|string|null 161 | */ 162 | public function getTotalTime(bool $timeOnly = true) 163 | { 164 | if ($this->profiles == null) { 165 | return null; 166 | } 167 | 168 | $totalTime = 0.00; 169 | $totalTimeString = ''; 170 | if (isset($this->profiles['connection'])) { 171 | $totalTime += $this->profiles['connection']['time']; 172 | if (!$timeOnly) { 173 | $totalTimeString .= "connection({$totalTime})"; 174 | } 175 | } 176 | 177 | if (isset($this->profiles['query'])) { 178 | foreach ($this->profiles['query'] as $i => $profile) { 179 | $totalTime += $profile['time']; 180 | if (!$timeOnly) { 181 | $totalTimeString .= " query({$i}, {$profile['time']})"; 182 | } 183 | } 184 | } 185 | 186 | if (!$timeOnly) { 187 | $totalTimeString .= " total({$totalTime})"; 188 | } 189 | 190 | return $timeOnly ? $totalTime : $totalTimeString; 191 | } 192 | 193 | /** 194 | * Start. 195 | * @param string $key 196 | * @return void 197 | * @throws Oppa\Exception\InvalidKeyException 198 | */ 199 | public function start(string $key): void 200 | { 201 | $start = microtime(true); 202 | switch ($key) { 203 | case 'connection': 204 | $this->profiles[$key] = [ 205 | 'start' => $start, 'end' => 0.00, 'time' => 0.00 206 | ]; 207 | break; 208 | case 'query': 209 | $i = $this->queryCount; 210 | if (isset($this->profiles[$key][$i])) { 211 | $this->profiles[$key][$i] += [ 212 | 'start' => $start, 'end' => 0.00, 'time' => 0.00 213 | ]; 214 | } 215 | break; 216 | default: 217 | throw new InvalidKeyException("Unimplemented key '{$key}' given, available keys are" 218 | ." 'connection,query' only!"); 219 | } 220 | } 221 | 222 | /** 223 | * End. 224 | * @param string $key 225 | * @return void 226 | * @throws Oppa\Exception\InvalidKeyException 227 | */ 228 | public function end(string $key): void 229 | { 230 | if (!isset($this->profiles[$key])) { 231 | throw new InvalidKeyException("Could not find a profile with given '{$key}' key!"); 232 | } 233 | 234 | $end = microtime(true); 235 | switch ($key) { 236 | case 'connection': 237 | $this->profiles['connection']['end'] = $end; 238 | $this->profiles['connection']['time'] = round($end - $this->profiles['connection']['start'], 10); 239 | break; 240 | case 'query': 241 | $i = $this->queryCount; 242 | if (isset($this->profiles['query'][$i])) { 243 | $this->profiles['query'][$i]['end'] = $end; 244 | $this->profiles['query'][$i]['time'] = round($end - $this->profiles['query'][$i]['start'], 10); 245 | } 246 | break; 247 | default: 248 | throw new InvalidKeyException("Unimplemented key '{$key}' given, available keys are" 249 | ." 'connection,query' only!"); 250 | } 251 | } 252 | 253 | /** 254 | * Reset. 255 | * @return void 256 | */ 257 | public function reset(): void 258 | { 259 | $this->profiles = []; 260 | $this->queryCount = 0; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/Query/BuilderException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Query; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Query\BuilderException 32 | * @author Kerem Güneş 33 | */ 34 | final class BuilderException extends \Exception 35 | {} 36 | -------------------------------------------------------------------------------- /src/Query/BuilderJsonTrait.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Query; 28 | 29 | use Oppa\Util; 30 | 31 | /** 32 | * @package Oppa 33 | * @object Oppa\Query\BuilderJsonTrait 34 | * @author Kerem Güneş 35 | */ 36 | trait BuilderJsonTrait 37 | { 38 | /** 39 | * Select json. 40 | * @param string|array $field 41 | * @param string $as 42 | * @param string $type 43 | * @param bool $reset 44 | * @return self 45 | * @throws Oppa\Query\BuilderException 46 | */ 47 | private function _selectJson($field, string $as, string $type, bool $reset = true): self 48 | { 49 | if (!in_array($type, ['array', 'object'])) { 50 | throw new BuilderException("Given JSON type '{$type}' is not implemented!"); 51 | } 52 | 53 | // reset 54 | $reset && $this->reset(); 55 | 56 | static $server, $serverVersion, $serverVersionMin, $fnJsonObject, $fnJsonArray, $toField, $toJson; 57 | 58 | // prepare static stuff 59 | if ($server == null) { 60 | if ($this->agent->isMysql()) { 61 | $server = 'MySQL'; $serverVersionMin = '5.7.8'; 62 | $fnJsonObject = 'json_object'; $fnJsonArray = 'json_array'; 63 | } elseif ($this->agent->isPgsql()) { 64 | $server = 'PostgreSQL'; $serverVersionMin = '9.4'; 65 | $fnJsonObject = 'json_build_object'; $fnJsonArray = 'json_build_array'; 66 | } 67 | 68 | $serverVersion = $this->link->getDatabase()->getInfo('serverVersion'); 69 | if (version_compare($serverVersion, $serverVersionMin) == -1) { 70 | throw new BuilderException(sprintf('JSON not supported by %s/v%s, minimum v%s required', 71 | $server, $serverVersion, $serverVersionMin)); 72 | } 73 | 74 | $toField = function ($field) { 75 | switch ($field) { 76 | case 'null': 77 | return null; 78 | case 'true': 79 | case 'false': 80 | return ($field == 'true') ? true : false; 81 | default: 82 | if (is_numeric($field)) { 83 | $field = strpos($field, '.') === false ? intval($field) : floatval($field); 84 | } elseif ($field && $field[0] == '@') { 85 | $field = $this->agent->escapeIdentifier($field); 86 | } else { 87 | $field = $this->agent->escape($field); 88 | } 89 | } 90 | return $field; 91 | }; 92 | 93 | $toJson = function ($values) use (&$toJson, &$toField, $fnJsonArray, $fnJsonObject) { 94 | $json = []; 95 | foreach ($values as $key => $value) { 96 | $keyType = gettype($key); 97 | $valueType = gettype($value); 98 | if ($valueType == 'array') { 99 | // eg: 'bar' => ['baz' => ['a', ['b' => ['c:d'], ...]]] 100 | $json[] = is_string($key) ? $this->agent->quote(trim($key)) .', '. $toJson($value) 101 | : $toJson($value); 102 | } elseif ($keyType == 'integer') { 103 | // eg: ['uid: @u.id' or 'uid' => '@u.id', 'user' => ['id: @u.id' or 'id' => '@u.id', ...], ...] 104 | if ($valueType == 'string' && strpbrk($value, ',:')) { 105 | if (strpos($value, ',')) { 106 | $json[] = $toJson(Util::split(',', $value)); 107 | } elseif (strpos($value, ':')) { 108 | [$key, $value] = Util::split(':', $value, 2); 109 | if (!isset($key, $value)) { 110 | throw new BuilderException('Field name and value must be given fo JSON objects!'); 111 | } 112 | 113 | if (!isset($json[0])) { 114 | $json[0] = $fnJsonObject; // tick 115 | } 116 | 117 | $json[] = $this->agent->quote(trim($key)) .', '. $toField($value); 118 | } 119 | } else { 120 | // eg: ['u.id', '@u.name', 1, 2, 3, true, ...] 121 | if ($valueType == 'integer') { 122 | $value = $toField($value); 123 | } 124 | 125 | if (!isset($json[0])) { 126 | $json[0] = $fnJsonArray; // tick 127 | } 128 | 129 | $json[] = $value; 130 | } 131 | } elseif ($keyType == 'string') { 132 | // eg: ['uid' => '@u.id'] 133 | if (!isset($json[0])) { 134 | $json[0] = $fnJsonObject; // tick 135 | } 136 | 137 | $json[] = $this->agent->quote(trim($key)) .', '. $toField($value); 138 | } 139 | } 140 | 141 | if ($json) { 142 | $fn = array_shift($json); 143 | $json = $fn ? $fn .'('. join(', ', $json) .')' : ''; 144 | if (substr($json, -2) == '()') { // .. :( 145 | $json = substr($json, 0, -2); 146 | } 147 | return $json; 148 | } 149 | 150 | return null; 151 | }; 152 | } 153 | 154 | $json = []; 155 | $jsonJoins = false; 156 | if (is_string($field)) { 157 | foreach (Util::split(',', $field) as $field) { 158 | if ($type == 'object') { 159 | // eg: 'id: @id, ...' 160 | [$key, $value] = Util::split(':', $field, 2); 161 | if (!isset($key, $value)) { 162 | throw new BuilderException('Field name and value must be given fo JSON objects!'); 163 | } 164 | $json[] = $this->agent->quote(trim($key)); 165 | $json[] = $toField($value); 166 | } elseif ($type == 'array') { 167 | // eg: 'id, ...' 168 | $json[] = $toField($field); 169 | } 170 | } 171 | } elseif (is_array($field)) { 172 | $keyIndex = 0; 173 | foreach ($field as $key => $value) { 174 | $keyType = gettype($key); 175 | if ($type == 'object') { 176 | // eg: ['id' => '@id', ... or 0 => 'id: @id, ...', ...] 177 | if ($keyType == 'integer') { 178 | $value = Util::split(',', $value); 179 | } elseif ($keyType != 'string') { 180 | throw new BuilderException("Field name must be string, {$keyType} given!"); 181 | } 182 | 183 | if (is_array($value)) { 184 | if ($keyType == 'string') { 185 | // eg: ['id' => '@id', ...] 186 | $key = $this->agent->quote(trim($key)); 187 | $json[$keyIndex][$key] = $toJson($value); 188 | } else { 189 | // eg: [0 => 'id: @id, ...', ...] 190 | $value = $toJson($value); 191 | $value = preg_replace('~json(?:_build)?_(?:object|array)\((.+)\)$~', '\1', $value); // :( 192 | $json[$keyIndex][] = [$value]; 193 | } 194 | $jsonJoins = true; 195 | continue; 196 | } elseif (is_string($value)) { 197 | $key = $this->agent->quote(trim($key)); 198 | $json[$keyIndex][$key] = $toJson(Util::split(',', $value)); 199 | $jsonJoins = true; 200 | continue; 201 | } 202 | 203 | $json[] = $key .', '. $toField($value); 204 | } elseif ($type == 'array') { 205 | // eg: ['@id', '@name', ...] 206 | if ($keyType != 'integer') { 207 | throw new BuilderException("Field name must be int, {$keyType} given!"); 208 | } 209 | $json[] = $toField($value); 210 | } 211 | $keyIndex++; 212 | } 213 | } else { 214 | throw new BuilderException(sprintf('String and array fields accepted only, %s given!', 215 | gettype($field))); 216 | } 217 | 218 | $as = $this->agent->quoteField($as); 219 | $fn = ($type == 'object') ? $fnJsonObject : $fnJsonArray; 220 | 221 | if ($jsonJoins) { 222 | $jsonJoins = []; 223 | foreach ($json[0] as $key => $value) { 224 | $jsonJoins[] = is_array($value) ? join(', ', $value) : $key .', '. $value; 225 | } 226 | $json = $jsonJoins; 227 | } 228 | 229 | return $this->push('select', sprintf('%s(%s) AS %s', $fn, join(', ', $json), $as)); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/Query/Identifier.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Query; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Query\Identifier 32 | * @author Kerem Güneş 33 | * @note Used to escape in Agent escape methods, and also in Builder to know identifiers to escape. 34 | */ 35 | final class Identifier extends RawSql 36 | {} 37 | -------------------------------------------------------------------------------- /src/Query/RawSql.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Query; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Query\RawSql 32 | * @author Kerem Güneş 33 | * @note Used to prevent escaping contents like NOW(), COUNT() etc. in agent.escape() methods. 34 | */ 35 | abstract class RawSql 36 | { 37 | /** 38 | * Contents. 39 | * @var string 40 | */ 41 | protected $contents; 42 | 43 | /** 44 | * Constructor. 45 | * @param string $contents 46 | */ 47 | public function __construct(string $contents) 48 | { 49 | $this->contents = trim($contents); 50 | } 51 | 52 | /** 53 | * String magic. 54 | * @return string 55 | */ 56 | public function __toString() 57 | { 58 | return $this->contents; 59 | } 60 | 61 | /** 62 | * To string. 63 | * @return string 64 | */ 65 | public function toString(): string 66 | { 67 | return $this->contents; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Query/Result/Mysql.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Query\Result; 28 | 29 | use Oppa\{Agent, Resource}; 30 | use Oppa\Exception\{InvalidResourceException, InvalidValueException}; 31 | 32 | /** 33 | * @package Oppa 34 | * @object Oppa\Query\Result\Mysql 35 | * @author Kerem Güneş 36 | */ 37 | final class Mysql extends Result 38 | { 39 | /** 40 | * Constructor. 41 | * @param Oppa\Agent\Mysql $agent 42 | */ 43 | public function __construct(Agent\Mysql $agent) 44 | { 45 | $this->agent = $agent; 46 | } 47 | 48 | /** 49 | * Process. 50 | * If query action contains "select", then process returned result. 51 | * If query action contains "update/delete" etc, then process affected result. 52 | * @param Oppa\Resource $resource 53 | * @param int $limit 54 | * @param int|string $fetchType 55 | * @return Oppa\Query\Result\ResultInterface 56 | * @throws Oppa\Exception\InvalidResourceException, Oppa\Exception\InvalidValueException 57 | */ 58 | public function process(Resource $resource, int $limit = null, $fetchType = null): ResultInterface 59 | { 60 | $agentResource = $this->agent->getResource(); 61 | if ($agentResource->getType() != Resource::TYPE_MYSQL_LINK) { 62 | throw new InvalidResourceException('Process resource must be instanceof \mysqli!'); 63 | } 64 | 65 | $resourceObject = $resource->getObject(); 66 | $agentResourceObject = $agentResource->getObject(); 67 | 68 | $rowsCount = 0; 69 | $rowsAffected = $agentResourceObject->affected_rows; 70 | if ($resource->getType() == Resource::TYPE_MYSQL_RESULT) { 71 | // insert,update,delete has no num_rows.. 72 | $rowsCount = (int) ($resourceObject->num_rows ?? 0); 73 | } 74 | 75 | $i = 0; 76 | // if results 77 | if ($rowsCount > 0) { 78 | $this->resource = $resource; 79 | 80 | if ($limit === null) { 81 | $limit = $this->fetchLimit; 82 | } elseif ($limit === -1) { 83 | $limit = ResultInterface::LIMIT; 84 | } 85 | 86 | $fetchType = ($fetchType === null) 87 | ? $this->fetchType : $this->detectFetchType($fetchType); 88 | 89 | switch ($fetchType) { 90 | case Result::AS_OBJECT: 91 | while ($i < $limit && $row = $resourceObject->fetch_object($this->fetchObject)) { 92 | $this->data[$i++] = $row; 93 | } 94 | break; 95 | case ResultInterface::AS_ARRAY_ASC: 96 | while ($i < $limit && $row = $resourceObject->fetch_assoc()) { 97 | $this->data[$i++] = $row; 98 | } 99 | break; 100 | case ResultInterface::AS_ARRAY_NUM: 101 | while ($i < $limit && $row = $resourceObject->fetch_array(MYSQLI_NUM)) { 102 | $this->data[$i++] = $row; 103 | } 104 | break; 105 | case ResultInterface::AS_ARRAY_ASCNUM: 106 | while ($i < $limit && $row = $resourceObject->fetch_array()) { 107 | $this->data[$i++] = $row; 108 | } 109 | break; 110 | default: 111 | $this->free(); 112 | 113 | throw new InvalidValueException("Could not implement given '{$fetchType}' fetch type!"); 114 | } 115 | 116 | // map result data if mapper exists 117 | if ($mapper =@ $this->agent->getMapper()) { 118 | $field = $resourceObject->fetch_field(); 119 | if (isset($field->orgtable)) { 120 | $this->data = $mapper->map($field->orgtable, $this->data); 121 | } 122 | } 123 | } 124 | 125 | $this->free(); 126 | 127 | $this->setRowsCount($i); 128 | $this->setRowsAffected($rowsAffected); 129 | 130 | // last insert id 131 | $id = (int) $agentResourceObject->insert_id; 132 | if ($id) { 133 | $ids = [$id]; 134 | 135 | // dirty ways to detect last insert id for multiple inserts 136 | // good point! http://stackoverflow.com/a/15664201/362780 137 | 138 | // only last id 139 | // if ($rowsAffected > 1) { 140 | // $id = ($id + $rowsAffected) - 1; 141 | // } 142 | 143 | // all ids 144 | // if ($rowsAffected > 1) { 145 | // for ($i = 0; $i < $rowsAffected - 1; $i++) { 146 | // $ids[] = $id + 1; 147 | // } 148 | // } 149 | 150 | // all ids (more tricky?) 151 | if ($rowsAffected > 1) { 152 | $ids = range($id, ($id + $rowsAffected) - 1); 153 | } 154 | 155 | $this->setIds($ids); 156 | } 157 | 158 | return $this; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/Query/Result/Pgsql.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Query\Result; 28 | 29 | use Oppa\{Agent, Resource}; 30 | use Oppa\Exception\{InvalidResourceException, InvalidValueException}; 31 | 32 | /** 33 | * @package Oppa 34 | * @object Oppa\Query\Result\Pgsql 35 | * @author Kerem Güneş 36 | */ 37 | final class Pgsql extends Result 38 | { 39 | /** 40 | * Constructor. 41 | * @param Oppa\Agent\Pgsql $agent 42 | */ 43 | public function __construct(Agent\Pgsql $agent) 44 | { 45 | $this->agent = $agent; 46 | } 47 | 48 | /** 49 | * Process. 50 | * If query action contains "select", then process returned result. 51 | * If query action contains "update/delete" etc, then process affected result. 52 | * @param Oppa\Resource $resource 53 | * @param int $limit 54 | * @param int|string $fetchType 55 | * @param string $query @internal 56 | * @return Oppa\Query\Result\ResultInterface 57 | * @throws Oppa\Exception\InvalidResourceException, Oppa\Exception\InvalidValueException 58 | */ 59 | public function process(Resource $resource, int $limit = null, $fetchType = null, 60 | string $query = null): ResultInterface 61 | { 62 | $agentResource = $this->agent->getResource(); 63 | if ($agentResource->getType() != Resource::TYPE_PGSQL_LINK) { 64 | throw new InvalidResourceException('Process resource must be type of pgsql link!'); 65 | } 66 | 67 | $resourceObject = $resource->getObject(); 68 | $agentResourceObject = $agentResource->getObject(); 69 | 70 | $rowsCount = 0; 71 | $rowsAffected = 0; 72 | if ($resource->getType() == Resource::TYPE_PGSQL_RESULT) { 73 | $rowsCount = pg_num_rows($resourceObject); 74 | $rowsAffected = pg_affected_rows($resourceObject); 75 | } 76 | 77 | $i = 0; 78 | // if results 79 | if ($rowsCount > 0) { 80 | $this->resource = $resource; 81 | 82 | if ($limit === null) { 83 | $limit = $this->fetchLimit; 84 | } elseif ($limit === -1) { 85 | $limit = ResultInterface::LIMIT; 86 | } 87 | 88 | $fetchType = ($fetchType === null) 89 | ? $this->fetchType : $this->detectFetchType($fetchType); 90 | 91 | switch ($fetchType) { 92 | case Result::AS_OBJECT: 93 | while ($i < $limit && $row = pg_fetch_object($resourceObject, null, $this->fetchObject)) { 94 | $this->data[$i++] = $row; 95 | } 96 | break; 97 | case ResultInterface::AS_ARRAY_ASC: 98 | while ($i < $limit && $row = pg_fetch_assoc($resourceObject)) { 99 | $this->data[$i++] = $row; 100 | } 101 | break; 102 | case ResultInterface::AS_ARRAY_NUM: 103 | while ($i < $limit && $row = pg_fetch_array($resourceObject, null, PGSQL_NUM)) { 104 | $this->data[$i++] = $row; 105 | } 106 | break; 107 | case ResultInterface::AS_ARRAY_ASCNUM: 108 | while ($i < $limit && $row = pg_fetch_array($resourceObject)) { 109 | $this->data[$i++] = $row; 110 | } 111 | break; 112 | default: 113 | $this->free(); 114 | 115 | throw new InvalidValueException("Could not implement given '{$fetchType}' fetch type!"); 116 | } 117 | 118 | // map result data if mapper exists 119 | if ($mapper =@ $this->agent->getMapper()) { 120 | $fieldTable = pg_field_table($resourceObject, 0); 121 | if ($fieldTable) { 122 | $this->data = $mapper->map($fieldTable, $this->data); 123 | } 124 | } 125 | } 126 | 127 | $this->free(); 128 | 129 | $this->setRowsCount($i); 130 | $this->setRowsAffected($rowsAffected); 131 | 132 | // last insert id 133 | if ($query && stripos($query, 'INSERT') === 0) { 134 | $result = pg_query($agentResourceObject, 'SELECT lastval() AS id'); 135 | if ($result) { 136 | $id = (int) pg_fetch_result($result, 'id'); 137 | if ($id) { 138 | $ids = [$id]; 139 | // multiple inserts 140 | if ($rowsAffected > 1) { 141 | $ids = range(($id - $rowsAffected) + 1, $id); 142 | } 143 | $this->setIds($ids); 144 | } 145 | pg_free_result($result); 146 | } 147 | } 148 | 149 | return $this; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/Query/Result/ResultInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Query\Result; 28 | 29 | use Oppa\Resource; 30 | 31 | /** 32 | * @package Oppa 33 | * @object Oppa\Query\Result\ResultInterface 34 | * @author Kerem Güneş 35 | */ 36 | interface ResultInterface extends \Countable, \IteratorAggregate, \ArrayAccess 37 | { 38 | /** 39 | * Fetch limit. 40 | * @const int 41 | */ 42 | public const LIMIT = 1000; // rows 43 | 44 | /** 45 | * Fetch types. 46 | * @const int 47 | */ 48 | public const AS_OBJECT = 1, // @default 49 | AS_ARRAY_ASC = 2, 50 | AS_ARRAY_NUM = 3, 51 | AS_ARRAY_ASCNUM = 4; 52 | 53 | /** 54 | * Free. 55 | * @return void 56 | */ 57 | public function free(): void; 58 | 59 | /** 60 | * Reset. 61 | * @return void 62 | */ 63 | public function reset(): void; 64 | 65 | /** 66 | * Process. 67 | * @param Oppa\Resource $result 68 | * @param int $limit 69 | * @param int|string $fetchType 70 | * @return Oppa\Query\Result\ResultInterface 71 | */ 72 | public function process(Resource $result, int $limit = null, $fetchType = null): ResultInterface; 73 | } 74 | -------------------------------------------------------------------------------- /src/Query/Sql.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\Query; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Query\Sql 32 | * @author Kerem Güneş 33 | * @note Used to prevent escaping contents like NOW(), COUNT() etc. in agent.escape() methods. 34 | */ 35 | class Sql extends RawSql 36 | {} 37 | -------------------------------------------------------------------------------- /src/Resource.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | use Oppa\Exception\InvalidResourceException; 30 | 31 | /** 32 | * @package Oppa 33 | * @object Oppa\Resource 34 | * @author Kerem Güneş 35 | */ 36 | final class Resource 37 | { 38 | /** 39 | * Types. 40 | * @const int 41 | */ 42 | public const TYPE_MYSQL_LINK = 1, 43 | TYPE_MYSQL_RESULT = 2, 44 | TYPE_PGSQL_LINK = 3, 45 | TYPE_PGSQL_RESULT = 4; 46 | 47 | /** 48 | * Type. 49 | * @var ?int 50 | */ 51 | private $type; 52 | 53 | /** 54 | * Object. 55 | * @var object|resource 56 | */ 57 | private $object; 58 | 59 | /** 60 | * Constructor. 61 | * @param object|resource $object 62 | * @throws Oppa\Exception\InvalidResourceException 63 | */ 64 | public function __construct($object) 65 | { 66 | switch (gettype($object)) { 67 | // mysql 68 | case 'object': 69 | if ($object instanceof \mysqli) { 70 | $this->type = self::TYPE_MYSQL_LINK; 71 | } elseif ($object instanceof \mysqli_result) { 72 | $this->type = self::TYPE_MYSQL_RESULT; 73 | } 74 | break; 75 | // pgsql 76 | case 'resource': 77 | $type = get_resource_type($object); 78 | if ($type == 'pgsql link') { 79 | $this->type = self::TYPE_PGSQL_LINK; 80 | } elseif ($type == 'pgsql result') { 81 | $this->type = self::TYPE_PGSQL_RESULT; 82 | } 83 | break; 84 | // unknown 85 | default: 86 | throw new InvalidResourceException('Unknown resource type!'); 87 | } 88 | 89 | $this->object = $object; 90 | } 91 | 92 | /** 93 | * Get type. 94 | * @return ?int 95 | */ 96 | public function getType(): ?int 97 | { 98 | return $this->type; 99 | } 100 | 101 | /** 102 | * Get object. 103 | * @return object|resource 104 | */ 105 | public function getObject() 106 | { 107 | return $this->object; 108 | } 109 | 110 | /** 111 | * Is valid. 112 | * @return bool 113 | */ 114 | public function isValid(): bool 115 | { 116 | return !!($this->type && $this->object); 117 | } 118 | 119 | /** 120 | * Close. 121 | * @return void 122 | */ 123 | public function close(): void 124 | { 125 | if ($this->type == self::TYPE_MYSQL_LINK) { 126 | $this->object->close(); 127 | } elseif ($this->type == self::TYPE_PGSQL_LINK) { 128 | pg_close($this->object); 129 | } 130 | 131 | $this->reset(); 132 | } 133 | 134 | /** 135 | * Free. 136 | * @return void 137 | */ 138 | public function free(): void 139 | { 140 | if ($this->type == self::TYPE_MYSQL_RESULT) { 141 | $this->object->free(); 142 | } elseif ($this->type == self::TYPE_PGSQL_RESULT) { 143 | pg_free_result($this->object); 144 | } 145 | 146 | $this->reset(); 147 | } 148 | 149 | /** 150 | * Reset. 151 | * @return void 152 | */ 153 | private function reset(): void 154 | { 155 | $this->type = null; 156 | $this->object = null; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/SqlState/MysqlClientError.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\SqlState; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\SqlState\MysqlClientError 32 | * @author Kerem Güneş 33 | * @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-client.html 34 | */ 35 | abstract /* static */ class MysqlClientError 36 | { 37 | public const 38 | /** 39 | * OK 40 | * @const int 41 | */ 42 | OK = 0, 43 | 44 | /** 45 | * Codes. 46 | * @const int 47 | */ 48 | UNKNOWN_ERROR = 2000, 49 | SOCKET_CREATE_ERROR = 2001, 50 | CONNECTION_ERROR = 2002, 51 | CONN_HOST_ERROR = 2003, 52 | IPSOCK_ERROR = 2004, 53 | UNKNOWN_HOST = 2005, 54 | SERVER_GONE_ERROR = 2006, 55 | VERSION_ERROR = 2007, 56 | OUT_OF_MEMORY = 2008, 57 | WRONG_HOST_INFO = 2009, 58 | LOCALHOST_CONNECTION = 2010, 59 | TCP_CONNECTION = 2011, 60 | SERVER_HANDSHAKE_ERR = 2012, 61 | SERVER_LOST = 2013, 62 | COMMANDS_OUT_OF_SYNC = 2014, 63 | NAMEDPIPE_CONNECTION = 2015, 64 | NAMEDPIPEWAIT_ERROR = 2016, 65 | NAMEDPIPEOPEN_ERROR = 2017, 66 | NAMEDPIPESETSTATE_ERROR = 2018, 67 | CANT_READ_CHARSET = 2019, 68 | NET_PACKET_TOO_LARGE = 2020, 69 | EMBEDDED_CONNECTION = 2021, 70 | PROBE_SLAVE_STATUS = 2022, 71 | PROBE_SLAVE_HOSTS = 2023, 72 | PROBE_SLAVE_CONNECT = 2024, 73 | PROBE_MASTER_CONNECT = 2025, 74 | SSL_CONNECTION_ERROR = 2026, 75 | MALFORMED_PACKET = 2027, 76 | WRONG_LICENSE = 2028, 77 | NULL_POINTER = 2029, 78 | NO_PREPARE_STMT = 2030, 79 | PARAMS_NOT_BOUND = 2031, 80 | DATA_TRUNCATED = 2032, 81 | NO_PARAMETERS_EXISTS = 2033, 82 | INVALID_PARAMETER_NO = 2034, 83 | INVALID_BUFFER_USE = 2035, 84 | UNSUPPORTED_PARAM_TYPE = 2036, 85 | SHARED_MEMORY_CONNECTION = 2037, 86 | SHARED_MEMORY_CONNECT_REQUEST_ERROR = 2038, 87 | SHARED_MEMORY_CONNECT_ANSWER_ERROR = 2039, 88 | SHARED_MEMORY_CONNECT_FILE_MAP_ERROR = 2040, 89 | SHARED_MEMORY_CONNECT_MAP_ERROR = 2041, 90 | SHARED_MEMORY_FILE_MAP_ERROR = 2042, 91 | SHARED_MEMORY_MAP_ERROR = 2043, 92 | SHARED_MEMORY_EVENT_ERROR = 2044, 93 | SHARED_MEMORY_CONNECT_ABANDONED_ERROR = 2045, 94 | SHARED_MEMORY_CONNECT_SET_ERROR = 2046, 95 | CONN_UNKNOW_PROTOCOL = 2047, 96 | INVALID_CONN_HANDLE = 2048, 97 | SECURE_AUTH = 2049, 98 | UNUSED_1 = 2049, 99 | FETCH_CANCELED = 2050, 100 | NO_DATA = 2051, 101 | NO_STMT_METADATA = 2052, 102 | NO_RESULT_SET = 2053, 103 | NOT_IMPLEMENTED = 2054, 104 | SERVER_LOST_EXTENDED = 2055, 105 | STMT_CLOSED = 2056, 106 | NEW_STMT_METADATA = 2057, 107 | ALREADY_CONNECTED = 2058, 108 | AUTH_PLUGIN_CANNOT_LOAD = 2059, 109 | DUPLICATE_CONNECTION_ATTR = 2060, 110 | AUTH_PLUGIN_ERR = 2061, 111 | INSECURE_API_ERR = 2062; 112 | } 113 | -------------------------------------------------------------------------------- /src/SqlState/SqlState.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa\SqlState; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\SqlState\SqlState 32 | * @author Kerem Güneş 33 | * @link https://www.postgresql.org/docs/9.6/static/errcodes-appendix.html 34 | */ 35 | abstract /* static */ class SqlState 36 | { 37 | public const 38 | /** 39 | * Oppa states. 40 | * @const string 41 | */ 42 | OPPA_CONNECTION_ERROR = 'OPPA0', 43 | OPPA_HOST_ERROR = 'OPPA1', 44 | OPPA_DATABASE_ERROR = 'OPPA2', 45 | OPPA_AUTHENTICATION_ERROR = 'OPPA3', 46 | OPPA_CHARSET_ERROR = 'OPPA4', 47 | OPPA_TIMEZONE_ERROR = 'OPPA5', 48 | OPPA_QUERY_ERROR = 'OPPA6'; 49 | } 50 | -------------------------------------------------------------------------------- /src/Util.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is furnished 12 | * to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | declare(strict_types=1); 26 | 27 | namespace Oppa; 28 | 29 | /** 30 | * @package Oppa 31 | * @object Oppa\Util 32 | * @author Kerem Güneş 33 | */ 34 | final /* static */ class Util 35 | { 36 | /** 37 | * Array rand. 38 | * @param array $array 39 | * @param any $value 40 | * @return any 41 | */ 42 | public static function arrayRand(array $array, $value = null) 43 | { 44 | shuffle($array); 45 | 46 | return $array[0] ?? $value; 47 | } 48 | 49 | /** 50 | * Array pick. 51 | * @param array &$array 52 | * @param int|string $key 53 | * @param any $value 54 | * @return any 55 | */ 56 | public static function arrayPick(array &$array, $key, $value = null) 57 | { 58 | if (array_key_exists($key, $array)) { 59 | $value = $array[$key] ?? $value; 60 | unset($array[$key]); 61 | } 62 | 63 | return $value; 64 | } 65 | 66 | /** 67 | * Convert given input from uppers to underscore. 68 | * @param string $input 69 | * @return string 70 | */ 71 | public static function upperToSnake(string $input): string 72 | { 73 | return preg_replace_callback('~([A-Z])~', function($m) { 74 | return '_'. strtolower($m[1]); 75 | }, $input); 76 | } 77 | 78 | /** 79 | * Get IP. 80 | * @return string 81 | */ 82 | public static function getIp(): string 83 | { 84 | $ip = 'unknown'; 85 | if ('' != ($ip = ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? ''))) { 86 | if (false !== strpos($ip, ',')) { 87 | $ip = trim((string) end(explode(',', $ip))); 88 | } 89 | } 90 | // all ok 91 | elseif ('' != ($ip = ($_SERVER['HTTP_CLIENT_IP'] ?? ''))) {} 92 | elseif ('' != ($ip = ($_SERVER['HTTP_X_REAL_IP'] ?? ''))) {} 93 | elseif ('' != ($ip = ($_SERVER['REMOTE_ADDR_REAL'] ?? ''))) {} 94 | elseif ('' != ($ip = ($_SERVER['REMOTE_ADDR'] ?? ''))) {} 95 | 96 | return $ip; 97 | } 98 | 99 | /** 100 | * Split. 101 | * @param string $pattern 102 | * @param string $input 103 | * @param int|null $size 104 | * @param int $flags 105 | * @return array 106 | */ 107 | public static function split(string $pattern, string $input, int $size = null, int $flags = 0): array 108 | { 109 | if ($pattern[0] != '~') { 110 | $pattern = '~\s*'. trim($pattern, '~') .'\s*~'; // make re pattern 111 | } 112 | 113 | $ret = (array) preg_split($pattern, $input, ($size = $size ?? -1), $flags += PREG_SPLIT_NO_EMPTY); 114 | 115 | // prevent "undefined index ..." 116 | if ($size != -1 && sizeof($ret) < $size) { 117 | $ret = array_pad($ret, $size, null); 118 | } 119 | 120 | return $ret; 121 | } 122 | 123 | /** 124 | * Generate deprecated message. 125 | * @param string|object $class 126 | * @param string $oldStuff 127 | * @param string $newStuff 128 | * @return void 129 | */ 130 | public static function generateDeprecatedMessage($class, string $oldStuff, string $newStuff): void 131 | { 132 | if (is_object($class)) { 133 | $class = get_class($class); 134 | } 135 | 136 | user_error(sprintf('%1$s::%2$s is deprecated, use %1$s::%3$s instead!', 137 | $class, $oldStuff, $newStuff), E_USER_DEPRECATED); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /test/_inc.php: -------------------------------------------------------------------------------- 1 | register(); 19 | -------------------------------------------------------------------------------- /test/a.php: -------------------------------------------------------------------------------- 1 | 'mysql', 8 | 'database' => [ 9 | 'host' => 'localhost', 'name' => 'test', 10 | 'username' => 'test', 'password' => '********', 11 | 'charset' => 'utf8', 'timezone' => '+00:00', 12 | ] 13 | ]; 14 | 15 | $db = new Database($cfg); 16 | $agent = $db->connect()->getLink()->getAgent(); 17 | 18 | // $s = $agent->prepare('sid = :sid, pid = :pid, a = ?, tid = :tid, b = %d', [ 19 | // 'aaa', 20 | // 'pid' => 2, 21 | // 'sid' => 1, 22 | // '9000', 23 | // 'tid' => 3 24 | // ]); 25 | $s = $agent->prepare('SELECT * FROM %n WHERE id = %i', ['foo', 1]); 26 | pre($s); 27 | -------------------------------------------------------------------------------- /test/active-record.php: -------------------------------------------------------------------------------- 1 | 'pgsql', 11 | 'profiling' => true, 12 | 'database' => [ 13 | 'host' => 'localhost', 'name' => 'test', 14 | 'username' => 'test', 'password' => '********', 15 | ] 16 | ]); 17 | } 18 | 19 | class Users extends ActiveRecord { 20 | protected $table = 'users'; 21 | protected $tablePrimary = 'id'; 22 | 23 | public function __construct() { 24 | parent::__construct(db()); 25 | } 26 | 27 | public function onFind(Builder $qb) { 28 | return $qb 29 | ->select('users.*') 30 | ->joinLeft('users_score', 'users_score.user_id = users.id') 31 | ->selectMore('sum(users_score.score) score') 32 | ->groupBy('users.id') 33 | ->orderBy('users.id') 34 | ; 35 | } 36 | 37 | public function onEntity($entity) { 38 | $entity->addMethod('getPageLink', function() use($entity) { 39 | return sprintf('%s', $entity->id, $entity->name); 40 | }); 41 | } 42 | } 43 | 44 | $users = new Users(); 45 | // pre($users); 46 | 47 | // $user = $users->find(1); 48 | // // pre($user); 49 | // pre($user->getPageLink(),1); 50 | // // prd($user->isFound()); 51 | 52 | // // $users = $users->findAll(); 53 | // $users = $users->findAll([1,2]); 54 | // // $users = $users->findAll('users.id in(?)', [[1,2]]); 55 | // // $users = $users->findAll('users.id in(?,?)', [1,2]); 56 | // // pre($users); 57 | // foreach ($users as $user) { 58 | // pre($user->getPageLink()); 59 | // } 60 | // $users = $users->findAll([-1,-2,-399]); 61 | // prd($users->isFound()); 62 | 63 | // insert 64 | // $user = $users->entity(); 65 | // $user->name = 'Deli'; 66 | // $user->old = rand(100,500); 67 | // prd($user->save()); 68 | // pre($user); 69 | 70 | // update 71 | // $user = $users->entity(); 72 | // $user->id = 933; 73 | // $user->name = 'Veli'; 74 | // $user->old = 55; 75 | // prd($users->save($user)); 76 | // pre($user); 77 | 78 | // update exists 79 | // $user = $users->find(933); 80 | // $user->old = 100; 81 | // pre($user->save()); 82 | 83 | // remove exists 84 | // $user = $users->entity(); 85 | // $user->id = 262; 86 | // pre($user->remove()); 87 | 88 | // $user = $users->find(261); 89 | // if ($user->isFound()) 90 | // pre($user->remove()); 91 | // else 92 | // pre("nö!"); 93 | 94 | // remove 95 | // $result = $users->removeAll(933); 96 | // $result = $users->removeAll([258,259,260]); 97 | // pre($result); 98 | -------------------------------------------------------------------------------- /test/batch.php: -------------------------------------------------------------------------------- 1 | 'mysql', 9 | 'database' => [ 10 | 'fetch_type' => 'object', 11 | 'charset' => 'utf8', 12 | 'timezone' => '+00:00', 13 | 'host' => 'localhost', 14 | 'name' => 'test', 15 | 'username' => 'test', 16 | 'password' => '********', 17 | ] 18 | ]; 19 | 20 | $db = new Database($cfg); 21 | try { 22 | $db->connect(); 23 | } catch(\Throwable $e) { 24 | print $e->getMessage() ."\n"; 25 | print $e->getCode() ."\n"; 26 | print $e->getSqlState() ."\n"; 27 | // throw $e; 28 | return; 29 | } 30 | 31 | $agent = $db->getLink()->getAgent(); 32 | 33 | // @tmp 34 | // $agent->query('delete from users where id > 3'); 35 | // $agent->query("select * from noneexists"); 36 | 37 | // pre($agent->getResourceStats(), 1); 38 | 39 | $batch = $agent->getBatch(); 40 | // set autocommit=1 41 | $batch->lock(); 42 | try { 43 | $agent->query("select * from noneexists"); 44 | // $batch->queue('insert into users (name,old) values (?,?)', ['John Doe', rand(1,100)]); 45 | // $batch->queue('insert into users (name,old!) values (?,?)', ['John Doe', rand(1,100)]); 46 | // $batch->queue('insert into users (name,old) values (?,?)', ['John Doe', rand(1,100)]); 47 | // $batch->queue('insert into users (name,old) values (?,?)', ['John Doe', rand(1,100)]); 48 | // $batch->queue('insert into users (name,old) values (?,?)', ['John Doe', rand(1,100)]); 49 | // $batch->queue('insert into users (name,old) values (?,?)', ['John Doe', rand(1,100)]); 50 | // $batch->queue('insert into users (name,old) values (?,?)', ['John Doe', rand(1,100)]); 51 | // $batch->queue('insert into users (name,old) values (?,?)', ['John Doe', rand(1,100)]); 52 | // $batch->queue('insert into users (name,old) values (?,?)', ['John Doe', rand(1,100)]); 53 | // $batch->queue('insert into userssssss (name,old) values (?,?)', ['John Doe', rand(1,100)]); 54 | // commit 55 | $batch->do(); 56 | // } catch (Oppa\Exception\QueryException $e) { 57 | } catch (Throwable $e) { 58 | print $e->getMessage() ."\n"; 59 | print $e->getCode() ."\n"; 60 | print $e->getSqlState() ."\n"; 61 | // rollback 62 | $batch->undo(); 63 | // throw $e; 64 | return; 65 | } 66 | // set autocommit=1 67 | $batch->unlock(); 68 | 69 | pre($batch->getResultsIds()); 70 | foreach ($batch->getResults() as $result) { 71 | print $result->getId() ."\n"; 72 | } 73 | 74 | // $batch->reset(); 75 | // pre($batch); 76 | // pre($db); 77 | -------------------------------------------------------------------------------- /test/connection.php: -------------------------------------------------------------------------------- 1 | 'pgsql', 10 | 'database' => [ 11 | 'fetch_type' => 'object', 12 | 'charset' => 'utf8', 13 | 'timezone' => '+00:00', 14 | // 'port' => 3306, 15 | 'host' => 'localhost', 16 | 'name' => 'test', 17 | 'username' => 'test', 18 | 'password' => '********', 19 | // 'options' => [MYSQLI_OPT_CONNECT_TIMEOUT => 3], // mysql 20 | // 'options' => ['connect_timeout' => 3], // pgsql 21 | ], 22 | // 'map_result' => true, 23 | // 'map_result_bool' => true, 24 | 'profile' => true, 25 | ]; 26 | 27 | $db = new Database($cfg); 28 | $db->connect(); 29 | 30 | $agent = $db->getLink()->getAgent(); 31 | 32 | // $s = $agent->query("update users set booleeeee=false where id=3"); 33 | // $s = $agent->query("select * from users order by id asc limit %d", ["3"]); 34 | // $s = $agent->query("insert into users (name,old) values ('Can',20)"); 35 | // $s = $agent->query("insert into users (name,old) values ('Can',20) returning currval('users_id_seq')"); 36 | // $s = $agent->query("insert into users (name,old) values ('a',20),('b',21)"); 37 | // $s = $agent->query("insert into users (name,old) values ('Can',20)"); 38 | // $s = $agent->count("select * from users where id > ? order by id", [10]); 39 | // SELECT reltuples::bigint FROM pg_class WHERE oid = 'public.foo'::regclass 40 | // $s = $agent->count("SELECT id FROM foo WHERE id < :id", ['id' => 10]); 41 | // $s = $agent->count("users"); 42 | // $s = $agent->count(null, "select * from users"); 43 | // pre($s,1); 44 | // pre($agent->getProfiler(),1); 45 | 46 | // $b = $db->getLink()->getAgent()->getBatch(); 47 | // $b->lock(); 48 | // try { 49 | // $b->queue("delete from users where id > 4"); 50 | // // $b->queue("insert into users (name,old) values ('a',20),('b',22)"); 51 | // // $b->queue("insert into users (name,old) values ('c',200),('d',220)"); 52 | // $b->queue("insert into users (name,old) values ('e',201)"); 53 | // $b->do(); 54 | // } catch (\Oppa\Exception\QueryException $e) { 55 | // pre($e->getMessage()); 56 | // $b->undo(); 57 | // } 58 | // $b->unlock(); 59 | // pre($b->getResults()); 60 | // pre($b->getResultsIds()); 61 | // foreach ($b->getResults() as $r) { 62 | // pre($r->getIds()); 63 | // } 64 | 65 | // pre($db); 66 | // pre($db->getLink()->getAgent()->isConnected()); 67 | // pre($db->getLink()->getAgent()->queryMulti(["select * from foos limit 1"])); // @TODO 68 | // pre($db->getLink()->getAgent()->query("select * from foos limit 1")); 69 | // pre($db->getLink()); 70 | // pre($db->getLink('localhost')); 71 | 72 | // // $db->disconnect(); 73 | // $db->disconnect('localhost'); 74 | // pre($db->getLink('localhost')); // err! 75 | 76 | /*** sharding ***/ 77 | $cfg = [ 78 | 'agent' => 'mysql', 79 | 'sharding' => true, 80 | 'database' => [ 81 | 'fetch_type' => 'object', 82 | 'charset' => 'utf8', 83 | 'timezone' => '+00:00', 84 | 'name' => 'test', 85 | 'port' => 3306, 86 | 'username' => 'test', 87 | 'password' => '********', 88 | 'master' => ['host' => 'master.mysql.local'], 89 | 'slaves' => [ 90 | ['host' => 'slave1.mysql.local'], 91 | ['host' => 'slave2.mysql.local'], 92 | ['host' => 'slave3.mysql.local'], 93 | ], 94 | // 'options' => [MYSQLI_OPT_CONNECT_TIMEOUT => 3], 95 | ] 96 | ]; 97 | 98 | $db = new Database($cfg); 99 | 100 | // // for master connection 101 | // $db->connect(); 102 | // $db->connect('master'); 103 | // $db->connect('master.mysql.local'); 104 | 105 | // // for slaves connection 106 | // // - if empty, then connects to master 107 | // // - so must be indicated as "slaves" or "slave.host.*" 108 | // $db->connect('slave'); // random 109 | // $db->connect('slave1.mysql.local'); 110 | // $db->connect('slave2.mysql.local'); 111 | // $db->connect('slave3.mysql.local'); 112 | // $db->connect('slave3.mysql.local'); // no more try to connect 113 | // $db->connect('slave3.mysql.local'); // no more try to connect 114 | // $db->connect('slave3.mysql.local'); // no more try to connect 115 | 116 | // $db->disconnect('master'); 117 | // $db->disconnect('slave'); 118 | // $db->disconnect('*'); 119 | // pre($db); 120 | 121 | // pre($db->getLink()); 122 | // pre($db->getLink('master')); 123 | // pre($db->getLink('master.mysql.local')); 124 | 125 | // pre($db->getLink('slave')); 126 | // pre($db->getLink('slave1.mysql.local')); 127 | // pre($db->getLink('slave2.mysql.local')); 128 | // pre($db->getLink('slave3.mysql.local')); 129 | -------------------------------------------------------------------------------- /test/crud.php: -------------------------------------------------------------------------------- 1 | 'mysql', 8 | 'profiling' => true, 9 | 'database' => [ 10 | 'fetch_type' => 'object', 11 | 'charset' => 'utf8', 12 | 'timezone' => '+00:00', 13 | 'host' => 'localhost', 14 | 'name' => 'test', 15 | 'username' => 'test', 16 | 'password' => '********', 17 | ] 18 | ]; 19 | 20 | $db = new Database($cfg); 21 | $db->connect(); 22 | 23 | $agent = $db->getLink()->getAgent(); 24 | 25 | // $result = $agent->select('users', ['id','name']); 26 | // $result = $agent->selectAll('users', ['id','name']); 27 | // $result = $agent->insert('users', ['name' => 'Ferhat', 'old' => 50]); 28 | // $result = $agent->insertAll('users', [['name' => 'Ferhat', 'old' => 50],['name' => 'Serhat', 'old' => 60]]); 29 | // $result = $agent->update('users', ['name' => 'Veli', 'old' => 60], 'id = ?', [4]); 30 | // $result = $agent->updateAll('users', ['name' => 'Veli', 'old' => 60], 'id > ?', [4]); 31 | // $result = $agent->delete('users', 'id = ?', [4]); 32 | // $result = $agent->deleteAll('users', 'id in (?,?)', [5,6]); 33 | // pre($result); 34 | 35 | pre($result); 36 | // pre($db); 37 | -------------------------------------------------------------------------------- /test/error.php: -------------------------------------------------------------------------------- 1 | 'mysql', 8 | // 'database' => [ 9 | // 'host' => 'localhost', 10 | // 'name' => 'test', 11 | // 'username' => 'test', 12 | // 'password' => '********', 13 | // ], 14 | // ]; 15 | 16 | // $db = Database($cfg); 17 | // try { 18 | // $agent = $db->connect()->getLink()->getAgent()->query('select * from nonexists'); 19 | // } catch (\Throwable $e) { 20 | // print $e->getMessage(); 21 | // } 22 | 23 | $cfg = [ 24 | 'agent' => 'mysql', 25 | 'database' => [ 26 | 'host' => 'localhost', 27 | 'name' => 'test', 28 | 'username' => 'test', 29 | 'password' => '********', 30 | ], 31 | 'query_error_handler' => function($exception, $query, $queryParams) { 32 | print $exception->getMessage(); 33 | } 34 | ]; 35 | 36 | $db = new Database($cfg); 37 | $db->connect()->getLink()->getAgent()->query('select * from nonexists'); 38 | -------------------------------------------------------------------------------- /test/gets.php: -------------------------------------------------------------------------------- 1 | 'mysql', 8 | 'database' => [ 9 | 'host' => 'localhost', 'name' => 'test', 10 | 'username' => 'test', 'password' => '********', 11 | 'charset' => 'utf8', 'timezone' => '+00:00', 12 | ], 13 | // 'fetch_type' => User::class, 14 | // 'fetch_limit' => 1, 15 | 'map_result' => true, 16 | 'map_result_bool' => true, 17 | ]; 18 | 19 | $db = new Database($cfg); 20 | $db->connect(); 21 | 22 | $agent = $db->getLink()->getAgent(); 23 | 24 | class User { 25 | // public function __construct() 26 | // { 27 | // // die(__class__); 28 | // } 29 | // public function __set($name,$value) 30 | // { 31 | // pre($name); 32 | // $this->{$name}=$value; 33 | // } 34 | } 35 | 36 | // $result = $agent->query("select * from users"); 37 | // pre($result->toClass(User::class)); 38 | // $result = $agent->query("select * from users", null, 1, User::class); 39 | // pre($result); 40 | 41 | // $result = $agent->get("select * from users", null, User::class); 42 | // $result = $agent->getAll("select * from users", null, User::class); 43 | 44 | // error 45 | // $agent->getResult()->setFetchObject("Foo\Bar"); 46 | // no error 47 | $agent->getResult()->setFetchObject("User"); 48 | $agent->getResult()->setFetchObject(User::class); 49 | 50 | $result = $agent->get("select * from users"); 51 | // $result = $agent->getAll("select * from users"); 52 | 53 | pre($result); 54 | 55 | 56 | -------------------------------------------------------------------------------- /test/logger.php: -------------------------------------------------------------------------------- 1 | setLevel(Logger::ALL); 8 | $logger->setDirectory(__dir__.'/../.logs'); 9 | 10 | $result = $logger->log(Logger::INFO, 'log...'); 11 | prd($result); 12 | -------------------------------------------------------------------------------- /test/profiler.php: -------------------------------------------------------------------------------- 1 | 'mysql', 9 | 'profile' => true, 10 | 'database' => [ 11 | 'fetch_type' => 'object', 12 | 'charset' => 'utf8', 13 | 'timezone' => '+00:00', 14 | 'host' => 'localhost', 15 | 'name' => 'test', 16 | 'username' => 'test', 17 | 'password' => '********', 18 | ] 19 | ]; 20 | 21 | $db = new Database($cfg); 22 | $db->connect(); 23 | 24 | $agent = $db->getLink()->getAgent(); 25 | $agent->query("delete from `users` where `id` > ?", [30000]); 26 | $agent->query("delete from `users` where `id` > ?", [40000]); 27 | 28 | pre($agent->getProfiler()); 29 | // pre($db); 30 | 31 | $profiler = $agent->getProfiler(); 32 | // $profiler->reset(); 33 | 34 | pre($profiler->getLastQuery()); 35 | // pre($profiler->getQueryCount()); 36 | // pre($profiler->getProfile($profiler::CONNECTION)); 37 | // pre($profiler->getProfile($profiler::QUERY)); 38 | -------------------------------------------------------------------------------- /test/query-builder.php: -------------------------------------------------------------------------------- 1 | 'mysql', 10 | 'profiling' => true, 11 | 'database' => [ 12 | 'fetch_type' => 'object', 13 | 'charset' => 'utf8', 14 | 'timezone' => '+00:00', 15 | 'host' => 'localhost', 16 | 'name' => 'test', 17 | 'username' => 'test', 18 | 'password' => '********', 19 | ] 20 | ]; 21 | 22 | $db = new Database($cfg); 23 | $db->connect(); 24 | 25 | $qb = new QueryBuilder($db->getLink()); 26 | $qb->setTable('users'); 27 | 28 | // $qb->select('id,name'); 29 | // $qb->select('id,name')->where('id=?', [1]); 30 | // $qb->select('id,name')->where('id=?', [1])->limit(1); 31 | // $qb->select('id,name')->whereLike('name LIKE ?', ['%Ker"em%']); 32 | // $qb->select('id,name')->whereLike('(id LIKE ? OR name LIKE ?)', ['2%', '%Ke_rem%']); 33 | 34 | // $qb->select('id,name') 35 | // ->where('id=?', [1]) 36 | // ->where('(name=? OR name=? OR old BETWEEN %d AND %d)', ['Kerem', 'Murat', 30, 40], $qb::OP_AND) 37 | // ; 38 | 39 | // $qb->select()->aggregate('count'); 40 | // pre($qb->get()); 41 | 42 | $qb->setTable('users u'); 43 | $qb->select('u.*, us.score, ul.login') 44 | ->aggregate('sum', 'us.score', 'sum_score') 45 | ->join('users_score us', 'us.user_id = u.id') 46 | ->joinLeft('users_login ul', 'ul.user_id = u.id') 47 | ->whereIn('u.id', [1,2,3]) 48 | ->whereBetween('u.old', [30,50]) 49 | ->whereNotNull('ul.login') 50 | ->groupBy('u.id') 51 | ->orderBy('old') 52 | ->having('sum_score <= ?', [30]) 53 | ->limit(0,10) 54 | ; 55 | 56 | pre($qb->toString()); 57 | pre($qb->get()); 58 | // pre($qb->getAll()); 59 | // pre($qb->run()); 60 | 61 | // insert 62 | // $qb->insert(['name' => 'Veli', 'old' => 25]); 63 | // $qb->insert([['name' => 'Veli', 'old' => 25], ['name' => 'Deli', 'old' => 29]]); 64 | // pre($qb->toString()); 65 | // $result = $qb->run(); 66 | // pre($result); 67 | // pre($result->getId()); 68 | // pre($result->getId(true)); 69 | 70 | // // // update 71 | // $qb->update(['old' => 100])->where('id > ?', [30])->limit(1); 72 | // $qb->update(['old' => 100])->where('id > ?', [30])->orderBy('id DESC')->limit(1); 73 | // pre($qb->toString()); 74 | // pre($qb->run()); 75 | 76 | // // delete 77 | // $qb->delete()->where('id > ?', [30])->limit(1); 78 | // $qb->delete()->where('id > ?', [30])->orderBy('id DESC')->limit(1); 79 | // $qb->delete()->where('id > ?', [30])->orderBy('id', $qb::OP_DESC)->limit(1); 80 | // $qb->delete()->whereBetween('id', [931,932])->limit(10); 81 | // $qb->delete()->where('id in(?)', [[931,932]])->limit(10); 82 | // pre($qb->toString()); 83 | // pre($qb->run()); 84 | 85 | // $qb->select('id,name'); 86 | // $qb->whereLessThan('id', 30); 87 | // $qb->whereGreaterThan('id', 20); 88 | // $qb->whereLessThanEqual('id', 30, 'OR'); 89 | // $qb->whereGreaterThanEqual('id', 20); 90 | 91 | // $qb->whereExists( 92 | // (new QueryBuilder($db->getLink(), 'foo')) 93 | // ->select('*') 94 | // ->where('y > ?') 95 | // , [10]); 96 | // $qb->whereExists('select * from foo where y > ?', [10]); 97 | // $qb->whereExists('select * from foo where y < ?', [20], 'OR'); 98 | 99 | prd($qb->toString()); 100 | pre($qb); 101 | // pre($db); 102 | -------------------------------------------------------------------------------- /test/query.php: -------------------------------------------------------------------------------- 1 | 'mysql', 9 | 'profiling' => true, 10 | 'database' => [ 11 | 'fetch_type' => 'object', 12 | 'charset' => 'utf8', 13 | 'timezone' => '+00:00', 14 | 'host' => 'localhost', 15 | 'name' => 'test', 16 | 'username' => 'test', 17 | 'password' => '********', 18 | ] 19 | ]; 20 | 21 | $db = new Database($cfg); 22 | $db->connect(); 23 | 24 | $agent = $db->getLink()->getAgent(); 25 | $agent->query("select * from `users` where `id` <> ?", [3]); 26 | pre($agent->rowsCount()); 27 | 28 | // pre($agent); 29 | // pre($db); 30 | -------------------------------------------------------------------------------- /test/result.php: -------------------------------------------------------------------------------- 1 | 'mysql', 9 | // 'profiling' => true, 10 | 'map_result' => true, 11 | 'map_result_tiny2bool' => true, 12 | 'database' => [ 13 | 'fetch_type' => 'object', 14 | 'charset' => 'utf8', 15 | 'timezone' => '+00:00', 16 | 'host' => 'localhost', 17 | 'name' => 'test', 18 | 'username' => 'test', 19 | 'password' => '********', 20 | ] 21 | ]; 22 | 23 | $db = new Database($cfg); 24 | $db->connect(); 25 | 26 | $agent = $db->getLink()->getAgent(); 27 | 28 | // $result = $agent->query("show tables"); 29 | // $result = $agent->query("describe users"); 30 | // $result = $agent->query("select * from information_schema.columns where table_schema = 'test'"); 31 | 32 | $result = $agent->query("select * from users u"); 33 | prd($result->getData()); 34 | pre($result); 35 | // pre($agent,1); 36 | 37 | // $result = $agent->query("select * from `users`"); 38 | // $result = $agent->query("select * from `users`"); 39 | // $result = $agent->query("select * from `users`"); 40 | // $result = $agent->query("select * from `users` where `id` = ?", [1]); 41 | // $result = $agent->query("select * from `users` where `id` IN(?)", [[1,2]]); 42 | // $result = $agent->query("select * from `users` where `id` IN(?,?)", [1,2]); 43 | // pre($result,1); 44 | 45 | // pre($result->count()); 46 | // foreach ($result as $user) { 47 | // pre($user->name); 48 | // } 49 | 50 | // $result = $agent->query("update `users` set `old` = 30 where `id`=?", [1]); 51 | // pre($agent->rowsAffected()); 52 | // pre($result); 53 | 54 | // $result = $agent->get("select * from `users` where `id` = ?", [1]); 55 | // pre($result); 56 | // $result = $agent->get("select * from `users`"); 57 | // pre($result); 58 | 59 | // $result = $agent->getAll("select * from `users`"); 60 | // pre($result); 61 | // $result = $agent->getAll("select * from `users` where `id` in(?,?)", [1,2]); 62 | // pre($result); 63 | 64 | // $agent->getAll("select * from `users`"); 65 | // prd($agent->rowsCount()); 66 | 67 | // pre($agent); 68 | // pre($db); 69 | -------------------------------------------------------------------------------- /test/simple-oop.php: -------------------------------------------------------------------------------- 1 | 'mysql', 12 | 'database' => [ 13 | 'host' => 'localhost', 'name' => 'test', 14 | 'username' => 'test', 'password' => '********', 15 | 'charset' => 'utf8', 'timezone' => '+00:00', 16 | ] 17 | ]; 18 | private $db; 19 | 20 | private function __clone() {} 21 | private function __construct() {} 22 | 23 | public static function init() { 24 | if (self::$instance == null) { 25 | self::$instance = new self(); 26 | self::$instance->db = 27 | new Database(self::$cfg); 28 | self::$instance->db->connect(); 29 | } 30 | return self::$instance; 31 | } 32 | 33 | public function query($sql, array $params = null) { 34 | return $this->db->getLink()->getAgent()->query($sql, $params); 35 | } 36 | } 37 | 38 | // get database instance 39 | $db = Db::init(); 40 | 41 | // make a regular query 42 | $users = $db->query("select * from `users` limit 3"); 43 | foreach ($users as $user) { 44 | print $user->name; 45 | } 46 | -------------------------------------------------------------------------------- /test/simple-procedural.php: -------------------------------------------------------------------------------- 1 | 'mysql', 8 | 'database' => [ 9 | 'host' => 'localhost', 'name' => 'test', 10 | 'username' => 'test', 'password' => '********', 11 | 'charset' => 'utf8', 'timezone' => '+00:00', 12 | ] 13 | ]; 14 | 15 | function db_init() { 16 | global $cfg; 17 | if (!isset($GLOBALS['$db'])) { 18 | $db = new Database($cfg); 19 | $db->connect(); 20 | // store db 21 | $GLOBALS['$db'] = $db; 22 | } 23 | 24 | return $GLOBALS['$db']; 25 | } 26 | 27 | function db_query($sql, array $params = null) { 28 | // get database instance 29 | $db = db_init(); 30 | return $db->getLink()->getAgent()->query($sql, $params); 31 | } 32 | 33 | // make a regular query 34 | $users = db_query("select * from `users` limit 3"); 35 | foreach ($users as $user) { 36 | print $user->name; 37 | } 38 | -------------------------------------------------------------------------------- /test/test.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 4.0.10deb1 3 | -- http://www.phpmyadmin.net 4 | -- 5 | -- Host: localhost 6 | -- Generation Time: Mar 08, 2015 at 06:02 PM 7 | -- Server version: 5.5.41-0ubuntu0.14.04.1 8 | -- PHP Version: 5.5.9-1ubuntu4.6 9 | 10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 11 | SET time_zone = "+00:00"; 12 | 13 | 14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 17 | /*!40101 SET NAMES utf8 */; 18 | 19 | -- 20 | -- Database: `test` 21 | -- 22 | 23 | -- -------------------------------------------------------- 24 | 25 | -- 26 | -- Table structure for table `users` 27 | -- 28 | 29 | CREATE TABLE IF NOT EXISTS `users` ( 30 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 31 | `name` varchar(20) NOT NULL, 32 | `old` int(11) NOT NULL, 33 | PRIMARY KEY (`id`) 34 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 35 | 36 | -- 37 | -- Dumping data for table `users` 38 | -- 39 | 40 | INSERT INTO `users` (`id`, `name`, `old`) VALUES 41 | (1, 'Kerem', 35), 42 | (2, 'Murat', 40), 43 | (3, 'Ali', 50), 44 | (934, 'Deli', 452), 45 | (935, 'Deli', 419), 46 | (936, 'Deli', 109), 47 | (938, 'Deli', 260), 48 | (939, 'Deli', 364); 49 | 50 | -- -------------------------------------------------------- 51 | 52 | -- 53 | -- Table structure for table `users_login` 54 | -- 55 | 56 | CREATE TABLE IF NOT EXISTS `users_login` ( 57 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 58 | `user_id` int(10) unsigned NOT NULL, 59 | `login` int(10) unsigned NOT NULL, 60 | PRIMARY KEY (`id`) 61 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 62 | 63 | -- 64 | -- Dumping data for table `users_login` 65 | -- 66 | 67 | INSERT INTO `users_login` (`id`, `user_id`, `login`) VALUES 68 | (1, 1, 1421085652), 69 | (2, 2, 1421085652); 70 | 71 | -- -------------------------------------------------------- 72 | 73 | -- 74 | -- Table structure for table `users_score` 75 | -- 76 | 77 | CREATE TABLE IF NOT EXISTS `users_score` ( 78 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 79 | `user_id` int(11) NOT NULL, 80 | `score` int(11) NOT NULL, 81 | PRIMARY KEY (`id`), 82 | KEY `user_id` (`user_id`) 83 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 84 | 85 | -- 86 | -- Dumping data for table `users_score` 87 | -- 88 | 89 | INSERT INTO `users_score` (`id`, `user_id`, `score`) VALUES 90 | (1, 1, 10), 91 | (2, 1, 10), 92 | (3, 1, 10), 93 | (4, 2, 20), 94 | (5, 2, 20), 95 | (6, 2, 20), 96 | (7, 3, 30), 97 | (8, 3, 30), 98 | (9, 3, 30); 99 | 100 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 101 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 102 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 103 | --------------------------------------------------------------------------------