From 4f7ea899256f941577a6f0ef96217ad737a1e2ce Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Fri, 14 Aug 2015 21:33:39 -0400 Subject: [PATCH] Update dependencies --- common.php | 2 +- composer.json | 9 +- controllers/config.php | 2 +- controllers/history.php | 2 +- controllers/item.php | 2 +- cronjob.php | 3 +- fever/index.php | 20 +- jsonrpc.php | 7 +- models/config.php | 12 +- models/feed.php | 52 +- models/group.php | 20 +- models/item.php | 58 +- models/remember_me.php | 12 +- models/user.php | 2 +- vendor/composer/autoload_classmap.php | 6 + vendor/composer/installed.json | 155 +++-- vendor/fguillot/json-rpc/.travis.yml | 19 + vendor/fguillot/json-rpc/README.markdown | 20 +- vendor/fguillot/json-rpc/composer.json | 5 +- vendor/fguillot/json-rpc/phpunit.xml | 2 +- .../src/JsonRPC/AccessDeniedException.php | 7 + .../fguillot/json-rpc/src/JsonRPC/Client.php | 214 ++++--- .../fguillot/json-rpc/src/JsonRPC/Server.php | 69 ++- vendor/fguillot/json-rpc/tests/ClientTest.php | 45 ++ .../json-rpc/tests/ServerProtocolTest.php | 20 +- vendor/fguillot/picodb/.travis.yml | 19 + vendor/fguillot/picodb/README.md | 298 +++++---- .../fguillot/picodb/lib/PicoDb/Condition.php | 291 +++++++++ .../fguillot/picodb/lib/PicoDb/Database.php | 179 ++++-- .../picodb/lib/PicoDb/Driver/Base.php | 192 ++++++ .../picodb/lib/PicoDb/Driver/Mysql.php | 211 +++++-- .../picodb/lib/PicoDb/Driver/Postgres.php | 190 ++++-- .../picodb/lib/PicoDb/Driver/Sqlite.php | 187 ++++-- .../fguillot/picodb/lib/PicoDb/Hashtable.php | 103 ++-- .../picodb/lib/PicoDb/SQLException.php | 14 + vendor/fguillot/picodb/lib/PicoDb/Schema.php | 39 +- vendor/fguillot/picodb/lib/PicoDb/Table.php | 571 +++++++++--------- vendor/fguillot/picodb/phpunit.xml | 15 + .../picodb/tests/MysqlDatabaseTest.php | 100 +++ .../fguillot/picodb/tests/MysqlDriverTest.php | 65 ++ .../fguillot/picodb/tests/MysqlSchemaTest.php | 34 ++ .../fguillot/picodb/tests/MysqlTableTest.php | 313 ++++++++++ .../picodb/tests/PostgresDatabaseTest.php | 99 +++ .../picodb/tests/PostgresDriverTest.php | 70 +++ .../picodb/tests/PostgresSchemaTest.php | 34 ++ .../picodb/tests/PostgresTableTest.php | 312 ++++++++++ .../fguillot/picodb/tests/SchemaFixture.php | 19 + .../picodb/tests/SqliteDatabaseTest.php | 119 ++++ .../picodb/tests/SqliteDriverTest.php | 62 ++ .../picodb/tests/SqliteSchemaTest.php | 30 + .../fguillot/picodb/tests/SqliteTableTest.php | 327 ++++++++++ 51 files changed, 3675 insertions(+), 983 deletions(-) create mode 100644 vendor/fguillot/json-rpc/.travis.yml create mode 100644 vendor/fguillot/json-rpc/src/JsonRPC/AccessDeniedException.php create mode 100644 vendor/fguillot/picodb/.travis.yml create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Condition.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php create mode 100644 vendor/fguillot/picodb/lib/PicoDb/SQLException.php create mode 100644 vendor/fguillot/picodb/phpunit.xml create mode 100644 vendor/fguillot/picodb/tests/MysqlDatabaseTest.php create mode 100644 vendor/fguillot/picodb/tests/MysqlDriverTest.php create mode 100644 vendor/fguillot/picodb/tests/MysqlSchemaTest.php create mode 100644 vendor/fguillot/picodb/tests/MysqlTableTest.php create mode 100644 vendor/fguillot/picodb/tests/PostgresDatabaseTest.php create mode 100644 vendor/fguillot/picodb/tests/PostgresDriverTest.php create mode 100644 vendor/fguillot/picodb/tests/PostgresSchemaTest.php create mode 100644 vendor/fguillot/picodb/tests/PostgresTableTest.php create mode 100644 vendor/fguillot/picodb/tests/SchemaFixture.php create mode 100644 vendor/fguillot/picodb/tests/SqliteDatabaseTest.php create mode 100644 vendor/fguillot/picodb/tests/SqliteDriverTest.php create mode 100644 vendor/fguillot/picodb/tests/SqliteSchemaTest.php create mode 100644 vendor/fguillot/picodb/tests/SqliteTableTest.php diff --git a/common.php b/common.php index 7e2a904..c569320 100644 --- a/common.php +++ b/common.php @@ -41,7 +41,7 @@ defined('ENABLE_HSTS') or define('ENABLE_HSTS', true); require __DIR__.'/check_setup.php'; -PicoDb\Database::bootstrap('db', function() { +PicoDb\Database::setInstance('db', function() { $db = new PicoDb\Database(array( 'driver' => 'sqlite', diff --git a/composer.json b/composer.json index 9f8c941..3786ef4 100644 --- a/composer.json +++ b/composer.json @@ -4,11 +4,16 @@ }, "require": { "fguillot/simple-validator": "v0.0.3", - "fguillot/json-rpc": "v0.0.3", - "fguillot/picodb": "v0.0.3", + "fguillot/json-rpc": "v1.0.1", + "fguillot/picodb": "v1.0.1", "fguillot/picofeed": "v0.1.8", "fguillot/picofarad": "dev-master" }, + "require-dev": { + "phpunit/phpunit": "4.8.3", + "phpunit/phpunit-selenium": "1.4.2", + "phpunit/dbunit": "1.4.1" + }, "autoload": { "files": [ "lib/helpers.php", diff --git a/controllers/config.php b/controllers/config.php index f9dd8f5..834df23 100644 --- a/controllers/config.php +++ b/controllers/config.php @@ -99,7 +99,7 @@ Router\get_action('generate-tokens', function() { Router\get_action('optimize-db', function() { if (Model\Config\check_csrf(Request\param('csrf'))) { - Database::get('db')->getConnection()->exec('VACUUM'); + Database::getInstance('db')->getConnection()->exec('VACUUM'); } Response\redirect('?action=database'); diff --git a/controllers/history.php b/controllers/history.php index 0970e4f..5979614 100644 --- a/controllers/history.php +++ b/controllers/history.php @@ -12,7 +12,7 @@ Router\get_action('history', function() { $nb_items = Model\Item\count_by_status('read'); $items = Model\Item\get_all_by_status( 'read', - null, + array(), $offset, Model\Config\get('items_per_page'), 'updated', diff --git a/controllers/item.php b/controllers/item.php index 10a15ba..85dc9da 100644 --- a/controllers/item.php +++ b/controllers/item.php @@ -15,7 +15,7 @@ Router\get_action('unread', function() { $direction = Request\param('direction', Model\Config\get('items_sorting_direction')); $offset = Request\int_param('offset', 0); $group_id = Request\int_param('group_id', null); - $feed_ids = null; + $feed_ids = array(); if (!is_null($group_id)) { $feed_ids = Model\Group\get_feeds_by_group($group_id); diff --git a/cronjob.php b/cronjob.php index 7ab05ce..7989f52 100644 --- a/cronjob.php +++ b/cronjob.php @@ -27,8 +27,7 @@ $update_interval = ! empty($options['update-interval']) && ctype_digit($options[ $call_interval = ! empty($options['call-interval']) && ctype_digit($options['call-interval']) ? (int) $options['call-interval'] : null; if ($update_interval !== null && $call_interval !== null && $limit === Model\Feed\LIMIT_ALL && $update_interval >= $call_interval) { - - $feeds_count = PicoDb\Database::get('db')->table('feeds')->count(); + $feeds_count = PicoDb\Database::getInstance('db')->table('feeds')->count(); $limit = ceil($feeds_count / ($update_interval / $call_interval)); } diff --git a/fever/index.php b/fever/index.php index f975f7f..b3e6624 100644 --- a/fever/index.php +++ b/fever/index.php @@ -41,7 +41,7 @@ function auth() } } - $credentials = Database::get('db')->hashtable('settings')->get('username', 'fever_token'); + $credentials = Database::getInstance('db')->hashtable('settings')->get('username', 'fever_token'); $api_key = md5($credentials['username'].':'.$credentials['fever_token']); $response = array( @@ -118,7 +118,7 @@ route('favicons', function() { if ($response['auth']) { - $favicons = Database::get('db') + $favicons = Database::getInstance('db') ->table('favicons') ->columns( 'feed_id', @@ -145,7 +145,7 @@ route('items', function() { if ($response['auth']) { - $query = Database::get('db') + $query = Database::getInstance('db') ->table('items') ->columns( 'rowid', @@ -187,7 +187,7 @@ route('items', function() { ); } - $response['total_items'] = Database::get('db') + $response['total_items'] = Database::getInstance('db') ->table('items') ->neq('status', 'removed') ->count(); @@ -215,7 +215,7 @@ route('unread_item_ids', function() { if ($response['auth']) { - $item_ids = Database::get('db') + $item_ids = Database::getInstance('db') ->table('items') ->eq('status', 'unread') ->findAllByColumn('rowid'); @@ -233,7 +233,7 @@ route('saved_item_ids', function() { if ($response['auth']) { - $item_ids = Database::get('db') + $item_ids = Database::getInstance('db') ->table('items') ->eq('bookmark', 1) ->findAllByColumn('rowid'); @@ -251,7 +251,7 @@ route('write_items', function() { if ($response['auth']) { - $query = Database::get('db') + $query = Database::getInstance('db') ->table('items') ->eq('rowid', $_POST['id']); @@ -259,7 +259,7 @@ route('write_items', function() { $query->update(array('bookmark' => 1)); // Send bookmark to third-party services if enabled - $item_id = Database::get('db') + $item_id = Database::getInstance('db') ->table('items') ->eq('rowid', $_POST['id']) ->findOneColumn('id'); @@ -287,7 +287,7 @@ route('write_feeds', function() { if ($response['auth']) { - Database::get('db') + Database::getInstance('db') ->table('items') ->eq('feed_id', $_POST['id']) ->lte('updated', $_POST['before']) @@ -303,7 +303,7 @@ route('write_groups', function() { $response = auth(); if ($response['auth']) { - $db = Database::get('db') + $db = Database::getInstance('db') ->table('items') ->lte('updated', $_POST['before']); diff --git a/jsonrpc.php b/jsonrpc.php index cebfbef..6e23fef 100644 --- a/jsonrpc.php +++ b/jsonrpc.php @@ -4,10 +4,11 @@ require __DIR__.'/common.php'; use JsonRPC\Server; use PicoFeed\PicoFeedException; +use Model\Config; $server = new Server; $server->authentication(array( - \Model\Config\get('username') => \Model\Config\get('api_token') + Config\get('username') => Config\get('api_token') )); // Get version @@ -115,7 +116,7 @@ $server->register('item.bookmark.delete', function ($item_id) { // Get all unread items $server->register('item.list_unread', function ($offset = null, $limit = null) { - return Model\Item\get_all_by_status('unread', null, $offset, $limit); + return Model\Item\get_all_by_status('unread', array(), $offset, $limit); }); // Count all unread items @@ -127,7 +128,7 @@ $server->register('item.count_unread', function () { // Get all read items $server->register('item.list_read', function ($offset = null, $limit = null) { - return Model\Item\get_all_by_status('read', null, $offset, $limit); + return Model\Item\get_all_by_status('read', array(), $offset, $limit); }); // Count all read items diff --git a/models/config.php b/models/config.php index d498a76..4cf4b21 100644 --- a/models/config.php +++ b/models/config.php @@ -255,14 +255,14 @@ function new_tokens() 'fever_token' => substr(generate_token(), 0, 8), ); - return Database::get('db')->hashtable('settings')->put($values); + return Database::getInstance('db')->hashtable('settings')->put($values); } // Get a config value from the DB or from the session function get($name) { if (! isset($_SESSION)) { - return current(Database::get('db')->hashtable('settings')->get($name)); + return current(Database::getInstance('db')->hashtable('settings')->get($name)); } else { @@ -281,10 +281,8 @@ function get($name) // Get all config parameters function get_all() { - $config = Database::get('db')->hashtable('settings')->get(); - + $config = Database::getInstance('db')->hashtable('settings')->get(); unset($config['password']); - return $config; } @@ -339,10 +337,10 @@ function save(array $values) // If the user does not want content of feeds, remove it in previous ones if (isset($values['nocontent']) && (bool) $values['nocontent']) { - Database::get('db')->table('items')->update(array('content' => '')); + Database::getInstance('db')->table('items')->update(array('content' => '')); } - if (Database::get('db')->hashtable('settings')->put($values)) { + if (Database::getInstance('db')->hashtable('settings')->put($values)) { reload(); return true; } diff --git a/models/feed.php b/models/feed.php index 0e293b2..5176c58 100644 --- a/models/feed.php +++ b/models/feed.php @@ -21,7 +21,7 @@ const LIMIT_ALL = -1; // Store the favicon function store_favicon($feed_id, $link, $icon) { - return Database::get('db') + return Database::getInstance('db') ->table('favicons') ->save(array( 'feed_id' => $feed_id, @@ -48,7 +48,7 @@ function fetch_favicon($feed_id, $site_url, $icon_link) // Return true if the feed have a favicon function has_favicon($feed_id) { - return Database::get('db')->table('favicons')->eq('feed_id', $feed_id)->count() === 1; + return Database::getInstance('db')->table('favicons')->eq('feed_id', $feed_id)->count() === 1; } // Get favicons for those feeds @@ -58,7 +58,7 @@ function get_favicons(array $feed_ids) return array(); } - $db = Database::get('db') + $db = Database::getInstance('db') ->hashtable('favicons') ->columnKey('feed_id') ->columnValue('icon'); @@ -86,7 +86,7 @@ function get_all_favicons() return array(); } - return Database::get('db') + return Database::getInstance('db') ->hashtable('favicons') ->getAll('feed_id', 'icon'); } @@ -94,9 +94,9 @@ function get_all_favicons() // Update feed information function update(array $values) { - Database::get('db')->startTransaction(); + Database::getInstance('db')->startTransaction(); - $result = Database::get('db') + $result = Database::getInstance('db') ->table('feeds') ->eq('id', $values['id']) ->save(array( @@ -111,12 +111,12 @@ function update(array $values) if ($result) { if (! Group\update_feed_groups($values['id'], $values['feed_group_ids'], $values['create_group'])) { - Database::get('db')->cancelTransaction(); + Database::getInstance('db')->cancelTransaction(); $result = false; } } - Database::get('db')->closeTransaction(); + Database::getInstance('db')->closeTransaction(); return $result; } @@ -136,7 +136,7 @@ function import_opml($content) if ($feeds) { - $db = Database::get('db'); + $db = Database::getInstance('db'); $db->startTransaction(); foreach ($feeds as $feed) { @@ -168,7 +168,7 @@ function create($url, $enable_grabber = false, $force_rtl = false, $cloak_referr { $feed_id = false; - $db = Database::get('db'); + $db = Database::getInstance('db'); // Discover the feed $reader = new Reader(Config\get_reader_config()); @@ -206,7 +206,7 @@ function create($url, $enable_grabber = false, $force_rtl = false, $cloak_referr )); if ($result) { - $feed_id = $db->getConnection()->getLastId(); + $feed_id = $db->getLastId(); Group\update_feed_groups($feed_id, $group_ids, $create_group); Item\update_all($feed_id, $feed->getItems()); @@ -224,7 +224,7 @@ function refresh_all($limit = LIMIT_ALL) } // Auto-vacuum for people using the cronjob - Database::get('db')->getConnection()->exec('VACUUM'); + Database::getInstance('db')->getConnection()->exec('VACUUM'); return true; } @@ -266,7 +266,7 @@ function refresh($feed_id) // Don't fetch previous items, only new one $parser->setGrabberIgnoreUrls( - Database::get('db')->table('items')->eq('feed_id', $feed_id)->findAllByColumn('url') + Database::getInstance('db')->table('items')->eq('feed_id', $feed_id)->findAllByColumn('url') ); } @@ -298,7 +298,7 @@ function refresh($feed_id) // Get the list of feeds ID to refresh function get_ids($limit = LIMIT_ALL) { - $query = Database::get('db')->table('feeds')->eq('enabled', 1)->asc('last_checked'); + $query = Database::getInstance('db')->table('feeds')->eq('enabled', 1)->asc('last_checked'); if ($limit !== LIMIT_ALL) { $query->limit((int) $limit); @@ -310,7 +310,7 @@ function get_ids($limit = LIMIT_ALL) // get number of feeds with errors function count_failed_feeds() { - return Database::get('db') + return Database::getInstance('db') ->table('feeds') ->eq('parsing_error', '1') ->count(); @@ -319,7 +319,7 @@ function count_failed_feeds() // Get all feeds function get_all() { - return Database::get('db') + return Database::getInstance('db') ->table('feeds') ->asc('title') ->findAll(); @@ -328,7 +328,7 @@ function get_all() // Get all feeds with the number unread/total items in the order failed, working, disabled function get_all_item_counts() { - return Database::get('db') + return Database::getInstance('db') ->table('feeds') ->columns( 'feeds.*', @@ -346,7 +346,7 @@ function get_all_item_counts() // Get unread/total count for one feed function count_items($feed_id) { - $counts = Database::get('db') + $counts = Database::getInstance('db') ->table('items') ->columns('status', 'count(*) as item_count') ->in('status', array('read', 'unread')) @@ -374,7 +374,7 @@ function count_items($feed_id) // Get one feed function get($feed_id) { - return Database::get('db') + return Database::getInstance('db') ->table('feeds') ->eq('id', $feed_id) ->findOne(); @@ -383,13 +383,13 @@ function get($feed_id) // Update parsing error column function update_parsing_error($feed_id, $value) { - Database::get('db')->table('feeds')->eq('id', $feed_id)->save(array('parsing_error' => $value)); + Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->save(array('parsing_error' => $value)); } // Update last check date function update_last_checked($feed_id) { - Database::get('db') + Database::getInstance('db') ->table('feeds') ->eq('id', $feed_id) ->save(array( @@ -400,7 +400,7 @@ function update_last_checked($feed_id) // Update Etag and last Modified columns function update_cache($feed_id, $last_modified, $etag) { - Database::get('db') + Database::getInstance('db') ->table('feeds') ->eq('id', $feed_id) ->save(array( @@ -413,25 +413,25 @@ function update_cache($feed_id, $last_modified, $etag) function remove($feed_id) { // Items are removed by a sql constraint - return Database::get('db')->table('feeds')->eq('id', $feed_id)->remove(); + return Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->remove(); } // Remove all feeds function remove_all() { - return Database::get('db')->table('feeds')->remove(); + return Database::getInstance('db')->table('feeds')->remove(); } // Enable a feed (activate refresh) function enable($feed_id) { - return Database::get('db')->table('feeds')->eq('id', $feed_id)->save((array('enabled' => 1))); + return Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->save((array('enabled' => 1))); } // Disable feed function disable($feed_id) { - return Database::get('db')->table('feeds')->eq('id', $feed_id)->save((array('enabled' => 0))); + return Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->save((array('enabled' => 0))); } // Validation for edit diff --git a/models/group.php b/models/group.php index dcdffcd..4fadd94 100644 --- a/models/group.php +++ b/models/group.php @@ -11,7 +11,7 @@ use PicoDb\Database; */ function get_all() { - return Database::get('db') + return Database::getInstance('db') ->table('groups') ->orderBy('title') ->findAll(); @@ -24,7 +24,7 @@ function get_all() */ function get_map() { - $result = Database::get('db') + $result = Database::getInstance('db') ->table('feeds_groups') ->findAll(); @@ -55,7 +55,7 @@ function get_map() */ function get_feed_group_ids($feed_id) { - return Database::get('db') + return Database::getInstance('db') ->table('groups') ->join('feeds_groups', 'group_id', 'id') ->eq('feed_id', $feed_id) @@ -70,7 +70,7 @@ function get_feed_group_ids($feed_id) */ function get_group_id($title) { - return Database::get('db') + return Database::getInstance('db') ->table('groups') ->eq('title', $title) ->findOneColumn('id'); @@ -84,7 +84,7 @@ function get_group_id($title) */ function get_feeds_by_group($group_id) { - return Database::get('db') + return Database::getInstance('db') ->table('feeds_groups') ->eq('group_id', $group_id) ->findAllByColumn('feed_id'); @@ -108,7 +108,7 @@ function create($title) // create group if missing if ($group_id === false) { - Database::get('db') + Database::getInstance('db') ->table('groups') ->insert($data); @@ -130,7 +130,7 @@ function add($feed_id, $group_ids) foreach ($group_ids as $group_id){ $data = array('feed_id' => $feed_id, 'group_id' => $group_id); - $result = Database::get('db') + $result = Database::getInstance('db') ->table('feeds_groups') ->insert($data); @@ -151,7 +151,7 @@ function add($feed_id, $group_ids) */ function remove($feed_id, $group_ids) { - return Database::get('db') + return Database::getInstance('db') ->table('feeds_groups') ->eq('feed_id', $feed_id) ->in('group_id', $group_ids) @@ -163,14 +163,14 @@ function remove($feed_id, $group_ids) */ function purge_groups() { - $groups = Database::get('db') + $groups = Database::getInstance('db') ->table('groups') ->join('feeds_groups', 'group_id', 'id') ->isnull('feed_id') ->findAllByColumn('id'); if (! empty($groups)) { - Database::get('db') + Database::getInstance('db') ->table('groups') ->in('id', $groups) ->remove(); diff --git a/models/item.php b/models/item.php index ee31a61..5eabdb5 100644 --- a/models/item.php +++ b/models/item.php @@ -12,7 +12,7 @@ use PicoFeed\Scraper\Scraper; // Get all items without filtering function get_all() { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->columns( 'items.id', @@ -39,7 +39,7 @@ function get_all() // Get everthing since date (timestamp) function get_all_since($timestamp) { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->columns( 'items.id', @@ -66,7 +66,7 @@ function get_all_since($timestamp) function get_latest_feeds_items() { - return Database::get('db') + return Database::getInstance('db') ->table('feeds') ->columns( 'feeds.id', @@ -82,7 +82,7 @@ function get_latest_feeds_items() // Get a list of [item_id => status,...] function get_all_status() { - return Database::get('db') + return Database::getInstance('db') ->hashtable('items') ->in('status', array('read', 'unread')) ->orderBy('updated', 'desc') @@ -90,9 +90,9 @@ function get_all_status() } // Get all items by status -function get_all_by_status($status, $feed_ids = null, $offset = null, $limit = null, $order_column = 'updated', $order_direction = 'desc') +function get_all_by_status($status, $feed_ids = array(), $offset = null, $limit = null, $order_column = 'updated', $order_direction = 'desc') { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->columns( 'items.id', @@ -120,9 +120,9 @@ function get_all_by_status($status, $feed_ids = null, $offset = null, $limit = n } // Get the number of items per status -function count_by_status($status, $feed_ids = null) +function count_by_status($status, $feed_ids = array()) { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('status', $status) ->in('feed_id', $feed_ids) @@ -132,7 +132,7 @@ function count_by_status($status, $feed_ids = null) // Get the number of bookmarks function count_bookmarks() { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('bookmark', 1) ->in('status', array('read', 'unread')) @@ -142,7 +142,7 @@ function count_bookmarks() // Get all bookmarks function get_bookmarks($offset = null, $limit = null) { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->columns( 'items.id', @@ -172,7 +172,7 @@ function get_bookmarks($offset = null, $limit = null) // Get the number of items per feed function count_by_feed($feed_id) { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('feed_id', $feed_id) ->in('status', array('unread', 'read')) @@ -182,7 +182,7 @@ function count_by_feed($feed_id) // Get all items per feed function get_all_by_feed($feed_id, $offset = null, $limit = null, $order_column = 'updated', $order_direction = 'desc') { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->columns( 'items.id', @@ -211,7 +211,7 @@ function get_all_by_feed($feed_id, $offset = null, $limit = null, $order_column // Get one item by id function get($id) { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('id', $id) ->findOne(); @@ -220,7 +220,7 @@ function get($id) // Get item naviguation (next/prev items) function get_nav($item, $status = array('unread'), $bookmark = array(1, 0), $feed_id = null) { - $query = Database::get('db') + $query = Database::getInstance('db') ->table('items') ->columns('id', 'status', 'title', 'bookmark') ->neq('status', 'removed') @@ -280,7 +280,7 @@ function get_nav($item, $status = array('unread'), $bookmark = array(1, 0), $fee // Change item status to removed and clear content function set_removed($id) { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('id', $id) ->save(array('status' => 'removed', 'content' => '')); @@ -289,7 +289,7 @@ function set_removed($id) // Change item status to read function set_read($id) { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('id', $id) ->save(array('status' => 'read')); @@ -298,7 +298,7 @@ function set_read($id) // Change item status to unread function set_unread($id) { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('id', $id) ->save(array('status' => 'unread')); @@ -309,7 +309,7 @@ function set_status($status, array $items) { if (! in_array($status, array('read', 'unread', 'removed'))) return false; - return Database::get('db') + return Database::getInstance('db') ->table('items') ->in('id', $items) ->save(array('status' => $status)); @@ -322,7 +322,7 @@ function set_bookmark_value($id, $value) Service\push($id); } - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('id', $id) ->in('status', array('read', 'unread')) @@ -332,7 +332,7 @@ function set_bookmark_value($id, $value) // Mark all unread items as read function mark_all_as_read() { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('status', 'unread') ->save(array('status' => 'read')); @@ -341,7 +341,7 @@ function mark_all_as_read() // Mark all read items to removed function mark_all_as_removed() { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('status', 'read') ->eq('bookmark', 0) @@ -351,7 +351,7 @@ function mark_all_as_removed() // Mark all items of a feed as read function mark_feed_as_read($feed_id) { - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('status', 'unread') ->eq('feed_id', $feed_id) @@ -364,7 +364,7 @@ function mark_group_as_read($group_id) // workaround for missing update with join $feed_ids = Group\get_feeds_by_group($group_id); - return Database::get('db') + return Database::getInstance('db') ->table('items') ->eq('status', 'unread') ->in('feed_id', $feed_ids) @@ -379,7 +379,7 @@ function autoflush_read() if ($autoflush > 0) { // Mark read items removed after X days - Database::get('db') + Database::getInstance('db') ->table('items') ->eq('bookmark', 0) ->eq('status', 'read') @@ -389,7 +389,7 @@ function autoflush_read() else if ($autoflush === -1) { // Mark read items removed immediately - Database::get('db') + Database::getInstance('db') ->table('items') ->eq('bookmark', 0) ->eq('status', 'read') @@ -405,7 +405,7 @@ function autoflush_unread() if ($autoflush > 0) { // Mark read items removed after X days - Database::get('db') + Database::getInstance('db') ->table('items') ->eq('bookmark', 0) ->eq('status', 'unread') @@ -421,7 +421,7 @@ function update_all($feed_id, array $items) $items_in_feed = array(); - $db = Database::get('db'); + $db = Database::getInstance('db'); $db->startTransaction(); foreach ($items as $item) { @@ -490,7 +490,7 @@ function cleanup($feed_id, array $items_in_feed) { if (! empty($items_in_feed)) { - $db = Database::get('db'); + $db = Database::getInstance('db'); $removed_items = $db ->table('items') @@ -558,7 +558,7 @@ function download_content_id($item_id) if (! Config\get('nocontent')) { // Save content - Database::get('db') + Database::getInstance('db') ->table('items') ->eq('id', $item['id']) ->save(array('content' => $content)); diff --git a/models/remember_me.php b/models/remember_me.php index d09a897..35d0c62 100644 --- a/models/remember_me.php +++ b/models/remember_me.php @@ -18,7 +18,7 @@ const EXPIRATION = 5184000; */ function find($token, $sequence) { - return Database::get('db') + return Database::getInstance('db') ->table(TABLE) ->eq('token', $token) ->eq('sequence', $sequence) @@ -34,7 +34,7 @@ function find($token, $sequence) */ function get_all() { - return Database::get('db') + return Database::getInstance('db') ->table(TABLE) ->desc('date_creation') ->columns('id', 'ip', 'user_agent', 'date_creation', 'expiration') @@ -111,7 +111,7 @@ function destroy() if ($credentials !== false) { - Database::get('db') + Database::getInstance('db') ->table(TABLE) ->eq('token', $credentials['token']) ->remove(); @@ -138,7 +138,7 @@ function create($dbname, $username, $ip, $user_agent) cleanup(); - Database::get('db') + Database::getInstance('db') ->table(TABLE) ->insert(array( 'username' => $username, @@ -165,7 +165,7 @@ function create($dbname, $username, $ip, $user_agent) */ function cleanup() { - return Database::get('db') + return Database::getInstance('db') ->table(TABLE) ->lt('expiration', time()) ->remove(); @@ -182,7 +182,7 @@ function update($token) { $new_sequence = Config\generate_token(); - Database::get('db') + Database::getInstance('db') ->table(TABLE) ->eq('token', $token) ->update(array('sequence' => $new_sequence)); diff --git a/models/user.php b/models/user.php index 21b44e9..8a24bf7 100644 --- a/models/user.php +++ b/models/user.php @@ -26,7 +26,7 @@ function logout() // Get the credentials from the current selected database function getCredentials() { - return Database::get('db') + return Database::getInstance('db') ->hashtable('settings') ->get('username', 'password'); } diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index b1ca374..8b8632e 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,16 +6,22 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'JsonRPC\\AccessDeniedException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/AccessDeniedException.php', 'JsonRPC\\AuthenticationFailure' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php', 'JsonRPC\\Client' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php', + 'JsonRPC\\ConnectionFailureException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php', 'JsonRPC\\InvalidJsonFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php', 'JsonRPC\\InvalidJsonRpcFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php', 'JsonRPC\\Server' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php', + 'JsonRPC\\ServerErrorException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php', + 'PicoDb\\Condition' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Condition.php', 'PicoDb\\Database' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Database.php', + 'PicoDb\\Driver\\Base' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Base.php', 'PicoDb\\Driver\\Mysql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php', 'PicoDb\\Driver\\Postgres' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php', 'PicoDb\\Driver\\Sqlite' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php', 'PicoDb\\Hashtable' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Hashtable.php', + 'PicoDb\\SQLException' => $vendorDir . '/fguillot/picodb/lib/PicoDb/SQLException.php', 'PicoDb\\Schema' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Schema.php', 'PicoDb\\Table' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Table.php', 'PicoFeed\\Client\\Client' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Client.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index df37fc7..a08af6c 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -76,84 +76,6 @@ "description": "The most easy to use validator library for PHP :)", "homepage": "https://github.com/fguillot/simpleValidator" }, - { - "name": "fguillot/json-rpc", - "version": "v0.0.3", - "version_normalized": "0.0.3.0", - "source": { - "type": "git", - "url": "https://github.com/fguillot/JsonRPC.git", - "reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8", - "reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "time": "2015-05-20 15:08:40", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "JsonRPC": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frédéric Guillot", - "homepage": "http://fredericguillot.com" - } - ], - "description": "Simple Json-RPC client/server library that just works", - "homepage": "https://github.com/fguillot/JsonRPC" - }, - { - "name": "fguillot/picodb", - "version": "v0.0.3", - "version_normalized": "0.0.3.0", - "source": { - "type": "git", - "url": "https://github.com/fguillot/picoDb.git", - "reference": "f65d11cb52de34e0fd236a34184ca1a310da244a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fguillot/picoDb/zipball/f65d11cb52de34e0fd236a34184ca1a310da244a", - "reference": "f65d11cb52de34e0fd236a34184ca1a310da244a", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "time": "2015-05-17 23:57:05", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "PicoDb": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frédéric Guillot", - "homepage": "http://fredericguillot.com" - } - ], - "description": "Minimalist database query builder", - "homepage": "https://github.com/fguillot/picoDb" - }, { "name": "fguillot/picofeed", "version": "v0.1.8", @@ -202,5 +124,82 @@ ], "description": "Modern library to handle RSS/Atom feeds", "homepage": "https://github.com/fguillot/picoFeed" + }, + { + "name": "fguillot/json-rpc", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/JsonRPC.git", + "reference": "9a117e964c4c6ad026da7ae1ca155f7686e3deaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/9a117e964c4c6ad026da7ae1ca155f7686e3deaf", + "reference": "9a117e964c4c6ad026da7ae1ca155f7686e3deaf", + "shasum": "" + }, + "require": { + "php": ">=5.3.4" + }, + "time": "2015-08-07 22:31:21", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "JsonRPC": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Simple Json-RPC client/server library that just works", + "homepage": "https://github.com/fguillot/JsonRPC" + }, + { + "name": "fguillot/picodb", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/fguillot/picoDb.git", + "reference": "8a311523d114180e04a1e08ced6766f26d7ebbae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/picoDb/zipball/8a311523d114180e04a1e08ced6766f26d7ebbae", + "reference": "8a311523d114180e04a1e08ced6766f26d7ebbae", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2015-08-13 01:44:29", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "PicoDb": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot", + "homepage": "http://fredericguillot.com" + } + ], + "description": "Minimalist database query builder", + "homepage": "https://github.com/fguillot/picoDb" } ] diff --git a/vendor/fguillot/json-rpc/.travis.yml b/vendor/fguillot/json-rpc/.travis.yml new file mode 100644 index 0000000..00b2b5b --- /dev/null +++ b/vendor/fguillot/json-rpc/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: + - 7.0 + - 5.6 + - 5.5 + - 5.4 + - 5.3 + +matrix: + fast_finish: true + allow_failures: + - php: 7.0 + +before_script: + - composer dump-autoload + +script: + - phpunit \ No newline at end of file diff --git a/vendor/fguillot/json-rpc/README.markdown b/vendor/fguillot/json-rpc/README.markdown index a0793bf..3915b7e 100644 --- a/vendor/fguillot/json-rpc/README.markdown +++ b/vendor/fguillot/json-rpc/README.markdown @@ -3,6 +3,10 @@ JsonRPC PHP Client and Server A simple Json-RPC client/server that just works. +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fguillot/JsonRPC/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/fguillot/JsonRPC/?branch=master) + +[![Build Status](https://travis-ci.org/fguillot/JsonRPC.svg?branch=master)](https://travis-ci.org/fguillot/JsonRPC) + Features -------- @@ -11,24 +15,19 @@ Features - Authentication and IP based client restrictions - Minimalist: there is only 2 files - Fully unit tested +- Requirements: PHP >= 5.3.4 - License: MIT -Requirements ------------- - -- The only dependency is the cURL extension -- PHP >= 5.3 - Author ------ -[Frédéric Guillot](http://fredericguillot.com) +Frédéric Guillot Installation with Composer -------------------------- ```bash -composer require fguillot/json-rpc dev-master +composer require fguillot/json-rpc @stable ``` Examples @@ -214,7 +213,10 @@ All results are stored at the same position of the call. - `BadFunctionCallException`: Procedure not found on the server - `InvalidArgumentException`: Wrong procedure arguments -- `RuntimeException`: Protocol error, authentication failure or connection failure, the message describe the exact error +- `JsonRPC\AccessDeniedException`: Access denied +- `JsonRPC\ConnectionFailureException`: Connection failure +- `JsonRPC\ServerErrorException`: Internal server error +- `RuntimeException`: Protocol error ### Enable client debugging diff --git a/vendor/fguillot/json-rpc/composer.json b/vendor/fguillot/json-rpc/composer.json index 3dc805c..41c5427 100644 --- a/vendor/fguillot/json-rpc/composer.json +++ b/vendor/fguillot/json-rpc/composer.json @@ -6,12 +6,11 @@ "license": "MIT", "authors": [ { - "name": "Frédéric Guillot", - "homepage": "http://fredericguillot.com" + "name": "Frédéric Guillot" } ], "require": { - "php": ">=5.3.0" + "php": ">=5.3.4" }, "autoload": { "psr-0": {"JsonRPC": "src/"} diff --git a/vendor/fguillot/json-rpc/phpunit.xml b/vendor/fguillot/json-rpc/phpunit.xml index dc36b5e..1eee422 100644 --- a/vendor/fguillot/json-rpc/phpunit.xml +++ b/vendor/fguillot/json-rpc/phpunit.xml @@ -1,4 +1,4 @@ - + tests diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/AccessDeniedException.php b/vendor/fguillot/json-rpc/src/JsonRPC/AccessDeniedException.php new file mode 100644 index 0000000..ad92746 --- /dev/null +++ b/vendor/fguillot/json-rpc/src/JsonRPC/AccessDeniedException.php @@ -0,0 +1,7 @@ +', 'Content-Type: application/json', - 'Accept: application/json' + 'Accept: application/json', + 'Connection: close', ); + /** * SSL certificates verification + * * @access public * @var boolean */ public $ssl_verify_peer = true; - /** - * cURL handle - * - * @access private - */ - private $ch; - /** * Constructor * * @access public * @param string $url Server URL - * @param integer $timeout Server URL + * @param integer $timeout HTTP timeout * @param array $headers Custom HTTP headers */ - public function __construct($url, $timeout = 5, $headers = array()) + public function __construct($url, $timeout = 3, $headers = array()) { $this->url = $url; $this->timeout = $timeout; $this->headers = array_merge($this->headers, $headers); - $this->ch = curl_init(); - } - - /** - * Destructor - * - * @access public - */ - public function __destruct() - { - curl_close($this->ch); } /** @@ -154,11 +143,14 @@ class Client * @access public * @param string $username Username * @param string $password Password + * @return Client */ public function authentication($username, $password) { $this->username = $username; $this->password = $password; + + return $this; } /** @@ -243,7 +235,6 @@ class Client public function parseResponse(array $payload) { if ($this->isBatchResponse($payload)) { - $results = array(); foreach ($payload as $response) { @@ -256,6 +247,113 @@ class Client return $this->getResult($payload); } + /** + * Throw an exception according the RPC error + * + * @access public + * @param array $error + * @throws BadFunctionCallException + * @throws InvalidArgumentException + * @throws RuntimeException + */ + public function handleRpcErrors(array $error) + { + switch ($error['code']) { + case -32601: + throw new BadFunctionCallException('Procedure not found: '. $error['message']); + case -32602: + throw new InvalidArgumentException('Invalid arguments: '. $error['message']); + default: + throw new RuntimeException('Invalid request/response: '. $error['message'], $error['code']); + } + } + + /** + * Throw an exception according the HTTP response + * + * @access public + * @param array $headers + * @throws AccessDeniedException + * @throws ServerErrorException + */ + public function handleHttpErrors(array $headers) + { + $exceptions = array( + '401' => 'JsonRPC\AccessDeniedException', + '403' => 'JsonRPC\AccessDeniedException', + '404' => 'JsonRPC\ConnectionFailureException', + '500' => 'JsonRPC\ServerErrorException', + ); + + foreach ($headers as $header) { + foreach ($exceptions as $code => $exception) { + if (strpos($header, 'HTTP/1.0 '.$code) !== false || strpos($header, 'HTTP/1.1 '.$code) !== false) { + throw new $exception('Response: '.$header); + } + } + } + } + + /** + * Do the HTTP request + * + * @access private + * @param array $payload + * @return array + */ + private function doRequest(array $payload) + { + $stream = @fopen(trim($this->url), 'r', false, $this->getContext($payload)); + + if (! is_resource($stream)) { + throw new ConnectionFailureException('Unable to establish a connection'); + } + + $metadata = stream_get_meta_data($stream); + $this->handleHttpErrors($metadata['wrapper_data']); + + $response = json_decode(stream_get_contents($stream), true); + + if ($this->debug) { + error_log('==> Request: '.PHP_EOL.json_encode($payload, JSON_PRETTY_PRINT)); + error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT)); + } + + return is_array($response) ? $response : array(); + } + + /** + * Prepare stream context + * + * @access private + * @param array $payload + * @return resource + */ + private function getContext(array $payload) + { + $headers = $this->headers; + + if (! empty($this->username) && ! empty($this->password)) { + $headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password); + } + + return stream_context_create(array( + 'http' => array( + 'method' => 'POST', + 'protocol_version' => 1.1, + 'timeout' => $this->timeout, + 'max_redirects' => 2, + 'header' => implode("\r\n", $headers), + 'content' => json_encode($payload), + 'ignore_errors' => true, + ), + "ssl" => array( + "verify_peer" => $this->ssl_verify_peer, + "verify_peer_name" => $this->ssl_verify_peer, + ) + )); + } + /** * Return true if we have a batch response * @@ -271,11 +369,11 @@ class Client /** * Get a RPC call result * - * @access public + * @access private * @param array $payload * @return mixed */ - public function getResult(array $payload) + private function getResult(array $payload) { if (isset($payload['error']['code'])) { $this->handleRpcErrors($payload['error']); @@ -283,68 +381,4 @@ class Client return isset($payload['result']) ? $payload['result'] : null; } - - /** - * Throw an exception according the RPC error - * - * @access public - * @param integer $code - */ - public function handleRpcErrors($error) - { - switch ($error['code']) { - case -32601: - throw new BadFunctionCallException('Procedure not found: '. $error['message']); - case -32602: - throw new InvalidArgumentException('Invalid arguments: '. $error['message']); - default: - throw new RuntimeException('Invalid request/response: '. $error['message'], $error['code']); - } - } - - /** - * Do the HTTP request - * - * @access public - * @param string $payload Data to send - */ - public function doRequest($payload) - { - curl_setopt_array($this->ch, array( - CURLOPT_URL => $this->url, - CURLOPT_HEADER => false, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_CONNECTTIMEOUT => $this->timeout, - CURLOPT_USERAGENT => 'JSON-RPC PHP Client', - CURLOPT_HTTPHEADER => $this->headers, - CURLOPT_FOLLOWLOCATION => false, - CURLOPT_CUSTOMREQUEST => 'POST', - CURLOPT_SSL_VERIFYPEER => $this->ssl_verify_peer, - CURLOPT_POSTFIELDS => json_encode($payload) - )); - - if ($this->username && $this->password) { - curl_setopt($this->ch, CURLOPT_USERPWD, $this->username.':'.$this->password); - } - - $http_body = curl_exec($this->ch); - $http_code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE); - - if (curl_errno($this->ch)) { - throw new RuntimeException(curl_error($this->ch)); - } - - if ($http_code === 401 || $http_code === 403) { - throw new RuntimeException('Access denied'); - } - - $response = json_decode($http_body, true); - - if ($this->debug) { - error_log('==> Request: '.PHP_EOL.json_encode($payload, JSON_PRETTY_PRINT)); - error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT)); - } - - return is_array($response) ? $response : array(); - } } diff --git a/vendor/fguillot/json-rpc/src/JsonRPC/Server.php b/vendor/fguillot/json-rpc/src/JsonRPC/Server.php index d0dba62..20d758b 100644 --- a/vendor/fguillot/json-rpc/src/JsonRPC/Server.php +++ b/vendor/fguillot/json-rpc/src/JsonRPC/Server.php @@ -19,7 +19,6 @@ class AuthenticationFailure extends Exception {}; * * @package JsonRPC * @author Frederic Guillot - * @license Unlicense http://unlicense.org/ */ class Server { @@ -27,9 +26,9 @@ class Server * Data received from the client * * @access private - * @var string + * @var array */ - private $payload; + private $payload = array(); /** * List of procedures @@ -91,17 +90,28 @@ class Server * Constructor * * @access public - * @param string $payload Client data - * @param array $callbacks Callbacks - * @param array $classes Classes + * @param string $request */ - public function __construct($payload = '', array $callbacks = array(), array $classes = array()) + public function __construct($request = '') + { + if ($request !== '') { + $this->payload = json_decode($request, true); + } + else { + $this->payload = json_decode(file_get_contents('php://input'), true); + } + } + + /** + * Set a payload + * + * @access public + * @param array $payload + * @return Server + */ + public function setPayload(array $payload) { $this->payload = $payload; - $this->callbacks = $callbacks; - $this->classes = $classes; - - } /** @@ -215,9 +225,9 @@ class Server * @param closure $callback Callback * @return Server */ - public function register($name, Closure $callback) + public function register($procedure, Closure $callback) { - $this->callbacks[$name] = $callback; + $this->callbacks[$procedure] = $callback; return $this; } @@ -308,18 +318,10 @@ class Server /** * Parse the payload and test if the parsed JSON is ok * - * @access public + * @access private */ - public function checkJsonFormat() + private function checkJsonFormat() { - if (empty($this->payload)) { - $this->payload = file_get_contents('php://input'); - } - - if (is_string($this->payload)) { - $this->payload = json_decode($this->payload, true); - } - if (! is_array($this->payload)) { throw new InvalidJsonFormat('Malformed payload'); } @@ -328,9 +330,9 @@ class Server /** * Test if all required JSON-RPC parameters are here * - * @access public + * @access private */ - public function checkRpcFormat() + private function checkRpcFormat() { if (! isset($this->payload['jsonrpc']) || ! isset($this->payload['method']) || @@ -377,10 +379,11 @@ class Server } else { - $server = new Server($payload, $this->callbacks, $this->classes); + $server = clone($this); + $server->setPayload($payload); $response = $server->execute(); - if ($response) { + if (! empty($response)) { $responses[] = $response; } } @@ -457,6 +460,9 @@ class Server catch (AuthenticationFailure $e) { $this->sendAuthenticationFailureResponse(); } + catch (AccessDeniedException $e) { + $this->sendForbiddenResponse(); + } catch (Exception $e) { foreach ($this->exceptions as $class) { @@ -537,8 +543,13 @@ class Server $instance = is_string($class) ? new $class : $class; // Execute before action - if (! empty($this->before) && method_exists($instance, $this->before)) { - $instance->{$this->before}($this->getUsername(), $this->getPassword(), get_class($class), $method); + if (! empty($this->before)) { + if (is_callable($this->before)) { + call_user_func_array($this->before, array($this->getUsername(), $this->getPassword(), get_class($class), $method)); + } + else if (method_exists($instance, $this->before)) { + $instance->{$this->before}($this->getUsername(), $this->getPassword(), get_class($class), $method); + } } $reflection = new ReflectionMethod($class, $method); diff --git a/vendor/fguillot/json-rpc/tests/ClientTest.php b/vendor/fguillot/json-rpc/tests/ClientTest.php index c592066..a6ad5f6 100644 --- a/vendor/fguillot/json-rpc/tests/ClientTest.php +++ b/vendor/fguillot/json-rpc/tests/ClientTest.php @@ -55,6 +55,51 @@ class ClientTest extends PHPUnit_Framework_TestCase $client->parseResponse(json_decode('{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}', true)); } + /** + * @expectedException JsonRPC\ServerErrorException + */ + public function testServerError() + { + $client = new Client('http://localhost/'); + $client->handleHttpErrors(array('HTTP/1.0 301 Moved Permantenly', 'Connection: close', 'HTTP/1.1 500 Internal Server Error')); + } + + /** + * @expectedException JsonRPC\ConnectionFailureException + */ + public function testBadUrl() + { + $client = new Client('http://something_not_found/', 1); + $client->execute('plop'); + } + + /** + * @expectedException JsonRPC\ConnectionFailureException + */ + public function test404() + { + $client = new Client('http://localhost/'); + $client->handleHttpErrors(array('HTTP/1.1 404 Not Found')); + } + + /** + * @expectedException JsonRPC\AccessDeniedException + */ + public function testAccessForbiddenError() + { + $client = new Client('http://localhost/'); + $client->handleHttpErrors(array('HTTP/1.0 301 Moved Permantenly', 'Connection: close', 'HTTP/1.1 403 Forbidden')); + } + + /** + * @expectedException JsonRPC\AccessDeniedException + */ + public function testAccessNotAllowedError() + { + $client = new Client('http://localhost/'); + $client->handleHttpErrors(array('HTTP/1.0 301 Moved Permantenly', 'Connection: close', 'HTTP/1.0 401 Unauthorized')); + } + public function testPrepareRequest() { $client = new Client('http://localhost/'); diff --git a/vendor/fguillot/json-rpc/tests/ServerProtocolTest.php b/vendor/fguillot/json-rpc/tests/ServerProtocolTest.php index f3586ea..4f55dca 100644 --- a/vendor/fguillot/json-rpc/tests/ServerProtocolTest.php +++ b/vendor/fguillot/json-rpc/tests/ServerProtocolTest.php @@ -2,6 +2,14 @@ use JsonRPC\Server; +class C +{ + public function doSomething() + { + return 'something'; + } +} + class ServerProtocolTest extends PHPUnit_Framework_TestCase { public function testPositionalParameters() @@ -165,7 +173,9 @@ class ServerProtocolTest extends PHPUnit_Framework_TestCase {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"}, {"foo": "boo"}, {"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"}, - {"jsonrpc": "2.0", "method": "get_data", "id": "9"} + {"jsonrpc": "2.0", "method": "get_data", "id": "9"}, + {"jsonrpc": "2.0", "method": "doSomething", "id": 10}, + {"jsonrpc": "2.0", "method": "doStuff", "id": 15} ]'); $server->register('sum', function($a, $b, $c) { @@ -180,6 +190,10 @@ class ServerProtocolTest extends PHPUnit_Framework_TestCase return array('hello', 5); }); + $server->attach(new C); + + $server->bind('doStuff', 'C', 'doSomething'); + $response = $server->execute(); $this->assertEquals( @@ -188,7 +202,9 @@ class ServerProtocolTest extends PHPUnit_Framework_TestCase {"jsonrpc": "2.0", "result": 19, "id": "2"}, {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}, {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"}, - {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"} + {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}, + {"jsonrpc": "2.0", "result": "something", "id": "10"}, + {"jsonrpc": "2.0", "result": "something", "id": "15"} ]', true), json_decode($response, true) ); diff --git a/vendor/fguillot/picodb/.travis.yml b/vendor/fguillot/picodb/.travis.yml new file mode 100644 index 0000000..00b2b5b --- /dev/null +++ b/vendor/fguillot/picodb/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: + - 7.0 + - 5.6 + - 5.5 + - 5.4 + - 5.3 + +matrix: + fast_finish: true + allow_failures: + - php: 7.0 + +before_script: + - composer dump-autoload + +script: + - phpunit \ No newline at end of file diff --git a/vendor/fguillot/picodb/README.md b/vendor/fguillot/picodb/README.md index 69700e4..823d5c3 100644 --- a/vendor/fguillot/picodb/README.md +++ b/vendor/fguillot/picodb/README.md @@ -2,7 +2,10 @@ PicoDb ====== PicoDb is a minimalist database query builder for PHP. -**It's not an ORM**. + +[![Build Status](https://travis-ci.org/fguillot/picoDb.svg?branch=master)](https://travis-ci.org/fguillot/picoDb) + +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fguillot/picoDb/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/fguillot/picoDb/?branch=master) Features -------- @@ -11,15 +14,21 @@ Features - Supported drivers: Sqlite, Mysql, Postgresql - Requires only PDO - Use prepared statements -- Handle schema versions (migrations) +- Handle schema migrations +- Fully unit tested on PHP 5.3, 5.4, 5.5, 5.6 and 7.0 - License: MIT Requirements ------------ - PHP >= 5.3 -- PDO -- A database: Sqlite, Mysql or Postgresql +- PDO extension +- Sqlite or Mysql or Postgresql + +Author +------ + +Frédéric Guillot Documentation ------------- @@ -30,53 +39,93 @@ Documentation composer require fguillot/picodb @stable ``` -### Connect to your database +### Database connection + +Sqlite: ```php use PicoDb\Database; // Sqlite driver $db = new Database(['driver' => 'sqlite', 'filename' => ':memory:']); +``` -// Mysql driver -// Optional options: "schema_table" (the default table name is "schema_version") -$db = new Database(array( +The Sqlite driver enable foreign keys by default. + +Mysql: + +```php +// Optional attributes: +// "charset" +// "schema_table" (the default table name is "schema_version") +// "port" + +$db = new Database([ 'driver' => 'mysql', 'hostname' => 'localhost', 'username' => 'root', 'password' => '', 'database' => 'my_db_name', - 'charset' => 'utf8', -)); +]); ``` -### Execute a SQL request +Postgres: ```php -$db->execute('CREATE TABLE toto (column1 TEXT)'); +// Optional attributes: +// "schema_table" (the default table name is "schema_version") +// "port" + +$db = new Database([ + 'driver' => 'postgres', + 'hostname' => 'localhost', + 'username' => 'root', + 'password' => '', + 'database' => 'my_db_name', +]); ``` -### Insert some data +### Execute any SQL query ```php -$db->table('toto')->save(['column1' => 'test']); +$db->execute('CREATE TABLE mytable (column1 TEXT)'); +``` + +- Returns a `PDOStatement` if successful +- Returns `false` if there is a duplicate key error +- Throws a `SQLException` for other errors + +### Insertion + +```php +$db->table('mytable')->save(['column1' => 'test']); ``` or ```php -$db->table('toto')->insert(['column1' => 'test']); +$db->table('mytable')->insert(['column1' => 'test']); +``` + +### Fetch last inserted id + +```php +$db->getLastId(); ``` ### Transactions ```php $db->transaction(function ($db) { - $db->table('toto')->save(['column1' => 'foo']); - $db->table('toto')->save(['column1' => 'bar']); + $db->table('mytable')->save(['column1' => 'foo']); + $db->table('mytable')->save(['column1' => 'bar']); }); ``` +- Returns `true` if the callback returns null +- Returns the callback return value otherwise +- Throws an SQLException if something is wrong + or ```php @@ -91,55 +140,65 @@ $db->cancelTransaction(); ### Fetch all data ```php -$records = $db->table('toto')->findAll(); +$records = $db->table('mytable')->findAll(); foreach ($records as $record) { var_dump($record['column1']); } ``` -### Update something +### Updates ```php -$db->table('toto')->eq('id', 1)->save(['column1' => 'hey']); +$db->table('mytable')->eq('id', 1)->save(['column1' => 'hey']); ``` -You just need to add a condition to perform an update. - -### Remove rows +or ```php -$db->table('toto')->lowerThan('column1', 10)->remove(); +$db->table('mytable')->eq('id', 1)->update(['column1' => 'hey']); +``` + +### Remove records + +```php +$db->table('mytable')->lt('column1', 10)->remove(); ``` ### Sorting ```php -$db->table('toto')->asc('column1')->findAll(); +$db->table('mytable')->asc('column1')->findAll(); ``` or ```php -$db->table('toto')->desc('column1')->findAll(); +$db->table('mytable')->desc('column1')->findAll(); ``` or ```php -#db->table('toto')->orderBy('column1', 'ASC')->findAll(); +$db->table('mytable')->orderBy('column1', 'ASC')->findAll(); +``` + +Multiple sorting: + +```php +$db->table('mytable')->asc('column1')->desc('column2')->findAll(); ``` ### Limit and offset ```php -$db->table('toto')->limit(10)->offset(5)->findAll(); +$db->table('mytable')->limit(10)->offset(5)->findAll(); ``` ### Fetch only some columns ```php -$db->table('toto')->columns('column1', 'column2')->findAll(); +$db->table('mytable')->columns('column1', 'column2')->findAll(); ``` ### Fetch only one column @@ -147,37 +206,87 @@ $db->table('toto')->columns('column1', 'column2')->findAll(); Many rows: ```php -$db->table('toto')->findAllByColumn('column1'); +$db->table('mytable')->findAllByColumn('column1'); ``` One row: ```php -$db->table('toto')->findOneColumn('column1'); +$db->table('mytable')->findOneColumn('column1'); ``` -### Equals condition +### Custom select ```php -$db->table('toto') - ->equals('column1', 'hey') - ->findAll(); +$db->table('mytable')->select(1)->eq('id', 42)->findOne(); +``` + +### Distinct + +```php +$db->table('mytable')->distinct('columnA')->findOne(); +``` + +### Group by + +```php +$db->table('mytable')->groupBy('columnA')->findAll(); +``` + +### Count + +```php +$db->table('mytable')->count(); +``` + +### Sum + +```php +$db->table('mytable')->sum('columnB'); +``` + +### Sum column values during update + +Add the value 42 to the existing value of the column "mycolumn": + +```php +$db->table('mytable')->sumColumn('mycolumn', 42)->update(); +``` + +### Exists + +Returns true if a record exists otherwise false. + +```php +$db->table('mytable')->eq('column1', 12)->exists(); +``` + +### Left joins + +```php +// SELECT * FROM mytable LEFT JOIN my_other_table AS t1 ON t1.id=mytable.foreign_key +$db->table('mytable')->left('my_other_table', 't1', 'id', 'mytable', 'foreign_key')->findAll(); ``` or ```php -$db->table('toto') +// SELECT * FROM mytable LEFT JOIN my_other_table ON my_other_table.id=mytable.foreign_key +$db->table('mytable')->join('my_other_table', 'id', 'foreign_key')->findAll(); +``` + +### Equals condition + +```php +$db->table('mytable') ->eq('column1', 'hey') ->findAll(); ``` -Yout got: 'SELECT * FROM toto WHERE column1=?' - ### IN condition ```php -$db->table('toto') +$db->table('mytable') ->in('column1', ['hey', 'bla']) ->findAll(); ``` @@ -187,7 +296,7 @@ $db->table('toto') Case-sensitive (only Mysql and Postgres): ```php -$db->table('toto') +$db->table('mytable') ->like('column1', '%Foo%') ->findAll(); ``` @@ -195,82 +304,66 @@ $db->table('toto') Not case-sensitive: ```php -$db->table('toto') +$db->table('mytable') ->ilike('column1', '%foo%') ->findAll(); ``` -### Lower than +### Lower than condition ```php -$db->table('toto') - ->lowerThan('column1', 2) - ->findAll(); -``` - -or - -```php -$db->table('toto') +$db->table('mytable') ->lt('column1', 2) ->findAll(); ``` -### Lower than or equals +### Lower than or equal condition ```php -$db->table('toto') - ->lowerThanOrEquals('column1', 2) - ->findAll(); -``` - -or - -```php -$db->table('toto') +$db->table('mytable') ->lte('column1', 2) ->findAll(); ``` -### Greater than +### Greater than condition ```php -$db->table('toto') - ->greaterThan('column1', 3) - ->findAll(); -``` - -or - -```php -$db->table('toto') +$db->table('mytable') ->gt('column1', 3) ->findAll(); ``` -### Greater than or equals +### Greater than or equal condition ```php -$db->table('toto') - ->greaterThanOrEquals('column1', 3) - ->findAll(); -``` - -or - -```php -$db->table('toto') +$db->table('mytable') ->gte('column1', 3) ->findAll(); ``` -### Multiple conditions - -Each condition is joined by a AND. +### IS NULL condition ```php -$db->table('toto') - ->like('column2', '%toto') +$db->table('mytable') + ->isNull('column1') + ->findAll(); +``` + +### IS NOT NULL condition + +```php +$db->table('mytable') + ->notNull('column1') + ->findAll(); +``` + +### Multiple conditions + +Add conditions are joined by a `AND`. + +```php +$db->table('mytable') + ->like('column2', '%mytable') ->gte('column1', 3) ->findAll(); ``` @@ -278,9 +371,9 @@ $db->table('toto') How to make a OR condition: ```php -$db->table('toto') +$db->table('mytable') ->beginOr() - ->like('column2', '%toto') + ->like('column2', '%mytable') ->gte('column1', 3) ->closeOr() ->eq('column5', 'titi') @@ -292,7 +385,7 @@ $db->table('toto') Log generated queries: ```php -$db->log_queries = true; +$db->logQueries = true; ``` Mesure each query time: @@ -304,7 +397,7 @@ $db->stopwatch = true; Get the number of queries executed: ```php -echo $db->nb_queries; +echo $db->nbQueries; ``` Get log messages: @@ -319,19 +412,19 @@ How to use a table as a key/value store: ```php $db->execute( - 'CREATE TABLE toto ( + 'CREATE TABLE mytable ( column1 TEXT NOT NULL UNIQUE, column2 TEXT default NULL )' ); -$db->table('toto')->insert(['column1' => 'option1', 'column2' => 'value1']); +$db->table('mytable')->insert(['column1' => 'option1', 'column2' => 'value1']); ``` Add/Replace some values: ```php -$db->hashtable('toto') +$db->hashtable('mytable') ->columnKey('column1') ->columnValue('column2') ->put(['option1' => 'new value', 'option2' => 'value2'])); @@ -340,7 +433,7 @@ $db->hashtable('toto') Get all values: ```php -$result = $db->hashtable('toto')->columnKey('column1')->columnValue('column2')->get(); +$result = $db->hashtable('mytable')->columnKey('column1')->columnValue('column2')->get(); print_r($result); Array @@ -353,18 +446,18 @@ Array or ```php -$result = $db->hashtable('toto')->getAll('column1', 'column2'); +$result = $db->hashtable('mytable')->getAll('column1', 'column2'); ``` Get a specific value: ```php -$db->hashtable('toto') +$db->hashtable('mytable') ->columnKey('column1') ->columnValue('column2') ->put(['option3' => 'value3']); -$result = $db->hashtable('toto') +$result = $db->hashtable('mytable') ->columnKey('column1') ->columnValue('column2') ->get('option1', 'option3'); @@ -417,10 +510,9 @@ function version_2($pdo) #### Run schema update automatically -- The method "check()" executes all migrations until to reach the correct version number. -- If we are already on the last version nothing will happen. -- The schema version for the driver Sqlite is stored inside a variable (PRAGMA user_version) -- You can use that with a dependency injection controller. +- The method `check()` execute all migrations until the version specified +- If an error occurs, the transaction is rollbacked +- Foreign keys checks are disabled if possible during the migration Example: @@ -447,7 +539,7 @@ else { Setup a new instance: ```php -PicoDb\Database::bootstrap('myinstance', function() { +PicoDb\Database::setInstance('myinstance', function() { $db = new PicoDb\Database(array( 'driver' => 'sqlite', @@ -466,5 +558,5 @@ PicoDb\Database::bootstrap('myinstance', function() { Get this instance anywhere in your code: ```php -PicoDb\Database::get('myinstance')->table(...) +PicoDb\Database::getInstance('myinstance')->table(...) ``` diff --git a/vendor/fguillot/picodb/lib/PicoDb/Condition.php b/vendor/fguillot/picodb/lib/PicoDb/Condition.php new file mode 100644 index 0000000..b8c6de4 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Condition.php @@ -0,0 +1,291 @@ +db = $db; + } + + /** + * Build the SQL condition + * + * @access public + * @return string + */ + public function build() + { + return empty($this->conditions) ? '' : ' WHERE '.implode(' AND ', $this->conditions); + } + + /** + * Get condition values + * + * @access public + * @return array + */ + public function getValues() + { + return $this->values; + } + + /** + * Returns true if there is some conditions + * + * @access public + * @return boolean + */ + public function hasCondition() + { + return ! empty($this->conditions); + } + + /** + * Add custom condition + * + * @access public + * @param string $sql + */ + public function addCondition($sql) + { + if ($this->beginOr) { + $this->or[] = $sql; + } + else { + $this->conditions[] = $sql; + } + } + + /** + * Start OR condition + * + * @access public + */ + public function beginOr() + { + $this->beginOr = true; + $this->or = array(); + } + /** + * Close OR condition + * + * @access public + */ + public function closeOr() + { + $this->beginOr = false; + + if (! empty($this->or)) { + $this->conditions[] = '('.implode(' OR ', $this->or).')'; + } + } + + /** + * Equal condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function eq($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' = ?'); + $this->values[] = $value; + } + + /** + * Not equal condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function neq($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' != ?'); + $this->values[] = $value; + } + + /** + * IN condition + * + * @access public + * @param string $column + * @param array $values + */ + public function in($column, array $values) + { + if (! empty($values)) { + $this->addCondition($this->db->escapeIdentifier($column).' IN ('.implode(', ', array_fill(0, count($values), '?')).')'); + $this->values = array_merge($this->values, $values); + } + } + + /** + * NOT IN condition + * + * @access public + * @param string $column + * @param array $values + */ + public function notin($column, array $values) + { + if (! empty($values)) { + $this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.implode(', ', array_fill(0, count($values), '?')).')'); + $this->values = array_merge($this->values, $values); + } + } + + /** + * LIKE condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function like($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('LIKE').' ?'); + $this->values[] = $value; + } + + /** + * ILIKE condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function ilike($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('ILIKE').' ?'); + $this->values[] = $value; + } + + /** + * Greater than condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function gt($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' > ?'); + $this->values[] = $value; + } + + /** + * Lower than condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function lt($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' < ?'); + $this->values[] = $value; + } + + /** + * Greater than or equals condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function gte($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' >= ?'); + $this->values[] = $value; + } + + /** + * Lower than or equals condition + * + * @access public + * @param string $column + * @param mixed $value + */ + public function lte($column, $value) + { + $this->addCondition($this->db->escapeIdentifier($column).' <= ?'); + $this->values[] = $value; + } + + /** + * IS NULL condition + * + * @access public + * @param string $column + */ + public function isNull($column) + { + $this->addCondition($this->db->escapeIdentifier($column).' IS NULL'); + } + + /** + * IS NOT NULL condition + * + * @access public + * @param string $column + */ + public function notNull($column) + { + $this->addCondition($this->db->escapeIdentifier($column).' IS NOT NULL'); + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Database.php b/vendor/fguillot/picodb/lib/PicoDb/Database.php index e02fd13..d3e4ab9 100644 --- a/vendor/fguillot/picodb/lib/PicoDb/Database.php +++ b/vendor/fguillot/picodb/lib/PicoDb/Database.php @@ -3,21 +3,24 @@ namespace PicoDb; use Closure; -use PDO; use PDOException; use LogicException; -use RuntimeException; -use Picodb\Driver\Sqlite; -use Picodb\Driver\Mysql; -use Picodb\Driver\Postgres; +use PicoDb\Driver\Sqlite; +use PicoDb\Driver\Mysql; +use PicoDb\Driver\Postgres; +/** + * Database + * + * @author Frederic Guillot + */ class Database { /** * Database instances * - * @access private * @static + * @access private * @var array */ private static $instances = array(); @@ -31,12 +34,11 @@ class Database private $logs = array(); /** - * PDO instance + * Driver instance * * @access private - * @var PDO */ - private $pdo; + private $driver; /** * Flag to calculate query time @@ -52,7 +54,7 @@ class Database * @access public * @var boolean */ - public $log_queries = false; + public $logQueries = false; /** * Number of SQL queries executed @@ -60,10 +62,10 @@ class Database * @access public * @var integer */ - public $nb_queries = 0; + public $nbQueries = 0; /** - * Constructor, iniatlize a PDO driver + * Initialize the driver * * @access public * @param array $settings Connection settings @@ -71,31 +73,22 @@ class Database public function __construct(array $settings) { if (! isset($settings['driver'])) { - throw new LogicException('You must define a database driver.'); + throw new LogicException('You must define a database driver'); } switch ($settings['driver']) { - case 'sqlite': - require_once __DIR__.'/Driver/Sqlite.php'; - $this->pdo = new Sqlite($settings); + $this->driver = new Sqlite($settings); break; - case 'mysql': - require_once __DIR__.'/Driver/Mysql.php'; - $this->pdo = new Mysql($settings); + $this->driver = new Mysql($settings); break; - case 'postgres': - require_once __DIR__.'/Driver/Postgres.php'; - $this->pdo = new Postgres($settings); + $this->driver = new Postgres($settings); break; - default: - throw new LogicException('This database driver is not supported.'); + throw new LogicException('This database driver is not supported'); } - - $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } /** @@ -116,7 +109,7 @@ class Database * @param string $name Instance name * @param Closure $callback Callback */ - public static function bootstrap($name, Closure $callback) + public static function setInstance($name, Closure $callback) { self::$instances[$name] = $callback; } @@ -129,10 +122,10 @@ class Database * @param string $name Instance name * @return Database */ - public static function get($name) + public static function getInstance($name) { if (! isset(self::$instances[$name])) { - throw new LogicException('No database instance created with that name.'); + throw new LogicException('No database instance created with that name'); } if (is_callable(self::$instances[$name])) { @@ -168,11 +161,33 @@ class Database * Get the PDO connection * * @access public - * @return PDO + * @return \PDO */ public function getConnection() { - return $this->pdo; + return $this->driver->getConnection(); + } + + /** + * Get the Driver instance + * + * @access public + * @return Sqlite|Postgres|Mysql + */ + public function getDriver() + { + return $this->driver; + } + + /** + * Get the last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return $this->driver->getLastId(); } /** @@ -182,7 +197,7 @@ class Database */ public function closeConnection() { - $this->pdo = null; + $this->driver->closeConnection(); } /** @@ -201,25 +216,44 @@ class Database } if (! empty($table)) { - return $this->pdo->escapeIdentifier($table).'.'.$this->pdo->escapeIdentifier($value); + return $this->driver->escape($table).'.'.$this->driver->escape($value); } - return $this->pdo->escapeIdentifier($value); + return $this->driver->escape($value); + } + + /** + * Escape an identifier list + * + * @access public + * @param array $identifiers List of identifiers + * @param string $table Table name + * @return string[] + */ + public function escapeIdentifierList(array $identifiers, $table = '') + { + foreach ($identifiers as $key => $value) { + $identifiers[$key] = $this->escapeIdentifier($value, $table); + } + + return $identifiers; } /** * Execute a prepared statement * + * Note: returns false on duplicate keys instead of SQLException + * * @access public * @param string $sql SQL query * @param array $values Values - * @return PDOStatement|false + * @return \PDOStatement|false */ public function execute($sql, array $values = array()) { try { - if ($this->log_queries) { + if ($this->logQueries) { $this->setLogMessage($sql); } @@ -227,28 +261,19 @@ class Database $start = microtime(true); } - $rq = $this->pdo->prepare($sql); + $rq = $this->getConnection()->prepare($sql); $rq->execute($values); if ($this->stopwatch) { $this->setLogMessage('DURATION='.(microtime(true) - $start)); } - $this->nb_queries++; + $this->nbQueries++; return $rq; } catch (PDOException $e) { - - $this->cancelTransaction(); - - if (in_array($e->getCode(), $this->pdo->getDuplicateKeyErrorCode())) { - return false; - } - - $this->setLogMessage($e->getMessage()); - - throw new RuntimeException('SQL error'.($this->log_queries ? ': '.$e->getMessage() : '')); + return $this->handleSqlError($e); } } @@ -261,11 +286,37 @@ class Database */ public function transaction(Closure $callback) { - $this->pdo->beginTransaction(); - $result = $callback($this); // Rollback is done in the execute() method - $this->closeTransaction(); + try { - return $result === null ? true : $result; + $this->startTransaction(); + $result = $callback($this); + $this->closeTransaction(); + + return $result === null ? true : $result; + } + catch (PDOException $e) { + return $this->handleSqlError($e); + } + } + + /** + * Handle PDOException + * + * @access private + * @param PDOException $e + * @return bool + * @throws SQLException + */ + private function handleSqlError(PDOException $e) + { + $this->cancelTransaction(); + $this->setLogMessage($e->getMessage()); + + if ($this->driver->isDuplicateKeyError($e->getCode())) { + return false; + } + + throw new SQLException('SQL error'.($this->logQueries ? ': '.$e->getMessage() : '')); } /** @@ -275,8 +326,8 @@ class Database */ public function startTransaction() { - if (! $this->pdo->inTransaction()) { - $this->pdo->beginTransaction(); + if (! $this->getConnection()->inTransaction()) { + $this->getConnection()->beginTransaction(); } } @@ -287,8 +338,8 @@ class Database */ public function closeTransaction() { - if ($this->pdo->inTransaction()) { - $this->pdo->commit(); + if ($this->getConnection()->inTransaction()) { + $this->getConnection()->commit(); } } @@ -299,8 +350,8 @@ class Database */ public function cancelTransaction() { - if ($this->pdo->inTransaction()) { - $this->pdo->rollback(); + if ($this->getConnection()->inTransaction()) { + $this->getConnection()->rollback(); } } @@ -308,11 +359,11 @@ class Database * Get a table instance * * @access public - * @return Picodb\Table + * @param string $table_name + * @return Table */ public function table($table_name) { - require_once __DIR__.'/Table.php'; return new Table($this, $table_name); } @@ -320,12 +371,11 @@ class Database * Get a hashtable instance * * @access public - * @return Picodb\Hashtable + * @param string $table_name + * @return Hashtable */ public function hashtable($table_name) { - require_once __DIR__.'/Table.php'; - require_once __DIR__.'/Hashtable.php'; return new Hashtable($this, $table_name); } @@ -333,11 +383,10 @@ class Database * Get a schema instance * * @access public - * @return Picodb\Schema + * @return Schema */ public function schema() { - require_once __DIR__.'/Schema.php'; return new Schema($this); } } \ No newline at end of file diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php new file mode 100644 index 0000000..fd6aa40 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Base.php @@ -0,0 +1,192 @@ +requiredAtttributes as $attribute) { + if (! isset($settings[$attribute])) { + throw new LogicException('This configuration parameter is missing: "'.$attribute.'"'); + } + } + + $this->createConnection($settings); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + + /** + * Get the PDO connection + * + * @access public + * @return PDO + */ + public function getConnection() + { + return $this->pdo; + } + + /** + * Release the PDO connection + * + * @access public + */ + public function closeConnection() + { + $this->pdo = null; + } + + /** + * Upsert for a key/value variable + * + * @access public + * @param string $table + * @param string $keyColumn + * @param string $valueColumn + * @param array $dictionary + * @return bool False on failure + */ + public function upsert($table, $keyColumn, $valueColumn, array $dictionary) + { + try { + $this->pdo->beginTransaction(); + + foreach ($dictionary as $key => $value) { + + $rq = $this->pdo->prepare('SELECT 1 FROM '.$this->escape($table).' WHERE '.$this->escape($keyColumn).'=?'); + $rq->execute(array($key)); + + if ($rq->fetchColumn()) { + $rq = $this->pdo->prepare('UPDATE '.$this->escape($table).' SET '.$this->escape($valueColumn).'=? WHERE '.$this->escape($keyColumn).'=?'); + $rq->execute(array($value, $key)); + } + else { + $rq = $this->pdo->prepare('INSERT INTO '.$this->escape($table).' ('.$this->escape($keyColumn).', '.$this->escape($valueColumn).') VALUES (?, ?)'); + $rq->execute(array($key, $value)); + } + } + + $this->pdo->commit(); + + return true; + } + catch (PDOException $e) { + $this->pdo->rollback(); + return false; + } + } +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php index c90a49c..e1ae73b 100644 --- a/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Mysql.php @@ -3,29 +3,46 @@ namespace PicoDb\Driver; use PDO; -use LogicException; +use PDOException; -class Mysql extends PDO +/** + * Mysql Driver + * + * @author Frederic Guillot + */ +class Mysql extends Base { - private $schema_table = 'schema_version'; + /** + * List of required settings options + * + * @access protected + * @var array + */ + protected $requiredAtttributes = array( + 'hostname', + 'username', + 'password', + 'database', + ); - public function __construct(array $settings) + /** + * Table to store the schema version + * + * @access private + * @var array + */ + private $schemaTable = 'schema_version'; + + /** + * Create a new PDO connection + * + * @access public + * @param array $settings + */ + public function createConnection(array $settings) { - $required_atttributes = array( - 'hostname', - 'username', - 'password', - 'database', - 'charset', - ); - - foreach ($required_atttributes as $attribute) { - if (! isset($settings[$attribute])) { - throw new LogicException('This configuration parameter is missing: "'.$attribute.'"'); - } - } - - $dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$settings['charset']; + $charset = empty($settings['charset']) ? 'utf8' : $settings['charset']; + $dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$charset; if (! empty($settings['port'])) { $dsn .= ';port='.$settings['port']; @@ -35,59 +52,159 @@ class Mysql extends PDO PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES', ); - parent::__construct($dsn, $settings['username'], $settings['password'], $options); + $this->pdo = new PDO($dsn, $settings['username'], $settings['password'], $options); if (isset($settings['schema_table'])) { - $this->schema_table = $settings['schema_table']; + $this->schemaTable = $settings['schema_table']; } } + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + $this->pdo->exec('SET FOREIGN_KEY_CHECKS=1'); + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + $this->pdo->exec('SET FOREIGN_KEY_CHECKS=0'); + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 23000; + } + + /** + * Escape identifier + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '`'.$identifier.'`'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE') { + return 'LIKE BINARY'; + } + else if ($operator === 'ILIKE') { + return 'LIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + return $this->pdo->lastInsertId(); + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ public function getSchemaVersion() { - $this->exec("CREATE TABLE IF NOT EXISTS `".$this->schema_table."` (`version` INT DEFAULT '0')"); + $this->pdo->exec("CREATE TABLE IF NOT EXISTS `".$this->schemaTable."` (`version` INT DEFAULT '0')"); - $rq = $this->prepare('SELECT `version` FROM `'.$this->schema_table.'`'); + $rq = $this->pdo->prepare('SELECT `version` FROM `'.$this->schemaTable.'`'); $rq->execute(); - $result = $rq->fetch(PDO::FETCH_ASSOC); + $result = $rq->fetchColumn(); - if (isset($result['version'])) { - return (int) $result['version']; + if ($result !== false) { + return (int) $result; } else { - $this->exec('INSERT INTO `'.$this->schema_table.'` VALUES(0)'); + $this->pdo->exec('INSERT INTO `'.$this->schemaTable.'` VALUES(0)'); } return 0; } + /** + * Set current schema version + * + * @access public + * @param integer $version + */ public function setSchemaVersion($version) { - $rq = $this->prepare('UPDATE `'.$this->schema_table.'` SET `version`=?'); + $rq = $this->pdo->prepare('UPDATE `'.$this->schemaTable.'` SET `version`=?'); $rq->execute(array($version)); } - public function getLastId() + /** + * Upsert for a key/value variable + * + * @access public + * @param string $table + * @param string $keyColumn + * @param string $valueColumn + * @param array $dictionary + * @return bool False on failure + */ + public function upsert($table, $keyColumn, $valueColumn, array $dictionary) { - return $this->lastInsertId(); - } + try { - public function escapeIdentifier($value) - { - return '`'.$value.'`'; - } + $sql = sprintf( + 'REPLACE INTO %s (%s, %s) VALUES %s', + $this->escape($table), + $this->escape($keyColumn), + $this->escape($valueColumn), + implode(', ', array_fill(0, count($dictionary), '(?, ?)')) + ); - public function operatorLikeCaseSensitive() - { - return 'LIKE BINARY'; - } + $values = array(); - public function operatorLikeNotCaseSensitive() - { - return 'LIKE'; - } + foreach ($dictionary as $key => $value) { + $values[] = $key; + $values[] = $value; + } - public function getDuplicateKeyErrorCode() - { - return array(23000); + $rq = $this->pdo->prepare($sql); + $rq->execute($values); + + return true; + } + catch (PDOException $e) { + return false; + } } -} \ No newline at end of file +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php index 71aa638..4259101 100644 --- a/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Postgres.php @@ -3,88 +3,170 @@ namespace PicoDb\Driver; use PDO; -use LogicException; +use PDOException; -class Postgres extends PDO +/** + * Postgres Driver + * + * @author Frederic Guillot + */ +class Postgres extends Base { - private $schema_table = 'schema_version'; + /** + * List of required settings options + * + * @access protected + * @var array + */ + protected $requiredAtttributes = array( + 'hostname', + 'username', + 'password', + 'database', + ); - public function __construct(array $settings) + /** + * Table to store the schema version + * + * @access private + * @var array + */ + private $schemaTable = 'schema_version'; + + /** + * Create a new PDO connection + * + * @access public + * @param array $settings + */ + public function createConnection(array $settings) { - $required_atttributes = array( - 'hostname', - 'username', - 'password', - 'database', - ); - - foreach ($required_atttributes as $attribute) { - if (! isset($settings[$attribute])) { - throw new LogicException('This configuration parameter is missing: "'.$attribute.'"'); - } - } - $dsn = 'pgsql:host='.$settings['hostname'].';dbname='.$settings['database']; if (! empty($settings['port'])) { $dsn .= ';port='.$settings['port']; } - parent::__construct($dsn, $settings['username'], $settings['password']); + $this->pdo = new PDO($dsn, $settings['username'], $settings['password']); if (isset($settings['schema_table'])) { - $this->schema_table = $settings['schema_table']; + $this->schemaTable = $settings['schema_table']; } } + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 23505 || $code == 23503; + } + + /** + * Escape identifier + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '"'.$identifier.'"'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE') { + return 'LIKE'; + } + else if ($operator === 'ILIKE') { + return 'ILIKE'; + } + + return ''; + } + + /** + * Get last inserted id + * + * @access public + * @return integer + */ + public function getLastId() + { + try { + $rq = $this->pdo->prepare('SELECT LASTVAL()'); + $rq->execute(); + + return $rq->fetchColumn(); + } + catch (PDOException $e) { + return 0; + } + } + + /** + * Get current schema version + * + * @access public + * @return integer + */ public function getSchemaVersion() { - $this->exec("CREATE TABLE IF NOT EXISTS ".$this->schema_table." (version SMALLINT DEFAULT 0)"); + $this->pdo->exec("CREATE TABLE IF NOT EXISTS ".$this->schemaTable." (version INTEGER DEFAULT 0)"); - $rq = $this->prepare('SELECT version FROM '.$this->schema_table.''); + $rq = $this->pdo->prepare('SELECT "version" FROM "'.$this->schemaTable.'"'); $rq->execute(); - $result = $rq->fetch(PDO::FETCH_ASSOC); + $result = $rq->fetchColumn(); - if (isset($result['version'])) { - return (int) $result['version']; + if ($result !== false) { + return (int) $result; } else { - $this->exec('INSERT INTO '.$this->schema_table.' VALUES(0)'); + $this->pdo->exec('INSERT INTO '.$this->schemaTable.' VALUES(0)'); } return 0; } + /** + * Set current schema version + * + * @access public + * @param integer $version + */ public function setSchemaVersion($version) { - $rq = $this->prepare('UPDATE '.$this->schema_table.' SET version=?'); + $rq = $this->pdo->prepare('UPDATE '.$this->schemaTable.' SET version=?'); $rq->execute(array($version)); } - - public function getLastId() - { - $rq = $this->prepare('SELECT LASTVAL()'); - $rq->execute(); - return $rq->fetchColumn(); - } - - public function escapeIdentifier($value) - { - return '"'.$value.'"'; - } - - public function operatorLikeCaseSensitive() - { - return 'LIKE'; - } - - public function operatorLikeNotCaseSensitive() - { - return 'ILIKE'; - } - - public function getDuplicateKeyErrorCode() - { - return array(23505, 23503); - } -} \ No newline at end of file +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php index 0943d15..67881ee 100644 --- a/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php +++ b/vendor/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php @@ -3,67 +3,166 @@ namespace PicoDb\Driver; use PDO; -use LogicException; +use PDOException; -class Sqlite extends PDO +/** + * Sqlite Driver + * + * @author Frederic Guillot + */ +class Sqlite extends Base { - public function __construct(array $settings) - { - $required_atttributes = array( - 'filename', - ); + /** + * List of required settings options + * + * @access protected + * @var array + */ + protected $requiredAtttributes = array('filename'); - foreach ($required_atttributes as $attribute) { - if (! isset($settings[$attribute])) { - throw new LogicException('This configuration parameter is missing: "'.$attribute.'"'); - } + /** + * Create a new PDO connection + * + * @access public + * @param array $settings + */ + public function createConnection(array $settings) + { + $this->pdo = new PDO('sqlite:'.$settings['filename']); + $this->enableForeignKeys(); + } + + /** + * Enable foreign keys + * + * @access public + */ + public function enableForeignKeys() + { + $this->pdo->exec('PRAGMA foreign_keys = ON'); + } + + /** + * Disable foreign keys + * + * @access public + */ + public function disableForeignKeys() + { + $this->pdo->exec('PRAGMA foreign_keys = OFF'); + } + + /** + * Return true if the error code is a duplicate key + * + * @access public + * @param integer $code + * @return boolean + */ + public function isDuplicateKeyError($code) + { + return $code == 23000; + } + + /** + * Escape identifier + * + * @access public + * @param string $identifier + * @return string + */ + public function escape($identifier) + { + return '"'.$identifier.'"'; + } + + /** + * Get non standard operator + * + * @access public + * @param string $operator + * @return string + */ + public function getOperator($operator) + { + if ($operator === 'LIKE' || $operator === 'ILIKE') { + return 'LIKE'; } - parent::__construct('sqlite:'.$settings['filename']); - - $this->exec('PRAGMA foreign_keys = ON'); - } - - public function getSchemaVersion() - { - $rq = $this->prepare('PRAGMA user_version'); - $rq->execute(); - $result = $rq->fetch(PDO::FETCH_ASSOC); - - if (isset($result['user_version'])) { - return (int) $result['user_version']; - } - - return 0; - } - - public function setSchemaVersion($version) - { - $this->exec('PRAGMA user_version='.$version); + return ''; } + /** + * Get last inserted id + * + * @access public + * @return integer + */ public function getLastId() { - return $this->lastInsertId(); + return $this->pdo->lastInsertId(); } - public function escapeIdentifier($value) + /** + * Get current schema version + * + * @access public + * @return integer + */ + public function getSchemaVersion() { - return '"'.$value.'"'; + $rq = $this->pdo->prepare('PRAGMA user_version'); + $rq->execute(); + + return (int) $rq->fetchColumn(); } - public function operatorLikeCaseSensitive() + /** + * Set current schema version + * + * @access public + * @param integer $version + */ + public function setSchemaVersion($version) { - return 'LIKE'; + $this->pdo->exec('PRAGMA user_version='.$version); } - public function operatorLikeNotCaseSensitive() + /** + * Upsert for a key/value variable + * + * @access public + * @param string $table + * @param string $keyColumn + * @param string $valueColumn + * @param array $dictionary + * @return bool False on failure + */ + public function upsert($table, $keyColumn, $valueColumn, array $dictionary) { - return 'LIKE'; - } + try { + $this->pdo->beginTransaction(); - public function getDuplicateKeyErrorCode() - { - return array(23000); + foreach ($dictionary as $key => $value) { + + $sql = sprintf( + 'INSERT OR REPLACE INTO %s (%s, %s) VALUES (?, ?)', + $this->escape($table), + $this->escape($keyColumn), + $this->escape($valueColumn) + ); + + $rq = $this->pdo->prepare($sql); + $rq->execute(array($key, $value)); + } + + $this->pdo->commit(); + + return true; + } + catch (PDOException $e) { + $this->pdo->rollback(); + return false; + } } -} \ No newline at end of file +} diff --git a/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php b/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php index b904c97..2121cfc 100644 --- a/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php +++ b/vendor/fguillot/picodb/lib/PicoDb/Hashtable.php @@ -4,21 +4,40 @@ namespace PicoDb; use PDO; +/** + * Hashtable (key/value) + * + * @author Frederic Guillot + * @author Mathias Kresin + */ class Hashtable extends Table { - private $column_key = 'key'; - private $column_value = 'value'; + /** + * Column for the key + * + * @access private + * @var string + */ + private $keyColumn = 'key'; + + /** + * Column for the value + * + * @access private + * @var string + */ + private $valueColumn = 'value'; /** * Set the key column * * @access public - * @param string $column - * @return \PicoDb\Table + * @param string $column + * @return Table */ public function columnKey($column) { - $this->column_key = $column; + $this->keyColumn = $column; return $this; } @@ -26,12 +45,12 @@ class Hashtable extends Table * Set the value column * * @access public - * @param string $column - * @return \PicoDb\Table + * @param string $column + * @return Table */ public function columnValue($column) { - $this->column_value = $column; + $this->valueColumn = $column; return $this; } @@ -39,64 +58,12 @@ class Hashtable extends Table * Insert or update * * @access public - * @param array $data + * @param array $hashmap * @return boolean */ - public function put(array $data) + public function put(array $hashmap) { - switch ($this->db->getConnection()->getAttribute(PDO::ATTR_DRIVER_NAME)) { - case 'mysql': - $sql = sprintf( - 'REPLACE INTO %s (%s) VALUES %s', - $this->db->escapeIdentifier($this->table_name), - "$this->column_key, $this->column_value", - implode(', ', array_fill(0, count($data), '(?, ?)')) - ); - - foreach ($data as $key => $value) { - $this->values[] = $key; - $this->values[] = $value; - } - - $this->db->execute($sql, $this->values); - break; - case 'sqlite': // multi insert (see mysql) requires sqlite library > 3.7.11 (bundled with PHP 5.5.11+) - // all or nothing - $this->db->startTransaction(); - - foreach($data as $key => $value) { - $sql = sprintf( - 'INSERT OR REPLACE INTO %s (%s) VALUES (?, ?)', - $this->db->escapeIdentifier($this->table_name), - $this->db->escapeIdentifier($this->column_key).', '.$this->db->escapeIdentifier($this->column_value) - ); - - $this->db->execute($sql, array($key, $value)); - } - - break; - default: // no upsert available, need to make a select/update/insert limbo - // all or nothing - $this->db->startTransaction(); - - foreach($data as $key => $value) { - // check if the key exists - $this->eq($this->column_key, $key); - - if ($this->count() === 1) { - // update the record - $this->update(array($this->column_key => $key, $this->column_value => $value)); - } - else { - // insert the record - $this->insert(array($this->column_key => $key, $this->column_value => $value)); - } - } - } - - $this->db->closeTransaction(); - - return true; + return $this->db->getDriver()->upsert($this->getName(), $this->keyColumn, $this->valueColumn, $hashmap); } /** @@ -111,13 +78,13 @@ class Hashtable extends Table // setup where condition if (func_num_args() > 0) { - $this->in($this->column_key, func_get_args()); + $this->in($this->keyColumn, func_get_args()); } // setup to select columns in case that there are more than two - $this->columns($this->column_key, $this->column_value); + $this->columns($this->keyColumn, $this->valueColumn); - $rq = $this->db->execute($this->buildSelectQuery(), $this->values); + $rq = $this->db->execute($this->buildSelectQuery(), $this->condition->getValues()); $rows = $rq->fetchAll(PDO::FETCH_NUM); foreach ($rows as $row) { @@ -137,8 +104,8 @@ class Hashtable extends Table */ public function getAll($key, $value) { - $this->column_key = $key; - $this->column_value = $value; + $this->keyColumn = $key; + $this->valueColumn = $value; return $this->get(); } } diff --git a/vendor/fguillot/picodb/lib/PicoDb/SQLException.php b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php new file mode 100644 index 0000000..4d5cd68 --- /dev/null +++ b/vendor/fguillot/picodb/lib/PicoDb/SQLException.php @@ -0,0 +1,14 @@ +db = $db; } + /** + * Check the schema version and run the migrations + * + * @access public + * @param integer $last_version + * @return boolean + */ public function check($last_version = 1) { - $current_version = $this->db->getConnection()->getSchemaVersion(); + $current_version = $this->db->getDriver()->getSchemaVersion(); if ($current_version < $last_version) { return $this->migrateTo($current_version, $last_version); @@ -24,11 +48,20 @@ class Schema return true; } + /** + * Migrate the schema to one version to another + * + * @access public + * @param integer $current_version + * @param integer $next_version + * @return boolean + */ public function migrateTo($current_version, $next_version) { try { $this->db->startTransaction(); + $this->db->getDriver()->disableForeignKeys(); for ($i = $current_version + 1; $i <= $next_version; $i++) { @@ -36,15 +69,17 @@ class Schema if (function_exists($function_name)) { call_user_func($function_name, $this->db->getConnection()); - $this->db->getConnection()->setSchemaVersion($i); } } + $this->db->getDriver()->setSchemaVersion($i - 1); + $this->db->getDriver()->enableForeignKeys(); $this->db->closeTransaction(); } catch (PDOException $e) { $this->db->setLogMessage($function_name.' => '.$e->getMessage()); $this->db->cancelTransaction(); + $this->db->getDriver()->enableForeignKeys(); return false; } diff --git a/vendor/fguillot/picodb/lib/PicoDb/Table.php b/vendor/fguillot/picodb/lib/PicoDb/Table.php index e6f145f..ca8dd69 100644 --- a/vendor/fguillot/picodb/lib/PicoDb/Table.php +++ b/vendor/fguillot/picodb/lib/PicoDb/Table.php @@ -3,45 +3,167 @@ namespace PicoDb; use PDO; +use Closure; +/** + * Table + * + * @author Frederic Guillot + * + * @method Table addCondition($sql) + * @method Table beginOr() + * @method Table closeOr() + * @method Table eq($column, $value) + * @method Table neq($column, $value) + * @method Table in($column, array $values) + * @method Table notin($column, array $values) + * @method Table like($column, $value) + * @method Table ilike($column, $value) + * @method Table gt($column, $value) + * @method Table lt($column, $value) + * @method Table gte($column, $value) + * @method Table lte($column, $value) + * @method Table isNull($column) + * @method Table notNull($column) + */ class Table { + /** + * Sorting direction + * + * @access public + * @var string + */ const SORT_ASC = 'ASC'; const SORT_DESC = 'DESC'; - protected $db; - protected $table_name = ''; - protected $values = array(); + /** + * Condition instance + * + * @access public + * @var Condition + */ + public $condition; + /** + * Database instance + * + * @access protected + * @var Database + */ + protected $db; + + /** + * Table name + * + * @access protected + * @var string + */ + protected $name = ''; + + /** + * Columns list for SELECT query + * + * @access private + * @var array + */ private $columns = array(); - private $sql_limit = ''; - private $sql_offset = ''; - private $sql_order = ''; + /** + * Columns to sum during update + * + * @access private + * @var array + */ + private $sumColumns = array(); + /** + * SQL limit + * + * @access private + * @var string + */ + private $sqlLimit = ''; + + /** + * SQL offset + * + * @access private + * @var string + */ + private $sqlOffset = ''; + + /** + * SQL order + * + * @access private + * @var string + */ + private $sqlOrder = ''; + + /** + * SQL custom SELECT value + * + * @access private + * @var string + */ + private $sqlSelect = ''; + + /** + * SQL joins + * + * @access private + * @var array + */ private $joins = array(); - private $condition = ''; - private $conditions = array(); - private $or_conditions = array(); - private $is_or_condition = false; - + /** + * Use DISTINCT or not? + * + * @access private + * @var boolean + */ private $distinct = false; - private $group_by = array(); - private $filter_callback = null; + /** + * Group by those columns + * + * @access private + * @var array + */ + private $groupBy = array(); + + /** + * Callback for result filtering + * + * @access private + * @var Closure + */ + private $callback = null; /** * Constructor * * @access public - * @param \PicoDb\Database $db - * @param string $table_name + * @param Database $db + * @param string $name */ - public function __construct(Database $db, $table_name) + public function __construct(Database $db, $name) { $this->db = $db; - $this->table_name = $table_name; + $this->name = $name; + $this->condition = new Condition($db); + } + + /** + * Return the table name + * + * @access public + * @return string + */ + public function getName() + { + return $this->name; } /** @@ -53,12 +175,7 @@ class Table */ public function save(array $data) { - if (! empty($this->conditions)) { - return $this->update($data); - } - else { - return $this->insert($data); - } + return $this->condition->hasCondition() ? $this->update($data) : $this->insert($data); } /** @@ -70,25 +187,34 @@ class Table * @param array $data * @return boolean */ - public function update(array $data) + public function update(array $data = array()) { $columns = array(); $values = array(); + // Split columns and values foreach ($data as $column => $value) { $columns[] = $this->db->escapeIdentifier($column).'=?'; $values[] = $value; } - foreach ($this->values as $value) { + // Sum columns + foreach ($this->sumColumns as $column => $value) { + $columns[] = $this->db->escapeIdentifier($column).'='.$this->db->escapeIdentifier($column).' + ?'; $values[] = $value; } + // Append condition values + foreach ($this->condition->getValues() as $value) { + $values[] = $value; + } + + // Build SQL query $sql = sprintf( 'UPDATE %s SET %s %s', - $this->db->escapeIdentifier($this->table_name), + $this->db->escapeIdentifier($this->name), implode(', ', $columns), - $this->buildCondition() + $this->condition->build() ); return $this->db->execute($sql, $values) !== false; @@ -111,7 +237,7 @@ class Table $sql = sprintf( 'INSERT INTO %s (%s) VALUES (%s)', - $this->db->escapeIdentifier($this->table_name), + $this->db->escapeIdentifier($this->name), implode(', ', $columns), implode(', ', array_fill(0, count($data), '?')) ); @@ -129,27 +255,14 @@ class Table { $sql = sprintf( 'DELETE FROM %s %s', - $this->db->escapeIdentifier($this->table_name), - $this->buildCondition() + $this->db->escapeIdentifier($this->name), + $this->condition->build() ); - $result = $this->db->execute($sql, $this->values); + $result = $this->db->execute($sql, $this->condition->getValues()); return $result->rowCount() > 0; } - /** - * Add callback to alter the resultset - * - * @access public - * @param array|callable $callback - * @return \PicoDb\Table - */ - public function filter($callback) - { - $this->filter_callback = $callback; - return $this; - } - /** * Fetch all rows * @@ -158,11 +271,11 @@ class Table */ public function findAll() { - $rq = $this->db->execute($this->buildSelectQuery(), $this->values); + $rq = $this->db->execute($this->buildSelectQuery(), $this->condition->getValues()); $results = $rq->fetchAll(PDO::FETCH_ASSOC); - if (is_callable($this->filter_callback) && ! empty($results)) { - return call_user_func($this->filter_callback, $results); + if (is_callable($this->callback) && ! empty($results)) { + return call_user_func($this->callback, $results); } return $results; @@ -173,12 +286,12 @@ class Table * * @access public * @param string $column - * @return boolean + * @return mixed */ public function findAllByColumn($column) { $this->columns = array($column); - $rq = $this->db->execute($this->buildSelectQuery(), $this->values); + $rq = $this->db->execute($this->buildSelectQuery(), $this->condition->getValues()); return $rq->fetchAll(PDO::FETCH_COLUMN, 0); } @@ -187,8 +300,7 @@ class Table * Fetch one row * * @access public - * @param array $data - * @return boolean + * @return array|null */ public function findOne() { @@ -210,35 +322,40 @@ class Table $this->limit(1); $this->columns = array($column); - $rq = $this->db->execute($this->buildSelectQuery(), $this->values); - - return $rq->fetchColumn(); + return $this->db->execute($this->buildSelectQuery(), $this->condition->getValues())->fetchColumn(); } /** - * Build a select query + * Build a subquery with an alias * * @access public - * @return string + * @param string $sql + * @param string $alias + * @return Table */ - public function buildSelectQuery() + public function subquery($sql, $alias) { - foreach ($this->columns as $key => $value) { - $this->columns[$key] = $this->db->escapeIdentifier($value, $this->table_name); - } + $this->columns[] = '('.$sql.') AS '.$this->db->escapeIdentifier($alias); + return $this; + } - return sprintf( - 'SELECT %s %s FROM %s %s %s %s %s %s %s', - $this->distinct ? 'DISTINCT' : '', - empty($this->columns) ? '*' : implode(', ', $this->columns), - $this->db->escapeIdentifier($this->table_name), - implode(' ', $this->joins), - $this->buildCondition(), - empty($this->group_by) ? '' : 'GROUP BY '.implode(', ', $this->group_by), - $this->sql_order, - $this->sql_limit, - $this->sql_offset + /** + * Exists + * + * @access public + * @return integer + */ + public function exists() + { + $sql = sprintf( + 'SELECT 1 FROM %s '.implode(' ', $this->joins).$this->condition->build(), + $this->db->escapeIdentifier($this->name) ); + + $rq = $this->db->execute($sql, $this->condition->getValues()); + $result = $rq->fetchColumn(); + + return $result ? true : false; } /** @@ -250,11 +367,11 @@ class Table public function count() { $sql = sprintf( - 'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->buildCondition().$this->sql_order.$this->sql_limit.$this->sql_offset, - $this->db->escapeIdentifier($this->table_name) + 'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->condition->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset, + $this->db->escapeIdentifier($this->name) ); - $rq = $this->db->execute($sql, $this->values); + $rq = $this->db->execute($sql, $this->condition->getValues()); $result = $rq->fetchColumn(); return $result ? (int) $result : 0; @@ -270,12 +387,12 @@ class Table public function sum($column) { $sql = sprintf( - 'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->buildCondition().$this->sql_order.$this->sql_limit.$this->sql_offset, + 'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->condition->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset, $this->db->escapeIdentifier($column), - $this->db->escapeIdentifier($this->table_name) + $this->db->escapeIdentifier($this->name) ); - $rq = $this->db->execute($sql, $this->values); + $rq = $this->db->execute($sql, $this->condition->getValues()); $result = $rq->fetchColumn(); return $result ? (float) $result : 0; @@ -290,7 +407,7 @@ class Table * @param string $local_column Local column * @param string $local_table Local table * @param string $alias Join table alias - * @return \PicoDb\Table + * @return Table */ public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '') { @@ -298,7 +415,7 @@ class Table 'LEFT JOIN %s ON %s=%s', $this->db->escapeIdentifier($table), $this->db->escapeIdentifier($alias ?: $table).'.'.$this->db->escapeIdentifier($foreign_column), - $this->db->escapeIdentifier($local_table ?: $this->table_name).'.'.$this->db->escapeIdentifier($local_column) + $this->db->escapeIdentifier($local_table ?: $this->name).'.'.$this->db->escapeIdentifier($local_column) ); return $this; @@ -313,7 +430,7 @@ class Table * @param string $column1 * @param string $table2 * @param string $column2 - * @return \PicoDb\Table + * @return Table */ public function left($table1, $alias1, $column1, $table2, $column2) { @@ -328,100 +445,24 @@ class Table return $this; } - /** - * Add custom condition - * - * @access private - * @return Table - */ - private function condition($condition) - { - $this->condition = $condition; - return $this; - } - - /** - * Build condition - * - * @access private - * @return string - */ - private function buildCondition() - { - if (! empty($this->condition)) { - return 'WHERE '.$this->condition; - } - - return empty($this->conditions) ? '' : ' WHERE '.implode(' AND ', $this->conditions); - } - - /** - * Add new condition - * - * @access public - * @param string $sql - * @return Table - */ - public function addCondition($sql) - { - if ($this->is_or_condition) { - $this->or_conditions[] = $sql; - } - else { - $this->conditions[] = $sql; - } - - return $this; - } - - /** - * Start OR condition - * - * @access public - * @return \PicoDb\Table - */ - public function beginOr() - { - $this->is_or_condition = true; - $this->or_conditions = array(); - return $this; - } - - /** - * Close OR condition - * - * @access public - * @return \PicoDb\Table - */ - public function closeOr() - { - $this->is_or_condition = false; - - if (! empty($this->or_conditions)) { - $this->conditions[] = '('.implode(' OR ', $this->or_conditions).')'; - } - - return $this; - } - /** * Order by * * @access public * @param string $column Column name * @param string $order Direction ASC or DESC - * @return \PicoDb\Table + * @return Table */ public function orderBy($column, $order = self::SORT_ASC) { $order = strtoupper($order); $order = $order === self::SORT_ASC || $order === self::SORT_DESC ? $order : self::SORT_ASC; - if ($this->sql_order === '') { - $this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order; + if ($this->sqlOrder === '') { + $this->sqlOrder = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order; } else { - $this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.$order; + $this->sqlOrder .= ', '.$this->db->escapeIdentifier($column).' '.$order; } return $this; @@ -432,17 +473,11 @@ class Table * * @access public * @param string $column - * @return \PicoDb\Table + * @return Table */ public function asc($column) { - if ($this->sql_order === '') { - $this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.self::SORT_ASC; - } - else { - $this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.self::SORT_ASC; - } - + $this->orderBy($column, self::SORT_ASC); return $this; } @@ -451,17 +486,11 @@ class Table * * @access public * @param string $column - * @return \PicoDb\Table + * @return Table */ public function desc($column) { - if ($this->sql_order === '') { - $this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.self::SORT_DESC; - } - else { - $this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.self::SORT_DESC; - } - + $this->orderBy($column, self::SORT_DESC); return $this; } @@ -470,12 +499,12 @@ class Table * * @access public * @param integer $value - * @return \PicoDb\Table + * @return Table */ public function limit($value) { if (! is_null($value)) { - $this->sql_limit = ' LIMIT '.(int) $value; + $this->sqlLimit = ' LIMIT '.(int) $value; } return $this; @@ -486,12 +515,12 @@ class Table * * @access public * @param integer $value - * @return \PicoDb\Table + * @return Table */ public function offset($value) { if (! is_null($value)) { - $this->sql_offset = ' OFFSET '.(int) $value; + $this->sqlOffset = ' OFFSET '.(int) $value; } return $this; @@ -501,11 +530,24 @@ class Table * Group by * * @access public - * @return \PicoDb\Table + * @return Table */ public function groupBy() { - $this->group_by = func_get_args(); + $this->groupBy = func_get_args(); + return $this; + } + + /** + * Custom select + * + * @access public + * @param string $select + * @return Table + */ + public function select($select) + { + $this->sqlSelect = $select; return $this; } @@ -513,7 +555,7 @@ class Table * Define the columns for the select * * @access public - * @return \PicoDb\Table + * @return Table */ public function columns() { @@ -521,11 +563,25 @@ class Table return $this; } + /** + * Sum column + * + * @access public + * @param string $column + * @param mixed $value + * @return Table + */ + public function sumColumn($column, $value) + { + $this->sumColumns[$column] = $value; + return $this; + } + /** * Distinct * * @access public - * @return \PicoDb\Table + * @return Table */ public function distinct() { @@ -534,119 +590,58 @@ class Table return $this; } + /** + * Add callback to alter the resultset + * + * @access public + * @param Closure|array $callback + * @return Table + */ + public function callback($callback) + { + $this->callback = $callback; + return $this; + } + + /** + * Build a select query + * + * @access public + * @return string + */ + public function buildSelectQuery() + { + if (empty($this->sqlSelect)) { + $this->columns = $this->db->escapeIdentifierList($this->columns); + $this->sqlSelect = ($this->distinct ? 'DISTINCT ' : '').(empty($this->columns) ? '*' : implode(', ', $this->columns)); + } + + $this->groupBy = $this->db->escapeIdentifierList($this->groupBy); + + return trim(sprintf( + 'SELECT %s FROM %s %s %s %s %s %s %s', + $this->sqlSelect, + $this->db->escapeIdentifier($this->name), + implode(' ', $this->joins), + $this->condition->build(), + empty($this->groupBy) ? '' : 'GROUP BY '.implode(', ', $this->groupBy), + $this->sqlOrder, + $this->sqlLimit, + $this->sqlOffset + )); + } + /** * Magic method for sql conditions * * @access public * @param string $name * @param array $arguments - * @return \PicoDb\Table + * @return Table */ public function __call($name, array $arguments) { - $column = $arguments[0]; - $sql = ''; - - switch (strtolower($name)) { - - case 'in': - if (isset($arguments[1]) && is_array($arguments[1]) && ! empty($arguments[1])) { - - $sql = sprintf( - '%s IN (%s)', - $this->db->escapeIdentifier($column), - implode(', ', array_fill(0, count($arguments[1]), '?')) - ); - } - break; - - case 'notin': - if (isset($arguments[1]) && is_array($arguments[1]) && ! empty($arguments[1])) { - - $sql = sprintf( - '%s NOT IN (%s)', - $this->db->escapeIdentifier($column), - implode(', ', array_fill(0, count($arguments[1]), '?')) - ); - } - break; - - case 'like': - $sql = sprintf( - '%s %s ?', - $this->db->escapeIdentifier($column), - $this->db->getConnection()->operatorLikeCaseSensitive() - ); - break; - - case 'ilike': - $sql = sprintf( - '%s %s ?', - $this->db->escapeIdentifier($column), - $this->db->getConnection()->operatorLikeNotCaseSensitive() - ); - break; - - case 'eq': - case 'equal': - case 'equals': - $sql = sprintf('%s = ?', $this->db->escapeIdentifier($column)); - break; - - case 'neq': - case 'notequal': - case 'notequals': - $sql = sprintf('%s != ?', $this->db->escapeIdentifier($column)); - break; - - case 'gt': - case 'greaterthan': - $sql = sprintf('%s > ?', $this->db->escapeIdentifier($column)); - break; - - case 'lt': - case 'lowerthan': - $sql = sprintf('%s < ?', $this->db->escapeIdentifier($column)); - break; - - case 'gte': - case 'greaterthanorequals': - $sql = sprintf('%s >= ?', $this->db->escapeIdentifier($column)); - break; - - case 'lte': - case 'lowerthanorequals': - $sql = sprintf('%s <= ?', $this->db->escapeIdentifier($column)); - break; - - case 'isnull': - $sql = sprintf('%s IS NULL', $this->db->escapeIdentifier($column)); - break; - - case 'notnull': - $sql = sprintf('%s IS NOT NULL', $this->db->escapeIdentifier($column)); - break; - } - - if ($sql !== '') { - - $this->addCondition($sql); - - if (isset($arguments[1])) { - - if (is_array($arguments[1])) { - - foreach ($arguments[1] as $value) { - $this->values[] = $value; - } - } - else { - - $this->values[] = $arguments[1]; - } - } - } - + call_user_func_array(array($this->condition, $name), $arguments); return $this; } } diff --git a/vendor/fguillot/picodb/phpunit.xml b/vendor/fguillot/picodb/phpunit.xml new file mode 100644 index 0000000..e719a50 --- /dev/null +++ b/vendor/fguillot/picodb/phpunit.xml @@ -0,0 +1,15 @@ + + + + tests/SqliteDriverTest.php + tests/SqliteDatabaseTest.php + tests/SqliteSchemaTest.php + tests/SqliteTableTest.php + + + \ No newline at end of file diff --git a/vendor/fguillot/picodb/tests/MysqlDatabaseTest.php b/vendor/fguillot/picodb/tests/MysqlDatabaseTest.php new file mode 100644 index 0000000..780b876 --- /dev/null +++ b/vendor/fguillot/picodb/tests/MysqlDatabaseTest.php @@ -0,0 +1,100 @@ +db = new Database(array('driver' => 'mysql', 'hostname' => 'localhost', 'username' => 'root', 'password' => '', 'database' => 'picodb')); + $this->db->getConnection()->exec('CREATE DATABASE IF NOT EXISTS `picodb`'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS foobar'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS schema_version'); + $this->db->logQueries = true; + } + + public function testEscapeIdentifer() + { + $this->assertEquals('`a`', $this->db->escapeIdentifier('a')); + $this->assertEquals('a.b', $this->db->escapeIdentifier('a.b')); + $this->assertEquals('`c`.`a`', $this->db->escapeIdentifier('a', 'c')); + $this->assertEquals('a.b', $this->db->escapeIdentifier('a.b', 'c')); + $this->assertEquals('SELECT COUNT(*) FROM test', $this->db->escapeIdentifier('SELECT COUNT(*) FROM test')); + $this->assertEquals('SELECT COUNT(*) FROM test', $this->db->escapeIdentifier('SELECT COUNT(*) FROM test', 'b')); + } + + public function testEscapeIdentiferList() + { + $this->assertEquals(array('`c`.`a`', '`c`.`b`'), $this->db->escapeIdentifierList(array('a', 'b'), 'c')); + $this->assertEquals(array('`a`', 'd.b'), $this->db->escapeIdentifierList(array('a', 'd.b'))); + } + + public function testThatPreparedStatementWorks() + { + $this->db->getConnection()->exec('CREATE TABLE foobar (id INT AUTO_INCREMENT NOT NULL, something TEXT, PRIMARY KEY (id)) ENGINE=InnoDB'); + $this->db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + $this->assertEquals(1, $this->db->getLastId()); + $this->assertEquals('a', $this->db->execute('SELECT something FROM foobar WHERE something=?', array('a'))->fetchColumn()); + } + + /** + * @expectedException PicoDb\SQLException + */ + public function testBadSQLQuery() + { + $this->db->execute('INSERT INTO foobar'); + } + + public function testDuplicateKey() + { + $this->db->getConnection()->exec('CREATE TABLE foobar (something CHAR(1) UNIQUE) ENGINE=InnoDB'); + + $this->assertNotFalse($this->db->execute('INSERT INTO foobar (something) VALUES (?)', array('a'))); + $this->assertFalse($this->db->execute('INSERT INTO foobar (something) VALUES (?)', array('a'))); + + $this->assertEquals(1, $this->db->execute('SELECT COUNT(*) FROM foobar WHERE something=?', array('a'))->fetchColumn()); + } + + public function testThatTransactionReturnsAValue() + { + $this->assertEquals('a', $this->db->transaction(function ($db) { + + $db->getConnection()->exec('CREATE TABLE foobar (something CHAR(1) UNIQUE) ENGINE=InnoDB'); + $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + + return $db->execute('SELECT something FROM foobar WHERE something=?', array('a'))->fetchColumn(); + })); + } + + public function testThatTransactionReturnsTrue() + { + $this->assertTrue($this->db->transaction(function ($db) { + $db->getConnection()->exec('CREATE TABLE foobar (something CHAR(1) UNIQUE) ENGINE=InnoDB'); + $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + })); + } + + /** + * @expectedException PicoDb\SQLException + */ + public function testThatTransactionThrowExceptionWhenRollbacked() + { + $this->assertFalse($this->db->transaction(function ($db) { + $db->getConnection()->exec('CREATE TABL'); + })); + } + + public function testThatTransactionReturnsFalseWhithDuplicateKey() + { + $this->assertFalse($this->db->transaction(function ($db) { + $db->getConnection()->exec('CREATE TABLE foobar (something CHAR(1) UNIQUE) ENGINE=InnoDB'); + $r1 = $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + $r2 = $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + return $r1 && $r2; + })); + } +} diff --git a/vendor/fguillot/picodb/tests/MysqlDriverTest.php b/vendor/fguillot/picodb/tests/MysqlDriverTest.php new file mode 100644 index 0000000..25e5386 --- /dev/null +++ b/vendor/fguillot/picodb/tests/MysqlDriverTest.php @@ -0,0 +1,65 @@ +driver = new Mysql(array('hostname' => 'localhost', 'username' => 'root', 'password' => '', 'database' => 'picodb')); + $this->driver->getConnection()->exec('CREATE DATABASE IF NOT EXISTS `picodb`'); + $this->driver->getConnection()->exec('DROP TABLE IF EXISTS foobar'); + $this->driver->getConnection()->exec('DROP TABLE IF EXISTS schema_version'); + } + + /** + * @expectedException LogicException + */ + public function testMissingRequiredParameter() + { + new Mysql(array()); + } + + public function testDuplicateKeyError() + { + $this->assertFalse($this->driver->isDuplicateKeyError(1234)); + $this->assertTrue($this->driver->isDuplicateKeyError(23000)); + } + + public function testOperator() + { + $this->assertEquals('LIKE BINARY', $this->driver->getOperator('LIKE')); + $this->assertEquals('LIKE', $this->driver->getOperator('ILIKE')); + $this->assertEquals('', $this->driver->getOperator('FOO')); + } + + public function testSchemaVersion() + { + $this->assertEquals(0, $this->driver->getSchemaVersion()); + + $this->driver->setSchemaVersion(1); + $this->assertEquals(1, $this->driver->getSchemaVersion()); + + $this->driver->setSchemaVersion(42); + $this->assertEquals(42, $this->driver->getSchemaVersion()); + } + + public function testLastInsertId() + { + $this->assertEquals(0, $this->driver->getLastId()); + + $this->driver->getConnection()->exec('CREATE TABLE foobar (id INT AUTO_INCREMENT NOT NULL, something TEXT, PRIMARY KEY (id)) ENGINE=InnoDB'); + $this->driver->getConnection()->exec('INSERT INTO foobar (something) VALUES (1)'); + + $this->assertEquals(1, $this->driver->getLastId()); + } + + public function testEscape() + { + $this->assertEquals('`foobar`', $this->driver->escape('foobar')); + } +} diff --git a/vendor/fguillot/picodb/tests/MysqlSchemaTest.php b/vendor/fguillot/picodb/tests/MysqlSchemaTest.php new file mode 100644 index 0000000..b7a2d95 --- /dev/null +++ b/vendor/fguillot/picodb/tests/MysqlSchemaTest.php @@ -0,0 +1,34 @@ +db = new PicoDb\Database(array('driver' => 'mysql', 'hostname' => 'localhost', 'username' => 'root', 'password' => '', 'database' => 'picodb')); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS test1'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS test2'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS schema_version'); + } + + public function testMigrations() + { + $this->assertTrue($this->db->schema()->check(2)); + $this->assertEquals(2, $this->db->getDriver()->getSchemaVersion()); + } + + public function testFailedMigrations() + { + $this->assertEquals(0, $this->db->getDriver()->getSchemaVersion()); + $this->assertFalse($this->db->schema()->check(3)); + $this->assertEquals(0, $this->db->getDriver()->getSchemaVersion()); + + $logs = $this->db->getLogMessages(); + $this->assertNotEmpty($logs); + $this->assertStringStartsWith('\Schema\version_3 => SQLSTATE[42000]: Syntax error or access violation', $logs[0]); + } +} diff --git a/vendor/fguillot/picodb/tests/MysqlTableTest.php b/vendor/fguillot/picodb/tests/MysqlTableTest.php new file mode 100644 index 0000000..eab784b --- /dev/null +++ b/vendor/fguillot/picodb/tests/MysqlTableTest.php @@ -0,0 +1,313 @@ +db = new Database(array('driver' => 'mysql', 'hostname' => 'localhost', 'username' => 'root', 'password' => '', 'database' => 'picodb')); + $this->db->getConnection()->exec('CREATE DATABASE IF NOT EXISTS `picodb`'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS test1'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS test2'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS foobar'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS schema_version'); + $this->db->logQueries = true; + } + + public function testSelect() + { + $this->assertEquals('SELECT 1 FROM `test`', $this->db->table('test')->select(1)->buildSelectQuery()); + } + + public function testColumns() + { + $this->assertEquals('SELECT `a`, `b` FROM `test`', $this->db->table('test')->columns('a', 'b')->buildSelectQuery()); + } + + public function testDistinct() + { + $this->assertEquals('SELECT DISTINCT `a`, `b` FROM `test`', $this->db->table('test')->distinct('a', 'b')->buildSelectQuery()); + } + + public function testGroupBy() + { + $this->assertEquals('SELECT * FROM `test` GROUP BY `a`', $this->db->table('test')->groupBy('a')->buildSelectQuery()); + } + + public function testOrderBy() + { + $this->assertEquals('SELECT * FROM `test` ORDER BY `a` ASC', $this->db->table('test')->asc('a')->buildSelectQuery()); + $this->assertEquals('SELECT * FROM `test` ORDER BY `a` ASC', $this->db->table('test')->orderBy('a', Table::SORT_ASC)->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM `test` ORDER BY `a` DESC', $this->db->table('test')->desc('a', Table::SORT_DESC)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM `test` ORDER BY `a` DESC', $this->db->table('test')->orderBy('a', Table::SORT_DESC)->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM `test` ORDER BY `a` ASC, `b` ASC', $this->db->table('test')->asc('a')->asc('b')->buildSelectQuery()); + $this->assertEquals('SELECT * FROM `test` ORDER BY `a` DESC, `b` DESC', $this->db->table('test')->desc('a')->desc('b')->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM `test` ORDER BY `a` ASC, `b` ASC', $this->db->table('test')->orderBy('a')->orderBy('b')->buildSelectQuery()); + $this->assertEquals('SELECT * FROM `test` ORDER BY `a` DESC, `b` DESC', $this->db->table('test')->orderBy('a', Table::SORT_DESC)->orderBy('b', Table::SORT_DESC)->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM `test` ORDER BY `a` DESC, `b` ASC', $this->db->table('test')->desc('a')->asc('b')->buildSelectQuery()); + } + + public function testLimit() + { + $this->assertEquals('SELECT * FROM `test` LIMIT 10', $this->db->table('test')->limit(10)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM `test`', $this->db->table('test')->limit(null)->buildSelectQuery()); + } + + public function testOffset() + { + $this->assertEquals('SELECT * FROM `test` OFFSET 0', $this->db->table('test')->offset(0)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM `test` OFFSET 10', $this->db->table('test')->offset(10)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM `test`', $this->db->table('test')->limit(null)->buildSelectQuery()); + } + + public function testLimitOffset() + { + $this->assertEquals('SELECT * FROM `test` LIMIT 2 OFFSET 0', $this->db->table('test')->offset(0)->limit(2)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM `test` LIMIT 5 OFFSET 10', $this->db->table('test')->offset(10)->limit(5)->buildSelectQuery()); + } + + public function testSubquery() + { + $this->assertEquals('SELECT (SELECT 1 FROM "foobar" WHERE 1=1) AS `b` FROM `test`', $this->db->table('test')->subquery('SELECT 1 FROM "foobar" WHERE 1=1', 'b')->buildSelectQuery()); + } + + public function testConditionEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` = ? AND `b` = ?', $table->eq('a', 2)->eq('b', 'foobar')->buildSelectQuery()); + $this->assertEquals(array(2, 'foobar'), $table->condition->getValues()); + } + + public function testConditionNotEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` != ?', $table->neq('a', 2)->buildSelectQuery()); + $this->assertEquals(array(2), $table->condition->getValues()); + } + + public function testConditionIn() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` IN (?, ?)', $table->in('a', array('b', 'c'))->buildSelectQuery()); + $this->assertEquals(array('b', 'c'), $table->condition->getValues()); + + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test`', $table->in('a', array())->buildSelectQuery()); + $this->assertEquals(array(), $table->condition->getValues()); + } + + public function testConditionNotIn() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` NOT IN (?, ?)', $table->notin('a', array('b', 'c'))->buildSelectQuery()); + $this->assertEquals(array('b', 'c'), $table->condition->getValues()); + + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test`', $table->notin('a', array())->buildSelectQuery()); + $this->assertEquals(array(), $table->condition->getValues()); + } + + public function testConditionLike() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` LIKE BINARY ?', $table->like('a', '%foobar%')->buildSelectQuery()); + $this->assertEquals(array('%foobar%'), $table->condition->getValues()); + } + + public function testConditionILike() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` LIKE ?', $table->ilike('a', '%foobar%')->buildSelectQuery()); + $this->assertEquals(array('%foobar%'), $table->condition->getValues()); + } + + public function testConditionGreaterThan() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` > ?', $table->gt('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionGreaterThanOrEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` >= ?', $table->gte('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionLowerThan() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` < ?', $table->lt('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionLowerThanOrEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` <= ?', $table->lte('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionIsNull() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` IS NOT NULL', $table->notNull('a')->buildSelectQuery()); + $this->assertEquals(array(), $table->condition->getValues()); + } + + public function testCustomCondition() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE a=c AND `b` = ?', $table->addCondition('a=c')->eq('b', 4)->buildSelectQuery()); + $this->assertEquals(array(4), $table->condition->getValues()); + } + + public function testOrConditions() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM `test` WHERE `a` IS NOT NULL AND (`b` = ? OR `c` >= ?)', $table->notNull('a')->beginOr()->eq('b', 2)->gte('c', 5)->closeOr()->buildSelectQuery()); + $this->assertEquals(array(2, 5), $table->condition->getValues()); + } + + public function testInsertUpdate() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (a TEXT)')); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 'b'))); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 'c'))); + + $this->assertEquals(array(array('a' => 'b'), array('a' => 'c')), $this->db->table('foobar')->findAll()); + + $this->assertEquals(array('b', 'c'), $this->db->table('foobar')->findAllByColumn('a')); + + $this->assertEquals(array('a' => 'b'), $this->db->table('foobar')->findOne()); + + $this->assertEquals('b', $this->db->table('foobar')->findOneColumn('a')); + + $this->assertTrue($this->db->table('foobar')->exists()); + $this->assertTrue($this->db->table('foobar')->eq('a', 'c')->exists()); + $this->assertFalse($this->db->table('foobar')->eq('a', 'e')->exists()); + + $this->assertEquals(2, $this->db->table('foobar')->count()); + $this->assertEquals(1, $this->db->table('foobar')->eq('a', 'c')->count()); + $this->assertEquals(0, $this->db->table('foobar')->eq('a', 'e')->count()); + + $this->assertTrue($this->db->table('foobar')->eq('a', 'c')->remove()); + $this->assertFalse($this->db->table('foobar')->eq('a', 'e')->remove()); + + $this->assertTrue($this->db->table('foobar')->eq('a', 'b')->update(array('a' => 'test'))); + $this->assertTrue($this->db->table('foobar')->eq('a', 'lol')->update(array('a' => 'test'))); + + $this->assertNotEmpty($this->db->table('foobar')->eq('a', 'test')->findOne()); + $this->assertNull($this->db->table('foobar')->eq('a', 'lol')->findOne()); + + $this->assertTrue($this->db->table('foobar')->eq('a', 'test')->save(array('a' => 'plop'))); + $this->assertEquals(1, $this->db->table('foobar')->count()); + } + + public function testSumColumn() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (b FLOAT, c FLOAT)')); + $this->assertTrue($this->db->table('foobar')->insert(array('b' => 2, 'c' => 3.3))); + + $this->assertTrue($this->db->table('foobar')->sumColumn('b', 2.5)->sumColumn('c', 3)->update()); + + $this->assertEquals( + array('b' => 4.5, 'c' => 6.3), + $this->db->table('foobar')->findOne() + ); + } + + public function testSum() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (a INTEGER)')); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 2))); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 5))); + $this->assertEquals(7, $this->db->table('foobar')->sum('a')); + } + + public function testLeftJoin() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE test1 (a INTEGER NOT NULL, foreign_key INTEGER NOT NULL)')); + $this->assertNotFalse($this->db->execute('CREATE TABLE test2 (id INTEGER NOT NULL, b INTEGER NOT NULL)')); + + $this->assertTrue($this->db->table('test2')->insert(array('id' => 42, 'b' => 2))); + $this->assertTrue($this->db->table('test1')->insert(array('a' => 18, 'foreign_key' => 42))); + + $this->assertEquals( + array('a' => 18, 'b' => 2), + $this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id')->findOne() + ); + + $this->assertEquals( + array('a' => 18, 'b' => 2), + $this->db->table('test2')->columns('a', 'b')->eq('a', 18)->join('test1', 'foreign_key', 'id')->findOne() + ); + + $this->assertEquals( + array('a' => 18, 'b' => 2), + $this->db->table('test1')->columns('a', 'b')->join('test2', 'id', 'foreign_key')->findOne() + ); + } + + public function testHashTable() + { + $this->assertNotFalse($this->db->execute( + 'CREATE TABLE foobar ( + column1 VARCHAR(20) NOT NULL UNIQUE, + column2 VARCHAR(20) default NULL + )' + )); + + $this->assertTrue($this->db->table('foobar')->insert(array('column1' => 'option1', 'column2' => 'value1'))); + $this->assertTrue($this->db->table('foobar')->insert(array('column1' => 'option2', 'column2' => 'value2'))); + $this->assertTrue($this->db->table('foobar')->insert(array('column1' => 'option3', 'column2' => 'value3'))); + + $values = array( + 'option1' => 'hey', + 'option4' => 'ho', + ); + + $this->assertTrue($this->db->hashtable('foobar')->columnKey('column1')->columnValue('column2')->put($values)); + + $this->assertEquals( + array('option2' => 'value2', 'option4' => 'ho'), + $this->db->hashtable('foobar')->columnKey('column1')->columnValue('column2')->get('option2', 'option4') + ); + + $this->assertEquals( + array('option2' => 'value2', 'option3' => 'value3', 'option1' => 'hey', 'option4' => 'ho'), + $this->db->hashtable('foobar')->columnKey('column1')->columnValue('column2')->get() + ); + + $this->assertEquals( + array('option2' => 'value2', 'option3' => 'value3', 'option1' => 'hey', 'option4' => 'ho'), + $this->db->hashtable('foobar')->getAll('column1', 'column2') + ); + } +} diff --git a/vendor/fguillot/picodb/tests/PostgresDatabaseTest.php b/vendor/fguillot/picodb/tests/PostgresDatabaseTest.php new file mode 100644 index 0000000..a99ea4a --- /dev/null +++ b/vendor/fguillot/picodb/tests/PostgresDatabaseTest.php @@ -0,0 +1,99 @@ +db = new Database(array('driver' => 'postgres', 'hostname' => 'localhost', 'username' => 'postgres', 'password' => 'postgres', 'database' => 'picodb')); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS foobar'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS schema_version'); + $this->db->logQueries = true; + } + + public function testEscapeIdentifer() + { + $this->assertEquals('"a"', $this->db->escapeIdentifier('a')); + $this->assertEquals('a.b', $this->db->escapeIdentifier('a.b')); + $this->assertEquals('"c"."a"', $this->db->escapeIdentifier('a', 'c')); + $this->assertEquals('a.b', $this->db->escapeIdentifier('a.b', 'c')); + $this->assertEquals('SELECT COUNT(*) FROM test', $this->db->escapeIdentifier('SELECT COUNT(*) FROM test')); + $this->assertEquals('SELECT COUNT(*) FROM test', $this->db->escapeIdentifier('SELECT COUNT(*) FROM test', 'b')); + } + + public function testEscapeIdentiferList() + { + $this->assertEquals(array('"c"."a"', '"c"."b"'), $this->db->escapeIdentifierList(array('a', 'b'), 'c')); + $this->assertEquals(array('"a"', 'd.b'), $this->db->escapeIdentifierList(array('a', 'd.b'))); + } + + public function testThatPreparedStatementWorks() + { + $this->db->getConnection()->exec('CREATE TABLE foobar (id serial PRIMARY KEY, something TEXT)'); + $this->db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + $this->assertEquals(1, $this->db->getLastId()); + $this->assertEquals('a', $this->db->execute('SELECT something FROM foobar WHERE something=?', array('a'))->fetchColumn()); + } + + /** + * @expectedException PicoDb\SQLException + */ + public function testBadSQLQuery() + { + $this->db->execute('INSERT INTO foobar'); + } + + public function testDuplicateKey() + { + $this->db->getConnection()->exec('CREATE TABLE foobar (something TEXT UNIQUE)'); + + $this->assertNotFalse($this->db->execute('INSERT INTO foobar (something) VALUES (?)', array('a'))); + $this->assertFalse($this->db->execute('INSERT INTO foobar (something) VALUES (?)', array('a'))); + + $this->assertEquals(1, $this->db->execute('SELECT COUNT(*) FROM foobar WHERE something=?', array('a'))->fetchColumn()); + } + + public function testThatTransactionReturnsAValue() + { + $this->assertEquals('a', $this->db->transaction(function ($db) { + + $db->getConnection()->exec('CREATE TABLE foobar (something TEXT UNIQUE)'); + $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + + return $db->execute('SELECT something FROM foobar WHERE something=?', array('a'))->fetchColumn(); + })); + } + + public function testThatTransactionReturnsTrue() + { + $this->assertTrue($this->db->transaction(function ($db) { + $db->getConnection()->exec('CREATE TABLE foobar (something TEXT UNIQUE)'); + $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + })); + } + + /** + * @expectedException PicoDb\SQLException + */ + public function testThatTransactionThrowExceptionWhenRollbacked() + { + $this->assertFalse($this->db->transaction(function ($db) { + $db->getConnection()->exec('CREATE TABL'); + })); + } + + public function testThatTransactionReturnsFalseWhithDuplicateKey() + { + $this->assertFalse($this->db->transaction(function ($db) { + $db->getConnection()->exec('CREATE TABLE foobar (something TEXT UNIQUE)'); + $r1 = $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + $r2 = $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + return $r1 && $r2; + })); + } +} diff --git a/vendor/fguillot/picodb/tests/PostgresDriverTest.php b/vendor/fguillot/picodb/tests/PostgresDriverTest.php new file mode 100644 index 0000000..ae81d08 --- /dev/null +++ b/vendor/fguillot/picodb/tests/PostgresDriverTest.php @@ -0,0 +1,70 @@ +driver = new Postgres(array('hostname' => 'localhost', 'username' => 'postgres', 'password' => 'postgres', 'database' => 'picodb')); + $this->driver->getConnection()->exec('DROP TABLE IF EXISTS foobar'); + $this->driver->getConnection()->exec('DROP TABLE IF EXISTS schema_version'); + } + + public function tearDown() + { + $this->driver->closeConnection(); + } + + /** + * @expectedException LogicException + */ + public function testMissingRequiredParameter() + { + new Postgres(array()); + } + + public function testDuplicateKeyError() + { + $this->assertFalse($this->driver->isDuplicateKeyError(1234)); + $this->assertTrue($this->driver->isDuplicateKeyError(23505)); + $this->assertTrue($this->driver->isDuplicateKeyError(23503)); + } + + public function testOperator() + { + $this->assertEquals('LIKE', $this->driver->getOperator('LIKE')); + $this->assertEquals('ILIKE', $this->driver->getOperator('ILIKE')); + $this->assertEquals('', $this->driver->getOperator('FOO')); + } + + public function testSchemaVersion() + { + $this->assertEquals(0, $this->driver->getSchemaVersion()); + + $this->driver->setSchemaVersion(1); + $this->assertEquals(1, $this->driver->getSchemaVersion()); + + $this->driver->setSchemaVersion(42); + $this->assertEquals(42, $this->driver->getSchemaVersion()); + } + + public function testLastInsertId() + { + $this->assertEquals(0, $this->driver->getLastId()); + + $this->driver->getConnection()->exec('CREATE TABLE foobar (id serial PRIMARY KEY, something TEXT)'); + $this->driver->getConnection()->exec('INSERT INTO foobar (something) VALUES (1)'); + + $this->assertEquals(1, $this->driver->getLastId()); + } + + public function testEscape() + { + $this->assertEquals('"foobar"', $this->driver->escape('foobar')); + } +} diff --git a/vendor/fguillot/picodb/tests/PostgresSchemaTest.php b/vendor/fguillot/picodb/tests/PostgresSchemaTest.php new file mode 100644 index 0000000..6395993 --- /dev/null +++ b/vendor/fguillot/picodb/tests/PostgresSchemaTest.php @@ -0,0 +1,34 @@ +db = new PicoDb\Database(array('driver' => 'postgres', 'hostname' => 'localhost', 'username' => 'postgres', 'password' => 'postgres', 'database' => 'picodb')); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS test1'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS test2'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS schema_version'); + } + + public function testMigrations() + { + $this->assertTrue($this->db->schema()->check(2)); + $this->assertEquals(2, $this->db->getDriver()->getSchemaVersion()); + } + + public function testFailedMigrations() + { + $this->assertEquals(0, $this->db->getDriver()->getSchemaVersion()); + $this->assertFalse($this->db->schema()->check(3)); + $this->assertEquals(0, $this->db->getDriver()->getSchemaVersion()); + + $logs = $this->db->getLogMessages(); + $this->assertNotEmpty($logs); + $this->assertStringStartsWith('\Schema\version_3 => SQLSTATE[42601]: Syntax error', $logs[0]); + } +} diff --git a/vendor/fguillot/picodb/tests/PostgresTableTest.php b/vendor/fguillot/picodb/tests/PostgresTableTest.php new file mode 100644 index 0000000..4db37b0 --- /dev/null +++ b/vendor/fguillot/picodb/tests/PostgresTableTest.php @@ -0,0 +1,312 @@ +db = new Database(array('driver' => 'postgres', 'hostname' => 'localhost', 'username' => 'postgres', 'password' => 'postgres', 'database' => 'picodb')); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS test1'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS test2'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS foobar'); + $this->db->getConnection()->exec('DROP TABLE IF EXISTS schema_version'); + $this->db->logQueries = true; + } + + public function testSelect() + { + $this->assertEquals('SELECT 1 FROM "test"', $this->db->table('test')->select(1)->buildSelectQuery()); + } + + public function testColumns() + { + $this->assertEquals('SELECT "a", "b" FROM "test"', $this->db->table('test')->columns('a', 'b')->buildSelectQuery()); + } + + public function testDistinct() + { + $this->assertEquals('SELECT DISTINCT "a", "b" FROM "test"', $this->db->table('test')->distinct('a', 'b')->buildSelectQuery()); + } + + public function testGroupBy() + { + $this->assertEquals('SELECT * FROM "test" GROUP BY "a"', $this->db->table('test')->groupBy('a')->buildSelectQuery()); + } + + public function testOrderBy() + { + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" ASC', $this->db->table('test')->asc('a')->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" ASC', $this->db->table('test')->orderBy('a', Table::SORT_ASC)->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC', $this->db->table('test')->desc('a', Table::SORT_DESC)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC', $this->db->table('test')->orderBy('a', Table::SORT_DESC)->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" ASC, "b" ASC', $this->db->table('test')->asc('a')->asc('b')->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC, "b" DESC', $this->db->table('test')->desc('a')->desc('b')->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" ASC, "b" ASC', $this->db->table('test')->orderBy('a')->orderBy('b')->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC, "b" DESC', $this->db->table('test')->orderBy('a', Table::SORT_DESC)->orderBy('b', Table::SORT_DESC)->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC, "b" ASC', $this->db->table('test')->desc('a')->asc('b')->buildSelectQuery()); + } + + public function testLimit() + { + $this->assertEquals('SELECT * FROM "test" LIMIT 10', $this->db->table('test')->limit(10)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test"', $this->db->table('test')->limit(null)->buildSelectQuery()); + } + + public function testOffset() + { + $this->assertEquals('SELECT * FROM "test" OFFSET 0', $this->db->table('test')->offset(0)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" OFFSET 10', $this->db->table('test')->offset(10)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test"', $this->db->table('test')->limit(null)->buildSelectQuery()); + } + + public function testLimitOffset() + { + $this->assertEquals('SELECT * FROM "test" LIMIT 2 OFFSET 0', $this->db->table('test')->offset(0)->limit(2)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" LIMIT 5 OFFSET 10', $this->db->table('test')->offset(10)->limit(5)->buildSelectQuery()); + } + + public function testSubquery() + { + $this->assertEquals('SELECT (SELECT 1 FROM "foobar" WHERE 1=1) AS "b" FROM "test"', $this->db->table('test')->subquery('SELECT 1 FROM "foobar" WHERE 1=1', 'b')->buildSelectQuery()); + } + + public function testConditionEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" = ? AND "b" = ?', $table->eq('a', 2)->eq('b', 'foobar')->buildSelectQuery()); + $this->assertEquals(array(2, 'foobar'), $table->condition->getValues()); + } + + public function testConditionNotEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" != ?', $table->neq('a', 2)->buildSelectQuery()); + $this->assertEquals(array(2), $table->condition->getValues()); + } + + public function testConditionIn() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" IN (?, ?)', $table->in('a', array('b', 'c'))->buildSelectQuery()); + $this->assertEquals(array('b', 'c'), $table->condition->getValues()); + + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test"', $table->in('a', array())->buildSelectQuery()); + $this->assertEquals(array(), $table->condition->getValues()); + } + + public function testConditionNotIn() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" NOT IN (?, ?)', $table->notin('a', array('b', 'c'))->buildSelectQuery()); + $this->assertEquals(array('b', 'c'), $table->condition->getValues()); + + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test"', $table->notin('a', array())->buildSelectQuery()); + $this->assertEquals(array(), $table->condition->getValues()); + } + + public function testConditionLike() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" LIKE ?', $table->like('a', '%foobar%')->buildSelectQuery()); + $this->assertEquals(array('%foobar%'), $table->condition->getValues()); + } + + public function testConditionILike() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" ILIKE ?', $table->ilike('a', '%foobar%')->buildSelectQuery()); + $this->assertEquals(array('%foobar%'), $table->condition->getValues()); + } + + public function testConditionGreaterThan() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" > ?', $table->gt('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionGreaterThanOrEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" >= ?', $table->gte('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionLowerThan() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" < ?', $table->lt('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionLowerThanOrEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" <= ?', $table->lte('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionIsNull() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" IS NOT NULL', $table->notNull('a')->buildSelectQuery()); + $this->assertEquals(array(), $table->condition->getValues()); + } + + public function testCustomCondition() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE a=c AND "b" = ?', $table->addCondition('a=c')->eq('b', 4)->buildSelectQuery()); + $this->assertEquals(array(4), $table->condition->getValues()); + } + + public function testOrConditions() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" IS NOT NULL AND ("b" = ? OR "c" >= ?)', $table->notNull('a')->beginOr()->eq('b', 2)->gte('c', 5)->closeOr()->buildSelectQuery()); + $this->assertEquals(array(2, 5), $table->condition->getValues()); + } + + public function testInsertUpdate() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (a TEXT)')); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 'b'))); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 'c'))); + + $this->assertEquals(array(array('a' => 'b'), array('a' => 'c')), $this->db->table('foobar')->findAll()); + + $this->assertEquals(array('b', 'c'), $this->db->table('foobar')->findAllByColumn('a')); + + $this->assertEquals(array('a' => 'b'), $this->db->table('foobar')->findOne()); + + $this->assertEquals('b', $this->db->table('foobar')->findOneColumn('a')); + + $this->assertTrue($this->db->table('foobar')->exists()); + $this->assertTrue($this->db->table('foobar')->eq('a', 'c')->exists()); + $this->assertFalse($this->db->table('foobar')->eq('a', 'e')->exists()); + + $this->assertEquals(2, $this->db->table('foobar')->count()); + $this->assertEquals(1, $this->db->table('foobar')->eq('a', 'c')->count()); + $this->assertEquals(0, $this->db->table('foobar')->eq('a', 'e')->count()); + + $this->assertTrue($this->db->table('foobar')->eq('a', 'c')->remove()); + $this->assertFalse($this->db->table('foobar')->eq('a', 'e')->remove()); + + $this->assertTrue($this->db->table('foobar')->eq('a', 'b')->update(array('a' => 'test'))); + $this->assertTrue($this->db->table('foobar')->eq('a', 'lol')->update(array('a' => 'test'))); + + $this->assertNotEmpty($this->db->table('foobar')->eq('a', 'test')->findOne()); + $this->assertNull($this->db->table('foobar')->eq('a', 'lol')->findOne()); + + $this->assertTrue($this->db->table('foobar')->eq('a', 'test')->save(array('a' => 'plop'))); + $this->assertEquals(1, $this->db->table('foobar')->count()); + } + + public function testSumColumn() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (b REAL, c REAL)')); + $this->assertTrue($this->db->table('foobar')->insert(array('b' => 2, 'c' => 3.3))); + + $this->assertTrue($this->db->table('foobar')->sumColumn('b', 2.5)->sumColumn('c', 3)->update()); + + $this->assertEquals( + array('b' => 4.5, 'c' => 6.3), + $this->db->table('foobar')->findOne() + ); + } + + public function testSum() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (a INTEGER)')); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 2))); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 5))); + $this->assertEquals(7, $this->db->table('foobar')->sum('a')); + } + + public function testLeftJoin() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE test1 (a INTEGER NOT NULL, foreign_key INTEGER NOT NULL)')); + $this->assertNotFalse($this->db->execute('CREATE TABLE test2 (id INTEGER NOT NULL, b INTEGER NOT NULL)')); + + $this->assertTrue($this->db->table('test2')->insert(array('id' => 42, 'b' => 2))); + $this->assertTrue($this->db->table('test1')->insert(array('a' => 18, 'foreign_key' => 42))); + + $this->assertEquals( + array('a' => 18, 'b' => 2), + $this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id')->findOne() + ); + + $this->assertEquals( + array('a' => 18, 'b' => 2), + $this->db->table('test2')->columns('a', 'b')->eq('a', 18)->join('test1', 'foreign_key', 'id')->findOne() + ); + + $this->assertEquals( + array('a' => 18, 'b' => 2), + $this->db->table('test1')->columns('a', 'b')->join('test2', 'id', 'foreign_key')->findOne() + ); + } + + public function testHashTable() + { + $this->assertNotFalse($this->db->execute( + 'CREATE TABLE foobar ( + column1 TEXT NOT NULL UNIQUE, + column2 TEXT default NULL + )' + )); + + $this->assertTrue($this->db->table('foobar')->insert(array('column1' => 'option1', 'column2' => 'value1'))); + $this->assertTrue($this->db->table('foobar')->insert(array('column1' => 'option2', 'column2' => 'value2'))); + $this->assertTrue($this->db->table('foobar')->insert(array('column1' => 'option3', 'column2' => 'value3'))); + + $values = array( + 'option1' => 'hey', + 'option4' => 'ho', + ); + + $this->assertTrue($this->db->hashtable('foobar')->columnKey('column1')->columnValue('column2')->put($values)); + + $this->assertEquals( + array('option2' => 'value2', 'option4' => 'ho'), + $this->db->hashtable('foobar')->columnKey('column1')->columnValue('column2')->get('option2', 'option4') + ); + + $this->assertEquals( + array('option2' => 'value2', 'option3' => 'value3', 'option1' => 'hey', 'option4' => 'ho'), + $this->db->hashtable('foobar')->columnKey('column1')->columnValue('column2')->get() + ); + + $this->assertEquals( + array('option2' => 'value2', 'option3' => 'value3', 'option1' => 'hey', 'option4' => 'ho'), + $this->db->hashtable('foobar')->getAll('column1', 'column2') + ); + } +} diff --git a/vendor/fguillot/picodb/tests/SchemaFixture.php b/vendor/fguillot/picodb/tests/SchemaFixture.php new file mode 100644 index 0000000..9e7f587 --- /dev/null +++ b/vendor/fguillot/picodb/tests/SchemaFixture.php @@ -0,0 +1,19 @@ +exec('CREATE TABLE test1 (column1 TEXT)'); +} + +function version_2($pdo) +{ + $pdo->exec('CREATE TABLE test2 (column2 TEXT)'); +} + +function version_3($pdo) +{ + // Simulate an error + $pdo->exec('CREATE TABL'); +} diff --git a/vendor/fguillot/picodb/tests/SqliteDatabaseTest.php b/vendor/fguillot/picodb/tests/SqliteDatabaseTest.php new file mode 100644 index 0000000..4375798 --- /dev/null +++ b/vendor/fguillot/picodb/tests/SqliteDatabaseTest.php @@ -0,0 +1,119 @@ +db = new Database(array('driver' => 'sqlite', 'filename' => ':memory:')); + $this->db->logQueries = true; + } + + public function testEscapeIdentifer() + { + $this->assertEquals('"a"', $this->db->escapeIdentifier('a')); + $this->assertEquals('a.b', $this->db->escapeIdentifier('a.b')); + $this->assertEquals('"c"."a"', $this->db->escapeIdentifier('a', 'c')); + $this->assertEquals('a.b', $this->db->escapeIdentifier('a.b', 'c')); + $this->assertEquals('SELECT COUNT(*) FROM test', $this->db->escapeIdentifier('SELECT COUNT(*) FROM test')); + $this->assertEquals('SELECT COUNT(*) FROM test', $this->db->escapeIdentifier('SELECT COUNT(*) FROM test', 'b')); + } + + public function testEscapeIdentiferList() + { + $this->assertEquals(array('"c"."a"', '"c"."b"'), $this->db->escapeIdentifierList(array('a', 'b'), 'c')); + $this->assertEquals(array('"a"', 'd.b'), $this->db->escapeIdentifierList(array('a', 'd.b'))); + } + + public function testThatPreparedStatementWorks() + { + $this->db->getConnection()->exec('CREATE TABLE foobar (id INTEGER PRIMARY KEY, something TEXT)'); + $this->db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + $this->assertEquals(1, $this->db->getLastId()); + $this->assertEquals('a', $this->db->execute('SELECT something FROM foobar WHERE something=?', array('a'))->fetchColumn()); + } + + /** + * @expectedException PicoDb\SQLException + */ + public function testBadSQLQuery() + { + $this->db->execute('INSERT INTO foobar'); + } + + public function testDuplicateKey() + { + $this->db->getConnection()->exec('CREATE TABLE foobar (something TEXT UNIQUE)'); + + $this->assertNotFalse($this->db->execute('INSERT INTO foobar (something) VALUES (?)', array('a'))); + $this->assertFalse($this->db->execute('INSERT INTO foobar (something) VALUES (?)', array('a'))); + + $this->assertEquals(1, $this->db->execute('SELECT COUNT(*) FROM foobar WHERE something=?', array('a'))->fetchColumn()); + } + + public function testThatTransactionReturnsAValue() + { + $this->assertEquals('a', $this->db->transaction(function ($db) { + + $db->getConnection()->exec('CREATE TABLE foobar (something TEXT UNIQUE)'); + $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + + return $db->execute('SELECT something FROM foobar WHERE something=?', array('a'))->fetchColumn(); + })); + } + + public function testThatTransactionReturnsTrue() + { + $this->assertTrue($this->db->transaction(function ($db) { + $db->getConnection()->exec('CREATE TABLE foobar (something TEXT UNIQUE)'); + $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + })); + } + + /** + * @expectedException PicoDb\SQLException + */ + public function testThatTransactionThrowExceptionWhenRollbacked() + { + $this->assertFalse($this->db->transaction(function ($db) { + $db->getConnection()->exec('CREATE TABL'); + })); + } + + public function testThatTransactionReturnsFalseWhithDuplicateKey() + { + $this->assertFalse($this->db->transaction(function ($db) { + $db->getConnection()->exec('CREATE TABLE foobar (something TEXT UNIQUE)'); + $r1 = $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + $r2 = $db->execute('INSERT INTO foobar (something) VALUES (?)', array('a')); + return $r1 && $r2; + })); + } + + public function testGetInstance() + { + Database::setInstance('main', function () { + return new Database(array('driver' => 'sqlite', 'filename' => ':memory:')); + }); + + $instance1 = Database::getInstance('main'); + $instance2 = Database::getInstance('main'); + + $this->assertInstanceOf('PicoDb\Database', $instance1); + $this->assertInstanceOf('PicoDb\Database', $instance2); + $this->assertTrue($instance1 === $instance2); + } + + /** + * @expectedException LogicException + */ + public function testGetMissingInstance() + { + Database::getInstance('notfound'); + } +} diff --git a/vendor/fguillot/picodb/tests/SqliteDriverTest.php b/vendor/fguillot/picodb/tests/SqliteDriverTest.php new file mode 100644 index 0000000..4344d1e --- /dev/null +++ b/vendor/fguillot/picodb/tests/SqliteDriverTest.php @@ -0,0 +1,62 @@ +driver = new Sqlite(array('filename' => ':memory:')); + } + + /** + * @expectedException LogicException + */ + public function testMissingRequiredParameter() + { + new Sqlite(array()); + } + + public function testDuplicateKeyError() + { + $this->assertFalse($this->driver->isDuplicateKeyError(1234)); + $this->assertTrue($this->driver->isDuplicateKeyError(23000)); + } + + public function testOperator() + { + $this->assertEquals('LIKE', $this->driver->getOperator('LIKE')); + $this->assertEquals('LIKE', $this->driver->getOperator('ILIKE')); + $this->assertEquals('', $this->driver->getOperator('FOO')); + } + + public function testSchemaVersion() + { + $this->assertEquals(0, $this->driver->getSchemaVersion()); + + $this->driver->setSchemaVersion(1); + $this->assertEquals(1, $this->driver->getSchemaVersion()); + + $this->driver->setSchemaVersion(42); + $this->assertEquals(42, $this->driver->getSchemaVersion()); + } + + public function testLastInsertId() + { + $this->assertEquals(0, $this->driver->getLastId()); + + $this->driver->getConnection()->exec('CREATE TABLE foobar (id INTEGER PRIMARY KEY, something TEXT)'); + $this->driver->getConnection()->exec('INSERT INTO foobar (something) VALUES (1)'); + + $this->assertEquals(1, $this->driver->getLastId()); + } + + public function testEscape() + { + $this->assertEquals('"foobar"', $this->driver->escape('foobar')); + } +} diff --git a/vendor/fguillot/picodb/tests/SqliteSchemaTest.php b/vendor/fguillot/picodb/tests/SqliteSchemaTest.php new file mode 100644 index 0000000..6ae60f7 --- /dev/null +++ b/vendor/fguillot/picodb/tests/SqliteSchemaTest.php @@ -0,0 +1,30 @@ +db = new PicoDb\Database(array('driver' => 'sqlite', 'filename' => ':memory:')); + } + + public function testMigrations() + { + $this->assertTrue($this->db->schema()->check(2)); + $this->assertEquals(2, $this->db->getDriver()->getSchemaVersion()); + } + + public function testFailedMigrations() + { + $this->assertFalse($this->db->schema()->check(3)); + $this->assertEquals(0, $this->db->getDriver()->getSchemaVersion()); + + $logs = $this->db->getLogMessages(); + $this->assertNotEmpty($logs); + $this->assertEquals('\Schema\version_3 => SQLSTATE[HY000]: General error: 1 near "TABL": syntax error', $logs[0]); + } +} diff --git a/vendor/fguillot/picodb/tests/SqliteTableTest.php b/vendor/fguillot/picodb/tests/SqliteTableTest.php new file mode 100644 index 0000000..5d10900 --- /dev/null +++ b/vendor/fguillot/picodb/tests/SqliteTableTest.php @@ -0,0 +1,327 @@ +db = new Database(array('driver' => 'sqlite', 'filename' => ':memory:')); + } + + public function testSelect() + { + $this->assertEquals('SELECT 1 FROM "test"', $this->db->table('test')->select(1)->buildSelectQuery()); + } + + public function testColumns() + { + $this->assertEquals('SELECT "a", "b" FROM "test"', $this->db->table('test')->columns('a', 'b')->buildSelectQuery()); + } + + public function testDistinct() + { + $this->assertEquals('SELECT DISTINCT "a", "b" FROM "test"', $this->db->table('test')->distinct('a', 'b')->buildSelectQuery()); + } + + public function testGroupBy() + { + $this->assertEquals('SELECT * FROM "test" GROUP BY "a"', $this->db->table('test')->groupBy('a')->buildSelectQuery()); + } + + public function testOrderBy() + { + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" ASC', $this->db->table('test')->asc('a')->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" ASC', $this->db->table('test')->orderBy('a', Table::SORT_ASC)->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC', $this->db->table('test')->desc('a', Table::SORT_DESC)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC', $this->db->table('test')->orderBy('a', Table::SORT_DESC)->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" ASC, "b" ASC', $this->db->table('test')->asc('a')->asc('b')->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC, "b" DESC', $this->db->table('test')->desc('a')->desc('b')->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" ASC, "b" ASC', $this->db->table('test')->orderBy('a')->orderBy('b')->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC, "b" DESC', $this->db->table('test')->orderBy('a', Table::SORT_DESC)->orderBy('b', Table::SORT_DESC)->buildSelectQuery()); + + $this->assertEquals('SELECT * FROM "test" ORDER BY "a" DESC, "b" ASC', $this->db->table('test')->desc('a')->asc('b')->buildSelectQuery()); + } + + public function testLimit() + { + $this->assertEquals('SELECT * FROM "test" LIMIT 10', $this->db->table('test')->limit(10)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test"', $this->db->table('test')->limit(null)->buildSelectQuery()); + } + + public function testOffset() + { + $this->assertEquals('SELECT * FROM "test" OFFSET 0', $this->db->table('test')->offset(0)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" OFFSET 10', $this->db->table('test')->offset(10)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test"', $this->db->table('test')->limit(null)->buildSelectQuery()); + } + + public function testLimitOffset() + { + $this->assertEquals('SELECT * FROM "test" LIMIT 2 OFFSET 0', $this->db->table('test')->offset(0)->limit(2)->buildSelectQuery()); + $this->assertEquals('SELECT * FROM "test" LIMIT 5 OFFSET 10', $this->db->table('test')->offset(10)->limit(5)->buildSelectQuery()); + } + + public function testSubquery() + { + $this->assertEquals('SELECT (SELECT 1 FROM "foobar" WHERE 1=1) AS "b" FROM "test"', $this->db->table('test')->subquery('SELECT 1 FROM "foobar" WHERE 1=1', 'b')->buildSelectQuery()); + } + + public function testConditionEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" = ? AND "b" = ?', $table->eq('a', 2)->eq('b', 'foobar')->buildSelectQuery()); + $this->assertEquals(array(2, 'foobar'), $table->condition->getValues()); + } + + public function testConditionNotEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" != ?', $table->neq('a', 2)->buildSelectQuery()); + $this->assertEquals(array(2), $table->condition->getValues()); + } + + public function testConditionIn() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" IN (?, ?)', $table->in('a', array('b', 'c'))->buildSelectQuery()); + $this->assertEquals(array('b', 'c'), $table->condition->getValues()); + + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test"', $table->in('a', array())->buildSelectQuery()); + $this->assertEquals(array(), $table->condition->getValues()); + } + + public function testConditionNotIn() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" NOT IN (?, ?)', $table->notin('a', array('b', 'c'))->buildSelectQuery()); + $this->assertEquals(array('b', 'c'), $table->condition->getValues()); + + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test"', $table->notin('a', array())->buildSelectQuery()); + $this->assertEquals(array(), $table->condition->getValues()); + } + + public function testConditionLike() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" LIKE ?', $table->like('a', '%foobar%')->buildSelectQuery()); + $this->assertEquals(array('%foobar%'), $table->condition->getValues()); + } + + public function testConditionILike() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" LIKE ?', $table->ilike('a', '%foobar%')->buildSelectQuery()); + $this->assertEquals(array('%foobar%'), $table->condition->getValues()); + } + + public function testConditionGreaterThan() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" > ?', $table->gt('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionGreaterThanOrEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" >= ?', $table->gte('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionLowerThan() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" < ?', $table->lt('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionLowerThanOrEqual() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" <= ?', $table->lte('a', 5)->buildSelectQuery()); + $this->assertEquals(array(5), $table->condition->getValues()); + } + + public function testConditionIsNull() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" IS NOT NULL', $table->notNull('a')->buildSelectQuery()); + $this->assertEquals(array(), $table->condition->getValues()); + } + + public function testCustomCondition() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE a=c AND "b" = ?', $table->addCondition('a=c')->eq('b', 4)->buildSelectQuery()); + $this->assertEquals(array(4), $table->condition->getValues()); + } + + public function testOrConditions() + { + $table = $this->db->table('test'); + + $this->assertEquals('SELECT * FROM "test" WHERE "a" IS NOT NULL AND ("b" = ? OR "c" >= ?)', $table->notNull('a')->beginOr()->eq('b', 2)->gte('c', 5)->closeOr()->buildSelectQuery()); + $this->assertEquals(array(2, 5), $table->condition->getValues()); + } + + public function testInsertUpdate() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (a TEXT)')); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 'b'))); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 'c'))); + + $this->assertEquals(array(array('a' => 'b'), array('a' => 'c')), $this->db->table('foobar')->findAll()); + + $this->assertEquals(array('b', 'c'), $this->db->table('foobar')->findAllByColumn('a')); + + $this->assertEquals(array('a' => 'b'), $this->db->table('foobar')->findOne()); + + $this->assertEquals('b', $this->db->table('foobar')->findOneColumn('a')); + + $this->assertTrue($this->db->table('foobar')->exists()); + $this->assertTrue($this->db->table('foobar')->eq('a', 'c')->exists()); + $this->assertFalse($this->db->table('foobar')->eq('a', 'e')->exists()); + + $this->assertEquals(2, $this->db->table('foobar')->count()); + $this->assertEquals(1, $this->db->table('foobar')->eq('a', 'c')->count()); + $this->assertEquals(0, $this->db->table('foobar')->eq('a', 'e')->count()); + + $this->assertTrue($this->db->table('foobar')->eq('a', 'c')->remove()); + $this->assertFalse($this->db->table('foobar')->eq('a', 'e')->remove()); + + $this->assertTrue($this->db->table('foobar')->eq('a', 'b')->update(array('a' => 'test'))); + $this->assertTrue($this->db->table('foobar')->eq('a', 'lol')->update(array('a' => 'test'))); + + $this->assertNotEmpty($this->db->table('foobar')->eq('a', 'test')->findOne()); + $this->assertNull($this->db->table('foobar')->eq('a', 'lol')->findOne()); + + $this->assertTrue($this->db->table('foobar')->eq('a', 'test')->save(array('a' => 'plop'))); + $this->assertEquals(1, $this->db->table('foobar')->count()); + } + + public function testSumColumn() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (b REAL, c REAL)')); + $this->assertTrue($this->db->table('foobar')->insert(array('b' => 2, 'c' => 3.3))); + + $this->assertTrue($this->db->table('foobar')->sumColumn('b', 2.5)->sumColumn('c', 3)->update()); + + $this->assertEquals( + array('b' => 4.5, 'c' => 6.3), + $this->db->table('foobar')->findOne() + ); + } + + public function testCallback() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (a TEXT)')); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 'b'))); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 'c'))); + + $func = function (array $records) { + return array('test'); + }; + + $this->assertEquals(array('test'), $this->db->table('foobar')->callback($func)->findAll()); + $this->assertEquals(array('plop'), $this->db->table('foobar')->callback(array($this, 'myCallback'))->findAll()); + } + + public function myCallback(array $records) + { + $this->assertEquals(array(array('a' => 'b'), array('a' => 'c')), $records); + return array('plop'); + } + + public function testSum() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE foobar (a INTEGER)')); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 2))); + $this->assertTrue($this->db->table('foobar')->insert(array('a' => 5))); + $this->assertEquals(7, $this->db->table('foobar')->sum('a')); + } + + public function testLeftJoin() + { + $this->assertNotFalse($this->db->execute('CREATE TABLE test1 (a INTEGER NOT NULL, foreign_key INTEGER NOT NULL)')); + $this->assertNotFalse($this->db->execute('CREATE TABLE test2 (id INTEGER NOT NULL, b INTEGER NOT NULL)')); + + $this->assertTrue($this->db->table('test2')->insert(array('id' => 42, 'b' => 2))); + $this->assertTrue($this->db->table('test1')->insert(array('a' => 18, 'foreign_key' => 42))); + + $this->assertEquals( + array('a' => 18, 'b' => 2), + $this->db->table('test2')->columns('a', 'b')->eq('a', 18)->left('test1', 't1', 'foreign_key', 'test2', 'id')->findOne() + ); + + $this->assertEquals( + array('a' => 18, 'b' => 2), + $this->db->table('test2')->columns('a', 'b')->eq('a', 18)->join('test1', 'foreign_key', 'id')->findOne() + ); + + $this->assertEquals( + array('a' => 18, 'b' => 2), + $this->db->table('test1')->columns('a', 'b')->join('test2', 'id', 'foreign_key')->findOne() + ); + } + + public function testHashTable() + { + $this->assertNotFalse($this->db->execute( + 'CREATE TABLE toto ( + column1 TEXT NOT NULL UNIQUE, + column2 TEXT default NULL + )' + )); + + $this->assertTrue($this->db->table('toto')->insert(array('column1' => 'option1', 'column2' => 'value1'))); + $this->assertTrue($this->db->table('toto')->insert(array('column1' => 'option2', 'column2' => 'value2'))); + $this->assertTrue($this->db->table('toto')->insert(array('column1' => 'option3', 'column2' => 'value3'))); + + $values = array( + 'option1' => 'hey', + 'option4' => 'ho', + ); + + $this->assertTrue($this->db->hashtable('toto')->columnKey('column1')->columnValue('column2')->put($values)); + + $this->assertEquals( + array('option2' => 'value2', 'option4' => 'ho'), + $this->db->hashtable('toto')->columnKey('column1')->columnValue('column2')->get('option2', 'option4') + ); + + $this->assertEquals( + array('option2' => 'value2', 'option3' => 'value3', 'option1' => 'hey', 'option4' => 'ho'), + $this->db->hashtable('toto')->columnKey('column1')->columnValue('column2')->get() + ); + + $this->assertEquals( + array('option2' => 'value2', 'option3' => 'value3', 'option1' => 'hey', 'option4' => 'ho'), + $this->db->hashtable('toto')->getAll('column1', 'column2') + ); + } +}