diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eea50c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/vendor/ +composer.lock +.vscode +.idea \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0797e11 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Kyutefox + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2227a4b --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +## :rocket: KyutePDO: Lightweight and Customizable PHP Library + +KyutePDO is a powerful and easy-to-use PHP library that allows developers to work with relational databases seamlessly. It is built on top of PDO, a PHP extension that provides a consistent interface for accessing different types of databases. + +### Features + +- Lightweight and easy to use +- Customizable and highly configurable +- Supports multiple databases (MySQL, PostgreSQL, SQLite, and Oracle) +- Provides a simple and intuitive API for executing queries and managing transactions +- Offers a caching system to improve performance +- Supports prepared statements to prevent SQL injection attacks + +### :page_with_curl: Documentation + +KyutePDO comes with comprehensive documentation that provides an in-depth overview of its features, configuration options, and usage examples. The documentation is available online, making it easy for developers to access and use it. + +[KyutePDO Full Docs](https://kyutefox.com/product/kyutepdo) + +### :star2: Contribution +If this project has helped you make sure to leave a start as a part of your contribution + +### :purple_heart: Maintainer +- [GitHub](https://github.com/razoo-choudhary) +- [Twitter](https://twitter.com/razoo_choudhary) +- [Linktree](https://linktr.ee/razoo_choudhary) \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0d9def7 --- /dev/null +++ b/composer.json @@ -0,0 +1,38 @@ +{ + "name": "kyutefox/kyutepdo", + "description": "A PDO-based PHP library for easy and efficient database management", + "type": "library", + "homepage": "https://kyutefox.com/product/kyutepdo", + "version": "1.0.0", + "keywords": [ + "pdo", + "database", + "php", + "library" + ], + "license": "MIT", + "authors": [ + { + "name": "Raju choudhary", + "homepage": "https://github.com/razoo-choudhary" + } + ], + "require": { + "php": "^7.4 || ^8.0", + "ext-pdo": "*", + "ext-json": "*" + }, + "autoload": { + "psr-4": { + "Kyutefox\\KyutePDO\\": "src/" + } + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/src/Config/Connection.php b/src/Config/Connection.php new file mode 100644 index 0000000..74d3c71 --- /dev/null +++ b/src/Config/Connection.php @@ -0,0 +1,62 @@ +driver = !empty($config["db_driver"]) ? $config["db_driver"] : 'mysql'; + $this->host = !empty($config["db_host"]) ? $config["db_host"] : 'localhost'; + $this->charset = !empty($config["db_charset"]) ? $config["db_charset"] : 'utf8mb4'; + $this->collation = !empty($config["db_collation"]) ? $config["db_collation"] : 'utf8mb4_unicode_ci'; + $this->port = !empty($config["db_port"]) ? (strstr($config["db_port"], ':') ? explode(':', $config["db_port"])[1] : "") : '3306'; + $this->user = !empty($config["db_user"]) ? $config["db_user"] : 'root'; + $this->pass = !empty($config["db_pass"]) ? $config["db_pass"] : ''; + $this->name = !empty($config["db_name"]) ? $config["db_name"] : ''; + $this->config = $config["db_config"] ?? []; + } + + public function init(): ?PDO + { + if (in_array($this->driver, ['mysql', 'pgsql'])) + { + $this->dsn = $this->driver . ':host=' . str_replace(':' . $this->port, '', $this->host) . ';port=' . $this->port . ';dbname=' . $this->name; + } + + if ($this->driver == 'sqlite') $this->dsn = $this->driver . ':' . $this->name; + if ($this->driver == "oracle") $this->dsn = 'oci:dbname=' . $this->name . '/' . $this->host; + + return $this->connect($this->dsn); + } + + private function connect(string $dataSource) + { + try + { + $pdo = new \PDO($dataSource, $this->user, $this->pass, $this->config); + $pdo->exec("SET NAMES '" . $this->charset . "' COLLATE '" . $this->collation . "'"); + $pdo->exec("SET CHARACTER SET '" . $this->charset . "'"); + $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); + } + catch(\PDOException $e) + { + die("Specified database connection couldn't be started with PDO. " . $e->getMessage()); + } + + return $pdo; + } +} diff --git a/src/Interfaces/KyutePDOInterface.php b/src/Interfaces/KyutePDOInterface.php new file mode 100644 index 0000000..21778bc --- /dev/null +++ b/src/Interfaces/KyutePDOInterface.php @@ -0,0 +1,45 @@ +', '<=', '>=', '<>']; + + protected $queryCount = 0; + protected $transactionCount = 0; + protected $debug = true; + protected $cache = null; + protected $cacheDir = null; + + /** + * @param array $config + */ + public function __construct(array $config) + { + $connection = new Connection($config); + $this->prefix = !empty($config["db_prefix"]) ? $config["db_prefix"] : ""; + $this->debug = !empty($config['debug']) ? $config['debug'] : true; + $this->cacheDir = !empty($config['cache_dir']) ? $config['cache_dir'] : __DIR__ . '/cache/'; + $this->pdo = $connection->Init(); + return $this->pdo; + } + + /** + * @param $table + * @return $this + */ + + public function table($table): KyutePDO + { + + if (is_array($table)) { + $from = ''; + foreach ($table as $key) { + $from .= $this->prefix . $key . ', '; + } + $this->from = rtrim($from, ', '); + } else { + if (strpos($table, ', ') > 0) { + $table = explode(',', $table); + foreach ($table as $key => $value) { + $value = $this->prefix . ltrim($value); + } + $this->from = implode(', ', $table); + } else { + $this->from = $this->prefix . $table; + } + } + return $this; + } + + /** + * @param $fields + * @return $this + */ + + public function select($fields): KyutePDO + { + $select = is_array($fields) ? implode(', ', $fields) : $fields; + $this->optimizeSelect($select); + return $this; + } + + /** + * @param $field + * @param null $name + * @return $this + */ + + public function max($field, $name = null): KyutePDO + { + $column = 'MAX(' . $field . ')' . (!is_null($name) ? ' AS ' . $name : ''); + $this->optimizeSelect($column); + return $this; + } + + /** + * @param $field + * @param null $name + * @return $this + */ + + public function min($field, $name = null): KyutePDO + { + $column = 'MIN(' . $field . ')' . (!is_null($name) ? ' AS ' . $name : ''); + $this->optimizeSelect($column); + return $this; + } + + /** + * @param $field + * @param null $name + * @return $this + */ + + public function sum($field, $name = null): KyutePDO + { + $column = 'SUM(' . $field . ')' . (!is_null($name) ? ' AS ' . $name : ''); + $this->optimizeSelect($column); + return $this; + } + + /** + * @param $field + * @param null $name + * @return $this + */ + + public function count($field, $name = null): KyutePDO + { + $column = 'COUNT(' . $field . ')' . (!is_null($name) ? ' AS ' . $name : ''); + $this->optimizeSelect($column); + return $this; + } + + /** + * @param $field + * @param null $name + * @return $this + */ + + public function avg($field, $name = null): KyutePDO + { + $column = 'AVG(' . $field . ')' . (!is_null($name) ? ' AS ' . $name : ''); + $this->optimizeSelect($column); + return $this; + } + + /** + * @param $table + * @param null $field1 + * @param null $operator + * @param null $field2 + * @param string $type + * @return $this + */ + + public function join($table, $field1 = null, $operator = null, $field2 = null, $type = ''): KyutePDO + { + $on = $field1; + $table = $this->prefix . $table; + + if (!is_null($operator)) { + $on = !in_array($operator, $this->operators) + ? $field1 . ' = ' . $operator . (!is_null($field2) ? ' ' . $field2 : '') + : $field1 . ' ' . $operator . ' ' . $field2; + } + + $this->join = (is_null($this->join)) + ? ' ' . $type . 'JOIN' . ' ' . $table . ' ON ' . $on + : $this->join . ' ' . $type . 'JOIN' . ' ' . $table . ' ON ' . $on; + + return $this; + } + + /** + * @param $table + * @param $field1 + * @param string $operator + * @param string $field2 + * @return $this + */ + + public function innerJoin($table, $field1, $operator = '', $field2 = ''): KyutePDO + { + return $this->join($table, $field1, $operator, $field2, 'INNER '); + } + + /** + * @param $table + * @param $field1 + * @param string $operator + * @param string $field2 + * @return $this + */ + + public function leftJoin($table, $field1, $operator = '', $field2 = ''): KyutePDO + { + return $this->join($table, $field1, $operator, $field2, 'LEFT '); + } + + /** + * @param $table + * @param $field1 + * @param string $operator + * @param string $field2 + * @return $this + */ + + public function rightJoin($table, $field1, $operator = '', $field2 = ''): KyutePDO + { + return $this->join($table, $field1, $operator, $field2, 'RIGHT '); + } + + /** + * @param $table + * @param $field1 + * @param string $operator + * @param string $field2 + * @return $this + */ + + public function fullOuterJoin($table, $field1, $operator = '', $field2 = ''): KyutePDO + { + return $this->join($table, $field1, $operator, $field2, 'FULL OUTER '); + } + + /** + * @param $table + * @param $field1 + * @param string $operator + * @param string $field2 + * @return $this + */ + + public function leftOuterJoin($table, $field1, $operator = '', $field2 = ''): KyutePDO + { + return $this->join($table, $field1, $operator, $field2, 'LEFT OUTER '); + } + + /** + * @param $table + * @param $field1 + * @param string $operator + * @param string $field2 + * @return $this + */ + + public function rightOuterJoin($table, $field1, $operator = '', $field2 = ''): KyutePDO + { + return $this->join($table, $field1, $operator, $field2, 'RIGHT OUTER '); + } + + /** + * @param $where + * @param null $operator + * @param null $val + * @param string $type + * @param string $andOr + * @return $this + */ + + public function where($where, $operator = null, $val = null, $type = '', $andOr = 'AND'): KyutePDO + { + if (is_array($where) && !empty($where)) { + $_where = []; + foreach ($where as $column => $data) { + $_where[] = $type . $column . '=' . $this->escape($data); + } + $where = implode(' ' . $andOr . ' ', $_where); + } else { + if (is_null($where) || empty($where)) { + return $this; + } + + if (is_array($operator)) { + $params = explode('?', $where); + $_where = ''; + foreach ($params as $key => $value) { + if (!empty($value)) { + $_where .= $type . $value . (isset($operator[$key]) ? $this->escape($operator[$key]) : ''); + } + } + $where = $_where; + } elseif (!in_array($operator, $this->operators) || $operator == false) { + $where = $type . $where . ' = ' . $this->escape($operator); + } else { + $where = $type . $where . ' ' . $operator . ' ' . $this->escape($val); + } + } + + if ($this->grouped) { + $where = '(' . $where; + $this->grouped = false; + } + + $this->where = is_null($this->where) + ? $where + : $this->where . ' ' . $andOr . ' ' . $where; + + return $this; + } + + /** + * @param $where + * @param null $operator + * @param null $val + * @return $this + */ + + public function orWhere($where, $operator = null, $val = null): KyutePDO + { + return $this->where($where, $operator, $val, '', 'OR'); + } + + /** + * @param $where + * @param null $operator + * @param null $val + * @return $this + */ + + public function notWhere($where, $operator = null, $val = null): KyutePDO + { + return $this->where($where, $operator, $val, 'NOT ', 'AND'); + } + + /** + * @param $where + * @param null $operator + * @param null $val + * @return $this + */ + + public function orNotWhere($where, $operator = null, $val = null): KyutePDO + { + return $this->where($where, $operator, $val, 'NOT ', 'OR'); + } + + /** + * @param $where + * @param false $not + * @return $this + */ + + public function whereNull($where, $not = false): KyutePDO + { + $where = $where . ' IS ' . ($not ? 'NOT' : '') . ' NULL'; + $this->where = is_null($this->where) ? $where : $this->where . ' ' . 'AND ' . $where; + + return $this; + } + + /** + * @param $where + * @return $this + */ + + public function whereNotNull($where): KyutePDO + { + return $this->whereNull($where, true); + } + + /** + * @param Closure $obj + * @return $this + */ + + public function grouped(Closure $obj): KyutePDO + { + $this->grouped = true; + call_user_func_array($obj, [$this]); + $this->where .= ')'; + + return $this; + } + + /** + * @param $field + * @param array $keys + * @param string $type + * @param string $andOr + * @return $this + */ + + public function in($field, array $keys, $type = '', $andOr = 'AND'): KyutePDO + { + if (is_array($keys)) { + $_keys = []; + foreach ($keys as $k => $v) { + $_keys[] = is_numeric($v) ? $v : $this->escape($v); + } + $where = $field . ' ' . $type . 'IN (' . implode(', ', $_keys) . ')'; + + if ($this->grouped) { + $where = '(' . $where; + $this->grouped = false; + } + + $this->where = is_null($this->where) + ? $where + : $this->where . ' ' . $andOr . ' ' . $where; + } + + return $this; + } + + /** + * @param $field + * @param array $keys + * @return $this + */ + + public function notIn($field, array $keys): KyutePDO + { + return $this->in($field, $keys, 'NOT ', 'AND'); + } + + /** + * @param $field + * @param array $keys + * @return $this + */ + + public function orIn($field, array $keys): KyutePDO + { + return $this->in($field, $keys, '', 'OR'); + } + + /** + * @param $field + * @param array $keys + * @return $this + */ + + public function orNotIn($field, array $keys): KyutePDO + { + return $this->in($field, $keys, 'NOT ', 'OR'); + } + + /** + * @param $field + * @param $value1 + * @param $value2 + * @param string $type + * @param string $andOr + * @return $this + */ + + public function between($field, $value1, $value2, $type = '', $andOr = 'AND'): KyutePDO + { + $where = '(' . $field . ' ' . $type . 'BETWEEN ' . ($this->escape($value1) . ' AND ' . $this->escape($value2)) . ')'; + if ($this->grouped) { + $where = '(' . $where; + $this->grouped = false; + } + + $this->where = is_null($this->where) + ? $where + : $this->where . ' ' . $andOr . ' ' . $where; + + return $this; + } + + /** + * @param $field + * @param $value1 + * @param $value2 + * @return $this + */ + + public function notBetween($field, $value1, $value2): KyutePDO + { + return $this->between($field, $value1, $value2, 'NOT ', 'AND'); + } + + /** + * @param $field + * @param $value1 + * @param $value2 + * @return $this + */ + + public function orBetween($field, $value1, $value2): KyutePDO + { + return $this->between($field, $value1, $value2, '', 'OR'); + } + + /** + * @param $field + * @param $value1 + * @param $value2 + * @return $this + */ + + public function orNotBetween($field, $value1, $value2): KyutePDO + { + return $this->between($field, $value1, $value2, 'NOT ', 'OR'); + } + + /** + * @param $field + * @param $data + * @param string $type + * @param string $andOr + * @return $this + */ + + public function like($field, $data, $type = '', $andOr = 'AND'): KyutePDO + { + $like = $this->escape($data); + $where = $field . ' ' . $type . 'LIKE ' . $like; + + if ($this->grouped) { + $where = '(' . $where; + $this->grouped = false; + } + + $this->where = is_null($this->where) + ? $where + : $this->where . ' ' . $andOr . ' ' . $where; + + return $this; + } + + /** + * @param $field + * @param $data + * @return $this + */ + + public function orLike($field, $data): KyutePDO + { + return $this->like($field, $data, '', 'OR'); + } + + /** + * @param $field + * @param $data + * @return $this + */ + + public function notLike($field, $data): KyutePDO + { + return $this->like($field, $data, 'NOT ', 'AND'); + } + + /** + * @param $field + * @param $data + * @return $this + */ + + public function orNotLike($field, $data): KyutePDO + { + return $this->like($field, $data, 'NOT ', 'OR'); + } + + /** + * @param $limit + * @param null $limitEnd + * @return $this + */ + + public function limit($limit, $limitEnd = null): KyutePDO + { + $this->limit = !is_null($limitEnd) + ? $limit . ', ' . $limitEnd + : $limit; + + return $this; + } + + /** + * @param $offset + * @return $this + */ + + public function offset($offset): KyutePDO + { + $this->offset = $offset; + + return $this; + } + + /** + * @param $perPage + * @param $page + * @return $this + */ + + public function pagination($perPage, $page): KyutePDO + { + $this->limit = $perPage; + $this->offset = (($page > 0 ? $page : 1) - 1) * $perPage; + + return $this; + } + + /** + * @param $orderBy + * @param null $orderDir + * @return $this + */ + + public function orderBy($orderBy, $orderDir = null): KyutePDO + { + if (!is_null($orderDir)) { + $this->orderBy = $orderBy . ' ' . strtoupper($orderDir); + } else { + $this->orderBy = stristr($orderBy, ' ') || strtolower($orderBy) === 'rand()' + ? $orderBy + : $orderBy . ' ASC'; + } + + return $this; + } + + /** + * @param $groupBy + * @return $this + */ + + public function groupBy($groupBy): KyutePDO + { + $this->groupBy = is_array($groupBy) ? implode(', ', $groupBy) : $groupBy; + + return $this; + } + + /** + * @param $field + * @param null $operator + * @param null $val + * @return $this + */ + + public function having($field, $operator = null, $val = null): KyutePDO + { + if (is_array($operator)) { + $fields = explode('?', $field); + $where = ''; + foreach ($fields as $key => $value) { + if (!empty($value)) { + $where .= $value . (isset($operator[$key]) ? $this->escape($operator[$key]) : ''); + } + } + $this->having = $where; + } elseif (!in_array($operator, $this->operators)) { + $this->having = $field . ' > ' . $this->escape($operator); + } else { + $this->having = $field . ' ' . $operator . ' ' . $this->escape($val); + } + + return $this; + } + + /** + * @return int + */ + + public function numRows(): int + { + return $this->numRows; + } + + /** + * @return null + */ + + public function insertId() + { + return $this->insertId; + } + + /** + * + */ + + public function error() + { + if ($this->debug === true) { + if (php_sapi_name() === 'cli') { + die("Query: " . $this->query . PHP_EOL . "Error: " . $this->error . PHP_EOL); + } + + $msg = '