ajout sqlite
This commit is contained in:
		
							parent
							
								
									3550730a9f
								
							
						
					
					
						commit
						3bd1031dda
					
				
							
								
								
									
										178
									
								
								src/db/sqlite/Sqlite.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								src/db/sqlite/Sqlite.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,178 @@ | ||||
| <?php | ||||
| namespace nur\sery\db\sqlite; | ||||
| 
 | ||||
| use nur\b\IllegalAccessException; | ||||
| use nur\sery\cl; | ||||
| use SQLite3; | ||||
| use SQLite3Result; | ||||
| use SQLite3Stmt; | ||||
| 
 | ||||
| /** | ||||
|  * Class Sqlite: frontend vers une base de données sqlite3 | ||||
|  */ | ||||
| class Sqlite { | ||||
|   static function config_enableExceptions(self $sqlite) { | ||||
|     $sqlite->db->enableExceptions(true); | ||||
|   } | ||||
| 
 | ||||
|   static function verifix_query(&$query, ?array &$params=null): void { | ||||
|     if (is_array($query)) { | ||||
|       throw IllegalAccessException::not_implemented(); #XXX
 | ||||
|     } elseif (!is_string($query)) { | ||||
|       $query = strval($query); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   const CONFIG = [ | ||||
|     [self::class, "config_enableExceptions"], | ||||
|   ]; | ||||
|   const MIGRATE = null; | ||||
| 
 | ||||
|   const SCHEMA = [ | ||||
|     "file" => ["string", ""], | ||||
|     "flags" => ["int", SQLITE3_OPEN_READWRITE + SQLITE3_OPEN_CREATE], | ||||
|     "encryption_key" => ["string", ""], | ||||
|     "config" => ["?array|callable"], | ||||
|     "migrate" => ["?array|string|callable"], | ||||
|     "auto_open" => ["bool", true], | ||||
|   ]; | ||||
| 
 | ||||
|   function __construct(?string $file=null, ?array $params=null) { | ||||
|     if ($file !== null) $params["file"] = $file; | ||||
|     ##schéma
 | ||||
|     $default_file = self::SCHEMA["file"][1]; | ||||
|     $this->file = strval($params["file"] ?? $default_file); | ||||
|     #
 | ||||
|     $default_flags = self::SCHEMA["flags"][1]; | ||||
|     $this->flags = intval($params["flags"] ?? $default_flags); | ||||
|     #
 | ||||
|     $default_encryptionKey = self::SCHEMA["encryption_key"][1]; | ||||
|     $this->encryptionKey = strval($params["encryption_key"] ?? $default_encryptionKey); | ||||
|     # configuration
 | ||||
|     $this->config = $params["config"] ?? static::CONFIG; | ||||
|     # migrations
 | ||||
|     $this->migration = $params["migrate"] ?? static::MIGRATE; | ||||
|     #
 | ||||
|     $default_autoOpen = self::SCHEMA["auto_open"][1]; | ||||
|     if ($params["auto_open"] ?? $default_autoOpen) { | ||||
|       $this->open(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** @var string */ | ||||
|   protected $file; | ||||
|   /** @var int */ | ||||
|   protected $flags; | ||||
|   /** @var string */ | ||||
|   protected $encryptionKey; | ||||
|   /** @var array|string|callable */ | ||||
|   protected $config; | ||||
|   /** @var array|string|callable */ | ||||
|   protected $migration; | ||||
| 
 | ||||
|   /** @var SQLite3 */ | ||||
|   protected $db; | ||||
| 
 | ||||
|   function open(): self { | ||||
|     if ($this->db === null) { | ||||
|       $this->db = new SQLite3($this->file, $this->flags, $this->encryptionKey); | ||||
|       SqliteConfig::with($this->config)->run($this); | ||||
|       SqliteMigration::with($this->migration)->run($this); | ||||
|     } | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   function close(): void { | ||||
|     if ($this->db !== null) { | ||||
|       $this->db->close(); | ||||
|       $this->db = null; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function exec($query, ?array $params=null): bool { | ||||
|     $this->open(); | ||||
|     self::verifix_query($query, $params); | ||||
|     $db = $this->db; | ||||
|     if ($params === null) { | ||||
|       return $db->exec($query); | ||||
|     } else { | ||||
|       /** @var SQLite3Stmt $stmt */ | ||||
|       $stmt = SqliteException::check($db, $db->prepare($query)); | ||||
|       try { | ||||
|         foreach ($params as $param => $value) { | ||||
|           SqliteException::check($db, $stmt->bindValue($param, $value)); | ||||
|         } | ||||
|         /** @var SQLite3Result $result */ | ||||
|         $result = SqliteException::check($db, $stmt->execute()); | ||||
|         return $result->finalize(); | ||||
|       } finally { | ||||
|         $stmt->close(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function get($query, ?array $params=null, bool $entireRow=false) { | ||||
|     $this->open(); | ||||
|     self::verifix_query($query, $params); | ||||
|     $db = $this->db; | ||||
|     if ($params === null) { | ||||
|       return $db->querySingle($query, $entireRow); | ||||
|     } else { | ||||
|       /** @var SQLite3Stmt $stmt */ | ||||
|       $stmt = SqliteException::check($db, $db->prepare($query)); | ||||
|       try { | ||||
|         foreach ($params as $param => $value) { | ||||
|           SqliteException::check($db, $stmt->bindValue($param, $value)); | ||||
|         } | ||||
|         /** @var SQLite3Result $result */ | ||||
|         $result = SqliteException::check($db, $stmt->execute()); | ||||
|         try { | ||||
|           $row = $result->fetchArray(SQLITE3_ASSOC); | ||||
|           if ($row === false) return null; | ||||
|           elseif ($entireRow) return $row; | ||||
|           else return cl::first($row); | ||||
|         } finally { | ||||
|           $result->finalize(); | ||||
|         } | ||||
|       } finally { | ||||
|         $stmt->close(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function all($query, ?array $params=null): iterable { | ||||
|     $this->open(); | ||||
|     self::verifix_query($query, $params); | ||||
|     $db = $this->db; | ||||
|     if ($params === null) { | ||||
|       /** @var SQLite3Result $result */ | ||||
|       $result = SqliteException::check($db, $db->query($query)); | ||||
|       try { | ||||
|         while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) { | ||||
|           yield $row; | ||||
|         } | ||||
|       } finally { | ||||
|         $result->finalize(); | ||||
|       } | ||||
|     } else { | ||||
|       /** @var SQLite3Stmt $stmt */ | ||||
|       $stmt = SqliteException::check($db, $db->prepare($query)); | ||||
|       try { | ||||
|         foreach ($params as $param => $value) { | ||||
|           SqliteException::check($db, $stmt->bindValue($param, $value)); | ||||
|         } | ||||
|         /** @var SQLite3Result $result */ | ||||
|         $result = SqliteException::check($db, $stmt->execute()); | ||||
|         try { | ||||
|           while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) { | ||||
|             yield $row; | ||||
|           } | ||||
|         } finally { | ||||
|           $result->finalize(); | ||||
|         } | ||||
|       } finally { | ||||
|         $stmt->close(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										35
									
								
								src/db/sqlite/SqliteConfig.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/db/sqlite/SqliteConfig.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| <?php | ||||
| namespace nur\sery\db\sqlite; | ||||
| 
 | ||||
| use nur\sery\php\func; | ||||
| 
 | ||||
| class SqliteConfig { | ||||
|   static function with($configs): SqliteConfig { | ||||
|     if ($configs instanceof self) return $configs; | ||||
|     return new static($configs); | ||||
|   } | ||||
| 
 | ||||
|   const CONFIG = null; | ||||
| 
 | ||||
|   function __construct($configs) { | ||||
|     if ($configs === null) $configs = static::CONFIG; | ||||
|     if ($configs === null) $configs = []; | ||||
|     elseif (is_string($configs)) $configs = [$configs]; | ||||
|     elseif (is_callable($configs)) $configs = [$configs]; | ||||
|     elseif (!is_array($configs)) $configs = [strval($configs)]; | ||||
|     $this->configs = $configs; | ||||
|   } | ||||
| 
 | ||||
|   /** @var array */ | ||||
|   protected $configs; | ||||
| 
 | ||||
|   function run(Sqlite $sqlite): void { | ||||
|     foreach ($this->configs as $key => $config) { | ||||
|       if (is_callable($config)) { | ||||
|         func::call($config, $sqlite, $key, $this); | ||||
|       } else { | ||||
|         $sqlite->exec($config); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										13
									
								
								src/db/sqlite/SqliteException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/db/sqlite/SqliteException.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| <?php | ||||
| namespace nur\sery\db\sqlite; | ||||
| 
 | ||||
| use RuntimeException; | ||||
| use SQLite3; | ||||
| 
 | ||||
| class SqliteException extends RuntimeException { | ||||
|   static final function check(SQLite3 $db, $value, bool $throw=true) { | ||||
|     if ($value !== false) return $value; | ||||
|     elseif (!$throw) return null; | ||||
|     else throw new static($db->lastErrorMsg(), $db->lastErrorCode()); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										49
									
								
								src/db/sqlite/SqliteMigration.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/db/sqlite/SqliteMigration.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| <?php | ||||
| namespace nur\sery\db\sqlite; | ||||
| 
 | ||||
| use nur\sery\php\func; | ||||
| 
 | ||||
| class SqliteMigration { | ||||
|   static function with($migrations): SqliteMigration { | ||||
|     if ($migrations instanceof self) return $migrations; | ||||
|     return new static($migrations); | ||||
|   } | ||||
| 
 | ||||
|   const MIGRATE = null; | ||||
| 
 | ||||
|   function __construct($migrations) { | ||||
|     if ($migrations === null) $migrations = static::MIGRATE; | ||||
|     if ($migrations === null) $migrations = []; | ||||
|     elseif (is_string($migrations)) $migrations = [$migrations]; | ||||
|     elseif (is_callable($migrations)) $migrations = [$migrations]; | ||||
|     elseif (!is_array($migrations)) $migrations = [strval($migrations)]; | ||||
|     $this->migrations = $migrations; | ||||
|   } | ||||
| 
 | ||||
|   /** @var callable[]|string[]  */ | ||||
|   protected $migrations; | ||||
|    | ||||
|   function run(Sqlite $sqlite): void { | ||||
|     $sqlite->exec("create table if not exists _migration(key varchar primary key, value varchar not null, done integer default 0)"); | ||||
|     foreach ($this->migrations as $key => $migration) { | ||||
|       $exists = $sqlite->get("select 1 from _migration where key = :key and done = 1", [ | ||||
|         "key" => $key, | ||||
|       ]); | ||||
|       if (!$exists) { | ||||
|         $sqlite->exec("insert or replace into _migration(key, value, done) values(:key, :value, :done)", [ | ||||
|           "key" => $key, | ||||
|           "value" => $migration, | ||||
|           "done" => 0, | ||||
|         ]); | ||||
|         if (is_callable($migration)) { | ||||
|           func::call($migration, $sqlite, $key, $this); | ||||
|         } else { | ||||
|           $sqlite->exec($migration); | ||||
|         } | ||||
|         $sqlite->exec("update _migration set done = 1 where key = :key", [ | ||||
|           "key" => $key, | ||||
|         ]); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										36
									
								
								tests/db/sqlite/SqliteTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								tests/db/sqlite/SqliteTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| <?php | ||||
| namespace nur\sery\db\sqlite; | ||||
| 
 | ||||
| use PHPUnit\Framework\TestCase; | ||||
| 
 | ||||
| class SqliteTest extends TestCase { | ||||
|   const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)"; | ||||
|   const INSERT_JEPHTE = "insert into person(nom, prenom, age) values ('clain', 'jephte', 50)"; | ||||
|   const INSERT_JEAN = "insert into person(nom, prenom, age) values ('payet', 'jean', 32)"; | ||||
| 
 | ||||
|   function testMigration() { | ||||
|     $sqlite = new Sqlite(":memory:", [ | ||||
|       "migrate" => [ | ||||
|         self::CREATE_PERSON, | ||||
|         self::INSERT_JEPHTE, | ||||
|       ], | ||||
|     ]); | ||||
|     self::assertSame("clain", $sqlite->get("select nom, age from person")); | ||||
|     self::assertSame([ | ||||
|       "nom" => "clain", | ||||
|       "age" => 50, | ||||
|     ], $sqlite->get("select nom, age from person", null, true)); | ||||
| 
 | ||||
|     $sqlite->exec(self::INSERT_JEAN); | ||||
|     self::assertSame("payet", $sqlite->get("select nom, age from person where nom = 'payet'")); | ||||
|     self::assertSame([ | ||||
|       "nom" => "payet", | ||||
|       "age" => 32, | ||||
|     ], $sqlite->get("select nom, age from person where nom = 'payet'", null, true)); | ||||
| 
 | ||||
|     self::assertSame([ | ||||
|       ["key" => "0", "value" => self::CREATE_PERSON, "done" => 1], | ||||
|       ["key" => "1", "value" => self::INSERT_JEPHTE, "done" => 1], | ||||
|     ], iterator_to_array($sqlite->all("select key, value, done from _migration"))); | ||||
|   } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user