{"id":13792214,"url":"https://github.com/quasilyte/KSQLite","last_synced_at":"2025-05-12T14:31:43.130Z","repository":{"id":43261000,"uuid":"460822358","full_name":"quasilyte/KSQLite","owner":"quasilyte","description":"KSQLite is a FFI-based SQLite library that can be used in both PHP and KPHP","archived":false,"fork":false,"pushed_at":"2022-12-23T12:10:59.000Z","size":86,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-03T01:51:10.536Z","etag":null,"topics":["composer","composer-package","databases","ffi","kphp","kphp-ffi","library","php","sqlite","sqlite3"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/quasilyte.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-02-18T11:15:38.000Z","updated_at":"2025-03-22T07:37:35.000Z","dependencies_parsed_at":"2023-01-30T18:30:27.874Z","dependency_job_id":null,"html_url":"https://github.com/quasilyte/KSQLite","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quasilyte%2FKSQLite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quasilyte%2FKSQLite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quasilyte%2FKSQLite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quasilyte%2FKSQLite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quasilyte","download_url":"https://codeload.github.com/quasilyte/KSQLite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253754967,"owners_count":21958934,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["composer","composer-package","databases","ffi","kphp","kphp-ffi","library","php","sqlite","sqlite3"],"created_at":"2024-08-03T22:01:09.624Z","updated_at":"2025-05-12T14:31:42.811Z","avatar_url":"https://github.com/quasilyte.png","language":"PHP","funding_links":[],"categories":["Table of Contents"],"sub_categories":["FFI libraries"],"readme":"# KSQLite\n\n![Build Status](https://github.com/quasilyte/ksqlite/workflows/PHP/badge.svg)\n\n![](docs/logo.png)\n\nKSQLite is a FFI-based SQLite library that can be used in both PHP and KPHP.\n\n## Installation\n\nSince this is a FFI library, it needs a dynamic library available during the run time.\n\nInstallation steps:\n\n1. Install libsqlite3 in your system (if you don't have it already)\n2. Locate the library file and place in under `./ffilibs/libsqlite3`\n3. Install this composer package to use KSQLite classes inside your code\n\nDepending on your system, you need to find `libsqlite3.so`, `libsqlite3.dylib`\nor `libsqlite3.dll` file. Then you can copy it to the application root `ffilibs` folder\nunder the `libsqlite3` name (note: no extension suffixes).\n\nIf you're having difficulties locating the library file, use a helper script:\n\n```bash\n$ php -f locate_lib.php\nlibrary candidate: /lib/x86_64-linux/libsqlite3.so.0\nlibrary candidate: /lib/x86_64-linux/libsqlite3.so\n\nrun something like this to make it discoverable (unix):\n\tmkdir -p ffilibs \u0026\u0026 ln -s /lib/x86_64-linux/libsqlite3.so ./ffilibs/libsqlite3\n```\n\nThen install the composer library itself:\n\n```bash\n$ composer require quasilyte/ksqlite\n```\n\nNotes:\n\n* If you want to place library files/links globally, make `./ffilibs` a symlink\n* You'll probably want to add `ffilibs/` to your gitignore\n\n## Examples\n\n* [quick_start.php](examples/quick_start.php) - a simple overview of the API basics\n* [prepared_statements.php](examples/prepared_statements.php) - how to re-use a single statement for multiple queries\n* [transactions.php](examples/transactions.php) - how to use transactions plus some best practices\n* [simple_site.php](examples/simple_site.php) - serving a simple TODO app using SQLite as a database\n\nRunning examples with PHP:\n\n```bash\n$ php -d opcache.enable_cli=1\\\n      -d opcache.preload=preload.php\\\n      -f ./examples/transactions.php\n```\n\nRunning examples with KPHP:\n\n```bash\n# Step 1: compile the example:\n$ kphp --mode cli --composer-root $(pwd) ./examples/transactions.php\n# Step 2: run the binary:\n$ ./kphp_out/cli\n```\n\n## API reference\n\nAll functions report error with `false` return value (operation status).\n\nWhen there is more than one result to be returned, a tuple like `tuple(T, bool)` is returned, where second tuple element is an operation status.\n\nIf operation status is `false`, use `KSQLite::getLastError()` to get the actual error message.\n\nNote that you only need to care about closing the opened database object. There are no other\nresources you need to finalize. The API is designed in a way that you don't get any FFI-allocated\nobject, so the library can manage these resources for you.\n\n* exec methods run the query while discarding their results\n* fetch methods collect and return results\n* query method is a low-level result set iteration privimite; fetch methods are built on that\n\n### exec\n\n```php\nfunction exec(string $sql, array $params = []): bool\n```\n\n* `$sql` SQL query string with optional bind var placeholders\n* `$params` bind variables for the query\n\nWhen to use: need to execute a query once, but don't need the results.\n\n```php\n// Simple case: not bind params.\n$query = 'CREATE TABLE IF NOT EXISTS languages(\n  lang_id INTEGER PRIMARY KEY,\n  lang_name TEXT NOT NULL\n);'\nif (!$db-\u003eexec($query)) {\n  handle_error($db-\u003egetLastError());\n}\n\n// Exec with named params.\n// Note: a var prefix (':', '@' or '$') should be consistent\n// between the query and bind params array.\n$query = 'INSERT INTO languages(lang_name) VALUES(:lang_name)';\n$params = [':lang_name' =\u003e 'KPHP'];\nif (!$db-\u003eexec($query, $params)) {\n  handle_error($db-\u003egetLastError());\n}\n\n// Exec with positional params.\n// Note: bind var numbers start from 1.\n$query = 'DELETE FROM languages WHERE lang_name = ?1 OR lang_name = ?2';\n$params = [1 =\u003e 'COBOL', 2 =\u003e 'Visual COBOL'];\nif (!$db-\u003eexec($query, $params)) {\n  handle_error($db-\u003egetLastError());\n}\n```\n\n### execPrepared\n\n```php\nfunction execPrepared(string $sql, $bind_params_func): bool\n```\n\n* `$sql` SQL query string with optional bind var placeholders\n* `$bind_params_func` a callback that binds variables for the query\n\nWhen to use: running a single SQL statement with different params, don't need the results.\n\n```php\n// Execute several inserts with different bind var sets.\n$values = [10, 20, 30, 40];\n$query = 'INSERT INTO fav_numbers(num_value) VALUES(?1)';\n$ok = $db-\u003eexecPrepared($query, function(KSQLiteParamsBinder $b) use ($values) {\n  if ($b-\u003equery_index \u003e= count($values)) {\n    return false; // No more rows to insert, stop now\n  }\n  // Bind ?1 to the specified value.\n  // Use string keys, like ':num_value', to bind named params.\n  $b-\u003ebind(1, $values[$b-\u003equery_index]);\n  return true; // Parameters bound, execute the query\n});\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\n\n// Execute 10 inserts without bind vars.\n$query = \"INSERT INTO fav_events(event_time) VALUES(time('now'))\";\n$ok = $db-\u003eexecPrepared($query, function(KSQLiteParamsBinder $b) {\n  return $b-\u003equery_index \u003c 10;\n});\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\n\n// Prepared statement API allows you to perform N queries\n// using the same statement even if you don't know the exact\n// N in advance.\n$query = \"INSERT INTO important_data(x, y) VALUES(:x, :y)\";\n$ok = $db-\u003eexecPrepared($query, function(KSQLiteParamsBinder $b) use ($stream) {\n  // Note: we're not even using $b-\u003eindex here as our data stream is statefull\n  // and it knows which item we're processing right now.\n  if (!$stream-\u003ehasMore()) {\n    return false;\n  }\n  $stream-\u003enext();\n  foreach ($stream-\u003ekeyValue() as $k =\u003e $v) {\n    $b-\u003ebind($k, $v);\n  }\n  return true;\n});\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\n```\n\nIf you find prepared statements API too low-level, consider wrapping it\nin some helper functions.\n\n### fetch\n\n```php\nfunction fetch(string $sql, array $params = [], $row_func = null): tuple(mixed, bool)\n```\n\n* `$sql` SQL query string with optional bind var placeholders\n* `$params` bind variables for the query\n* `$row_func` a callback that is called for every row, its return value is collected\n\nIf `$row_func` is null, default mapping behavior is used (`rowAssoc`).\n\nWhen to use: execute a query once, collect results.\n\n```php\n// The simplest case: no bind params, default mapping function, collecting all results.\n// The result rows are arrays of [x, y].\n$query = 'SELECT x, y FROM tab';\n[$rows, $ok] = $db-\u003efetch($query);\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\nforeach ($rows as $i =\u003e [$x, $y]) {\n  var_dump([$i =\u003e \"x=$x y=$y\"]);\n}\n\n// Using the same query, but building the result with assoc arrays,\n// like ['x' =\u003e $x, 'y' =\u003e $y].\n[$rows, $ok] = $db-\u003efetch($query, [], function(KSQLiteQueryContext $ctx) {\n  return $ctx-\u003erowDataAssoc();\n});\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\nforeach ($rows as $i =\u003e $data) {\n  var_dump([$i =\u003e \"x=$data['x'] y=$data['y']\"]);\n}\n\n// If you return a non-array value from fetch, you'll get a flat array in the final result.\n$query = 'SELECT id, second_key FROM users WHERE age \u003e= ?1';\n$vars = [1 =\u003e 18];\n[$ids, $ok] = $db-\u003efetch($query, $vars, function(KSQLiteQueryContext $ctx) {\n  return $ctx-\u003erowDataAssoc()['id'];\n});\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\nforeach ($ids as $i =\u003e $id) {\n  var_dump([$i =\u003e \"id=$id\"]);\n}\n```\n\nNotes:\n\n* You can stop the results fetching by using `$ctx-\u003estop()`\n* Use empty array for `$params` if your query has no bind vars, but you need a custom `$row_func`\n\n### fetchRow\n\n```php\nfunction fetchRow(string $sql, array $params = []): tuple(mixed[], bool)\n```\n\n* `$sql` SQL query string with optional bind var placeholders\n* `$params` bind variables for the query\n\nWhen to use: execute a query once, collecting exactly one result row.\n\n```php\n$query = 'SELECT * FROM users WHERE user_id = :id';\n[$user, $ok] = $db-\u003efetchRow($query, [':id' =\u003e $id]);\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\n```\n\nNote: if query returns more than one row, error will be reported.\nEither use `LIMIT 1` or other ways to request only 1 row from the database,\nor use `fetch()` method and skip rest of the rows explicitely.\n\n### fetchRowAssoc\n\nLike `fetchRow`, but result array has column name keys instead of indexes.\n\n### fetchColumn\n\n```php\nfunction fetchColumn(string $sql, array $params = []): tuple(mixed, bool)\n```\n\n* `$sql` SQL query string with optional bind var placeholders\n* `$params` bind variables for the query\n\nWhen to use: execute a query once, collecting exactly one result column.\n\n```php\n$query = 'SELECT COUNT(*) FROM users';\n[$num_users, $ok] = $db-\u003efetchColumn($query);\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\n```\n\nNote: if query returns more than one row or that row contains\nmore than one value, error will be reported.\n\n### query\n\n```php\nfunction query(string $sql, array $params, $row_func): bool\n```\n\n* `$sql` SQL query string with optional bind var placeholders\n* `$params` bind variables for the query\n* `$row_func` a void-result callback that is called for every row\n\nUnlike fetch-style API, it does not collect any results on its own.\nUse external state to do that.\n\nWhen to use: when query results are needed, but fetch API is not flexible enough.\n\n```php\n// Implementing a fetch-like operation via query.\n$result = new KSQLiteArray();\n$ok = $db-\u003equery($sql, $vars, function(SQLiteQueryContext $ctx) use ($result) {\n  $result-\u003evalues[] = $ctx-\u003erowData();\n});\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\n$handler-\u003eprocessData($result-\u003evalues); // Work with unboxed [K]PHP array\n```\n\n\u003e We're using KSQLiteArray here instead of a normal array since KPHP doesn't\n\u003e support by-reference closure captures.\n\n### queryPrepared\n\n```php\nfunction queryPrepared(string $sql, $bind_params_func, $row_func): bool\n```\n\n* `$sql` SQL query string with optional bind var placeholders\n* `$bind_params_func` a callback that binds variables for the query\n* `$row_func` a void-result callback that is called for every row\n\nWhen to use: same advantages like with `execPrepared`, but here you can collect the results.\n\n```php\n$ok = $db-\u003equeryPrepared(\n  'SELECT * FROM fav_numbers WHERE num_id = :num_id',\n  function(KSQLiteParamsBinder $b) use ($ids) {\n    if ($b-\u003equery_index \u003e= count($ids)) {\n      return false;\n    }\n    $b-\u003ebind(':num_id', $ids[$b-\u003equery_index]);\n    return true;\n  },\n  function(KSQLiteQueryContext $ctx) {\n    // $ctx-\u003equery_index is 0 for the first prepared query execution.\n    // The second execution will have $query_index=1 and so on.\n    var_dump($ctx-\u003equery_index . '=\u003e' . $ctx-\u003erowDataAssoc()['num_value']);\n  }\n);\nif (!$ok) {\n  handle_error($db-\u003egetLastError());\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquasilyte%2FKSQLite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquasilyte%2FKSQLite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquasilyte%2FKSQLite/lists"}