Update dependencies

This commit is contained in:
Frederic Guillot 2015-08-14 21:33:39 -04:00
parent 168fd180e1
commit 4f7ea89925
51 changed files with 3675 additions and 983 deletions

View File

@ -41,7 +41,7 @@ defined('ENABLE_HSTS') or define('ENABLE_HSTS', true);
require __DIR__.'/check_setup.php'; require __DIR__.'/check_setup.php';
PicoDb\Database::bootstrap('db', function() { PicoDb\Database::setInstance('db', function() {
$db = new PicoDb\Database(array( $db = new PicoDb\Database(array(
'driver' => 'sqlite', 'driver' => 'sqlite',

View File

@ -4,11 +4,16 @@
}, },
"require": { "require": {
"fguillot/simple-validator": "v0.0.3", "fguillot/simple-validator": "v0.0.3",
"fguillot/json-rpc": "v0.0.3", "fguillot/json-rpc": "v1.0.1",
"fguillot/picodb": "v0.0.3", "fguillot/picodb": "v1.0.1",
"fguillot/picofeed": "v0.1.8", "fguillot/picofeed": "v0.1.8",
"fguillot/picofarad": "dev-master" "fguillot/picofarad": "dev-master"
}, },
"require-dev": {
"phpunit/phpunit": "4.8.3",
"phpunit/phpunit-selenium": "1.4.2",
"phpunit/dbunit": "1.4.1"
},
"autoload": { "autoload": {
"files": [ "files": [
"lib/helpers.php", "lib/helpers.php",

View File

@ -99,7 +99,7 @@ Router\get_action('generate-tokens', function() {
Router\get_action('optimize-db', function() { Router\get_action('optimize-db', function() {
if (Model\Config\check_csrf(Request\param('csrf'))) { if (Model\Config\check_csrf(Request\param('csrf'))) {
Database::get('db')->getConnection()->exec('VACUUM'); Database::getInstance('db')->getConnection()->exec('VACUUM');
} }
Response\redirect('?action=database'); Response\redirect('?action=database');

View File

@ -12,7 +12,7 @@ Router\get_action('history', function() {
$nb_items = Model\Item\count_by_status('read'); $nb_items = Model\Item\count_by_status('read');
$items = Model\Item\get_all_by_status( $items = Model\Item\get_all_by_status(
'read', 'read',
null, array(),
$offset, $offset,
Model\Config\get('items_per_page'), Model\Config\get('items_per_page'),
'updated', 'updated',

View File

@ -15,7 +15,7 @@ Router\get_action('unread', function() {
$direction = Request\param('direction', Model\Config\get('items_sorting_direction')); $direction = Request\param('direction', Model\Config\get('items_sorting_direction'));
$offset = Request\int_param('offset', 0); $offset = Request\int_param('offset', 0);
$group_id = Request\int_param('group_id', null); $group_id = Request\int_param('group_id', null);
$feed_ids = null; $feed_ids = array();
if (!is_null($group_id)) { if (!is_null($group_id)) {
$feed_ids = Model\Group\get_feeds_by_group($group_id); $feed_ids = Model\Group\get_feeds_by_group($group_id);

View File

@ -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; $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) { if ($update_interval !== null && $call_interval !== null && $limit === Model\Feed\LIMIT_ALL && $update_interval >= $call_interval) {
$feeds_count = PicoDb\Database::getInstance('db')->table('feeds')->count();
$feeds_count = PicoDb\Database::get('db')->table('feeds')->count();
$limit = ceil($feeds_count / ($update_interval / $call_interval)); $limit = ceil($feeds_count / ($update_interval / $call_interval));
} }

View File

@ -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']); $api_key = md5($credentials['username'].':'.$credentials['fever_token']);
$response = array( $response = array(
@ -118,7 +118,7 @@ route('favicons', function() {
if ($response['auth']) { if ($response['auth']) {
$favicons = Database::get('db') $favicons = Database::getInstance('db')
->table('favicons') ->table('favicons')
->columns( ->columns(
'feed_id', 'feed_id',
@ -145,7 +145,7 @@ route('items', function() {
if ($response['auth']) { if ($response['auth']) {
$query = Database::get('db') $query = Database::getInstance('db')
->table('items') ->table('items')
->columns( ->columns(
'rowid', 'rowid',
@ -187,7 +187,7 @@ route('items', function() {
); );
} }
$response['total_items'] = Database::get('db') $response['total_items'] = Database::getInstance('db')
->table('items') ->table('items')
->neq('status', 'removed') ->neq('status', 'removed')
->count(); ->count();
@ -215,7 +215,7 @@ route('unread_item_ids', function() {
if ($response['auth']) { if ($response['auth']) {
$item_ids = Database::get('db') $item_ids = Database::getInstance('db')
->table('items') ->table('items')
->eq('status', 'unread') ->eq('status', 'unread')
->findAllByColumn('rowid'); ->findAllByColumn('rowid');
@ -233,7 +233,7 @@ route('saved_item_ids', function() {
if ($response['auth']) { if ($response['auth']) {
$item_ids = Database::get('db') $item_ids = Database::getInstance('db')
->table('items') ->table('items')
->eq('bookmark', 1) ->eq('bookmark', 1)
->findAllByColumn('rowid'); ->findAllByColumn('rowid');
@ -251,7 +251,7 @@ route('write_items', function() {
if ($response['auth']) { if ($response['auth']) {
$query = Database::get('db') $query = Database::getInstance('db')
->table('items') ->table('items')
->eq('rowid', $_POST['id']); ->eq('rowid', $_POST['id']);
@ -259,7 +259,7 @@ route('write_items', function() {
$query->update(array('bookmark' => 1)); $query->update(array('bookmark' => 1));
// Send bookmark to third-party services if enabled // Send bookmark to third-party services if enabled
$item_id = Database::get('db') $item_id = Database::getInstance('db')
->table('items') ->table('items')
->eq('rowid', $_POST['id']) ->eq('rowid', $_POST['id'])
->findOneColumn('id'); ->findOneColumn('id');
@ -287,7 +287,7 @@ route('write_feeds', function() {
if ($response['auth']) { if ($response['auth']) {
Database::get('db') Database::getInstance('db')
->table('items') ->table('items')
->eq('feed_id', $_POST['id']) ->eq('feed_id', $_POST['id'])
->lte('updated', $_POST['before']) ->lte('updated', $_POST['before'])
@ -303,7 +303,7 @@ route('write_groups', function() {
$response = auth(); $response = auth();
if ($response['auth']) { if ($response['auth']) {
$db = Database::get('db') $db = Database::getInstance('db')
->table('items') ->table('items')
->lte('updated', $_POST['before']); ->lte('updated', $_POST['before']);

View File

@ -4,10 +4,11 @@ require __DIR__.'/common.php';
use JsonRPC\Server; use JsonRPC\Server;
use PicoFeed\PicoFeedException; use PicoFeed\PicoFeedException;
use Model\Config;
$server = new Server; $server = new Server;
$server->authentication(array( $server->authentication(array(
\Model\Config\get('username') => \Model\Config\get('api_token') Config\get('username') => Config\get('api_token')
)); ));
// Get version // Get version
@ -115,7 +116,7 @@ $server->register('item.bookmark.delete', function ($item_id) {
// Get all unread items // Get all unread items
$server->register('item.list_unread', function ($offset = null, $limit = null) { $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 // Count all unread items
@ -127,7 +128,7 @@ $server->register('item.count_unread', function () {
// Get all read items // Get all read items
$server->register('item.list_read', function ($offset = null, $limit = null) { $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 // Count all read items

View File

@ -255,14 +255,14 @@ function new_tokens()
'fever_token' => substr(generate_token(), 0, 8), '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 // Get a config value from the DB or from the session
function get($name) function get($name)
{ {
if (! isset($_SESSION)) { if (! isset($_SESSION)) {
return current(Database::get('db')->hashtable('settings')->get($name)); return current(Database::getInstance('db')->hashtable('settings')->get($name));
} }
else { else {
@ -281,10 +281,8 @@ function get($name)
// Get all config parameters // Get all config parameters
function get_all() function get_all()
{ {
$config = Database::get('db')->hashtable('settings')->get(); $config = Database::getInstance('db')->hashtable('settings')->get();
unset($config['password']); unset($config['password']);
return $config; 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 the user does not want content of feeds, remove it in previous ones
if (isset($values['nocontent']) && (bool) $values['nocontent']) { 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(); reload();
return true; return true;
} }

View File

@ -21,7 +21,7 @@ const LIMIT_ALL = -1;
// Store the favicon // Store the favicon
function store_favicon($feed_id, $link, $icon) function store_favicon($feed_id, $link, $icon)
{ {
return Database::get('db') return Database::getInstance('db')
->table('favicons') ->table('favicons')
->save(array( ->save(array(
'feed_id' => $feed_id, '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 // Return true if the feed have a favicon
function has_favicon($feed_id) 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 // Get favicons for those feeds
@ -58,7 +58,7 @@ function get_favicons(array $feed_ids)
return array(); return array();
} }
$db = Database::get('db') $db = Database::getInstance('db')
->hashtable('favicons') ->hashtable('favicons')
->columnKey('feed_id') ->columnKey('feed_id')
->columnValue('icon'); ->columnValue('icon');
@ -86,7 +86,7 @@ function get_all_favicons()
return array(); return array();
} }
return Database::get('db') return Database::getInstance('db')
->hashtable('favicons') ->hashtable('favicons')
->getAll('feed_id', 'icon'); ->getAll('feed_id', 'icon');
} }
@ -94,9 +94,9 @@ function get_all_favicons()
// Update feed information // Update feed information
function update(array $values) function update(array $values)
{ {
Database::get('db')->startTransaction(); Database::getInstance('db')->startTransaction();
$result = Database::get('db') $result = Database::getInstance('db')
->table('feeds') ->table('feeds')
->eq('id', $values['id']) ->eq('id', $values['id'])
->save(array( ->save(array(
@ -111,12 +111,12 @@ function update(array $values)
if ($result) { if ($result) {
if (! Group\update_feed_groups($values['id'], $values['feed_group_ids'], $values['create_group'])) { if (! Group\update_feed_groups($values['id'], $values['feed_group_ids'], $values['create_group'])) {
Database::get('db')->cancelTransaction(); Database::getInstance('db')->cancelTransaction();
$result = false; $result = false;
} }
} }
Database::get('db')->closeTransaction(); Database::getInstance('db')->closeTransaction();
return $result; return $result;
} }
@ -136,7 +136,7 @@ function import_opml($content)
if ($feeds) { if ($feeds) {
$db = Database::get('db'); $db = Database::getInstance('db');
$db->startTransaction(); $db->startTransaction();
foreach ($feeds as $feed) { foreach ($feeds as $feed) {
@ -168,7 +168,7 @@ function create($url, $enable_grabber = false, $force_rtl = false, $cloak_referr
{ {
$feed_id = false; $feed_id = false;
$db = Database::get('db'); $db = Database::getInstance('db');
// Discover the feed // Discover the feed
$reader = new Reader(Config\get_reader_config()); $reader = new Reader(Config\get_reader_config());
@ -206,7 +206,7 @@ function create($url, $enable_grabber = false, $force_rtl = false, $cloak_referr
)); ));
if ($result) { if ($result) {
$feed_id = $db->getConnection()->getLastId(); $feed_id = $db->getLastId();
Group\update_feed_groups($feed_id, $group_ids, $create_group); Group\update_feed_groups($feed_id, $group_ids, $create_group);
Item\update_all($feed_id, $feed->getItems()); Item\update_all($feed_id, $feed->getItems());
@ -224,7 +224,7 @@ function refresh_all($limit = LIMIT_ALL)
} }
// Auto-vacuum for people using the cronjob // Auto-vacuum for people using the cronjob
Database::get('db')->getConnection()->exec('VACUUM'); Database::getInstance('db')->getConnection()->exec('VACUUM');
return true; return true;
} }
@ -266,7 +266,7 @@ function refresh($feed_id)
// Don't fetch previous items, only new one // Don't fetch previous items, only new one
$parser->setGrabberIgnoreUrls( $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 // Get the list of feeds ID to refresh
function get_ids($limit = LIMIT_ALL) 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) { if ($limit !== LIMIT_ALL) {
$query->limit((int) $limit); $query->limit((int) $limit);
@ -310,7 +310,7 @@ function get_ids($limit = LIMIT_ALL)
// get number of feeds with errors // get number of feeds with errors
function count_failed_feeds() function count_failed_feeds()
{ {
return Database::get('db') return Database::getInstance('db')
->table('feeds') ->table('feeds')
->eq('parsing_error', '1') ->eq('parsing_error', '1')
->count(); ->count();
@ -319,7 +319,7 @@ function count_failed_feeds()
// Get all feeds // Get all feeds
function get_all() function get_all()
{ {
return Database::get('db') return Database::getInstance('db')
->table('feeds') ->table('feeds')
->asc('title') ->asc('title')
->findAll(); ->findAll();
@ -328,7 +328,7 @@ function get_all()
// Get all feeds with the number unread/total items in the order failed, working, disabled // Get all feeds with the number unread/total items in the order failed, working, disabled
function get_all_item_counts() function get_all_item_counts()
{ {
return Database::get('db') return Database::getInstance('db')
->table('feeds') ->table('feeds')
->columns( ->columns(
'feeds.*', 'feeds.*',
@ -346,7 +346,7 @@ function get_all_item_counts()
// Get unread/total count for one feed // Get unread/total count for one feed
function count_items($feed_id) function count_items($feed_id)
{ {
$counts = Database::get('db') $counts = Database::getInstance('db')
->table('items') ->table('items')
->columns('status', 'count(*) as item_count') ->columns('status', 'count(*) as item_count')
->in('status', array('read', 'unread')) ->in('status', array('read', 'unread'))
@ -374,7 +374,7 @@ function count_items($feed_id)
// Get one feed // Get one feed
function get($feed_id) function get($feed_id)
{ {
return Database::get('db') return Database::getInstance('db')
->table('feeds') ->table('feeds')
->eq('id', $feed_id) ->eq('id', $feed_id)
->findOne(); ->findOne();
@ -383,13 +383,13 @@ function get($feed_id)
// Update parsing error column // Update parsing error column
function update_parsing_error($feed_id, $value) 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 // Update last check date
function update_last_checked($feed_id) function update_last_checked($feed_id)
{ {
Database::get('db') Database::getInstance('db')
->table('feeds') ->table('feeds')
->eq('id', $feed_id) ->eq('id', $feed_id)
->save(array( ->save(array(
@ -400,7 +400,7 @@ function update_last_checked($feed_id)
// Update Etag and last Modified columns // Update Etag and last Modified columns
function update_cache($feed_id, $last_modified, $etag) function update_cache($feed_id, $last_modified, $etag)
{ {
Database::get('db') Database::getInstance('db')
->table('feeds') ->table('feeds')
->eq('id', $feed_id) ->eq('id', $feed_id)
->save(array( ->save(array(
@ -413,25 +413,25 @@ function update_cache($feed_id, $last_modified, $etag)
function remove($feed_id) function remove($feed_id)
{ {
// Items are removed by a sql constraint // 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 // Remove all feeds
function remove_all() function remove_all()
{ {
return Database::get('db')->table('feeds')->remove(); return Database::getInstance('db')->table('feeds')->remove();
} }
// Enable a feed (activate refresh) // Enable a feed (activate refresh)
function enable($feed_id) 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 // Disable feed
function disable($feed_id) 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 // Validation for edit

View File

@ -11,7 +11,7 @@ use PicoDb\Database;
*/ */
function get_all() function get_all()
{ {
return Database::get('db') return Database::getInstance('db')
->table('groups') ->table('groups')
->orderBy('title') ->orderBy('title')
->findAll(); ->findAll();
@ -24,7 +24,7 @@ function get_all()
*/ */
function get_map() function get_map()
{ {
$result = Database::get('db') $result = Database::getInstance('db')
->table('feeds_groups') ->table('feeds_groups')
->findAll(); ->findAll();
@ -55,7 +55,7 @@ function get_map()
*/ */
function get_feed_group_ids($feed_id) function get_feed_group_ids($feed_id)
{ {
return Database::get('db') return Database::getInstance('db')
->table('groups') ->table('groups')
->join('feeds_groups', 'group_id', 'id') ->join('feeds_groups', 'group_id', 'id')
->eq('feed_id', $feed_id) ->eq('feed_id', $feed_id)
@ -70,7 +70,7 @@ function get_feed_group_ids($feed_id)
*/ */
function get_group_id($title) function get_group_id($title)
{ {
return Database::get('db') return Database::getInstance('db')
->table('groups') ->table('groups')
->eq('title', $title) ->eq('title', $title)
->findOneColumn('id'); ->findOneColumn('id');
@ -84,7 +84,7 @@ function get_group_id($title)
*/ */
function get_feeds_by_group($group_id) function get_feeds_by_group($group_id)
{ {
return Database::get('db') return Database::getInstance('db')
->table('feeds_groups') ->table('feeds_groups')
->eq('group_id', $group_id) ->eq('group_id', $group_id)
->findAllByColumn('feed_id'); ->findAllByColumn('feed_id');
@ -108,7 +108,7 @@ function create($title)
// create group if missing // create group if missing
if ($group_id === false) { if ($group_id === false) {
Database::get('db') Database::getInstance('db')
->table('groups') ->table('groups')
->insert($data); ->insert($data);
@ -130,7 +130,7 @@ function add($feed_id, $group_ids)
foreach ($group_ids as $group_id){ foreach ($group_ids as $group_id){
$data = array('feed_id' => $feed_id, 'group_id' => $group_id); $data = array('feed_id' => $feed_id, 'group_id' => $group_id);
$result = Database::get('db') $result = Database::getInstance('db')
->table('feeds_groups') ->table('feeds_groups')
->insert($data); ->insert($data);
@ -151,7 +151,7 @@ function add($feed_id, $group_ids)
*/ */
function remove($feed_id, $group_ids) function remove($feed_id, $group_ids)
{ {
return Database::get('db') return Database::getInstance('db')
->table('feeds_groups') ->table('feeds_groups')
->eq('feed_id', $feed_id) ->eq('feed_id', $feed_id)
->in('group_id', $group_ids) ->in('group_id', $group_ids)
@ -163,14 +163,14 @@ function remove($feed_id, $group_ids)
*/ */
function purge_groups() function purge_groups()
{ {
$groups = Database::get('db') $groups = Database::getInstance('db')
->table('groups') ->table('groups')
->join('feeds_groups', 'group_id', 'id') ->join('feeds_groups', 'group_id', 'id')
->isnull('feed_id') ->isnull('feed_id')
->findAllByColumn('id'); ->findAllByColumn('id');
if (! empty($groups)) { if (! empty($groups)) {
Database::get('db') Database::getInstance('db')
->table('groups') ->table('groups')
->in('id', $groups) ->in('id', $groups)
->remove(); ->remove();

View File

@ -12,7 +12,7 @@ use PicoFeed\Scraper\Scraper;
// Get all items without filtering // Get all items without filtering
function get_all() function get_all()
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->columns( ->columns(
'items.id', 'items.id',
@ -39,7 +39,7 @@ function get_all()
// Get everthing since date (timestamp) // Get everthing since date (timestamp)
function get_all_since($timestamp) function get_all_since($timestamp)
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->columns( ->columns(
'items.id', 'items.id',
@ -66,7 +66,7 @@ function get_all_since($timestamp)
function get_latest_feeds_items() function get_latest_feeds_items()
{ {
return Database::get('db') return Database::getInstance('db')
->table('feeds') ->table('feeds')
->columns( ->columns(
'feeds.id', 'feeds.id',
@ -82,7 +82,7 @@ function get_latest_feeds_items()
// Get a list of [item_id => status,...] // Get a list of [item_id => status,...]
function get_all_status() function get_all_status()
{ {
return Database::get('db') return Database::getInstance('db')
->hashtable('items') ->hashtable('items')
->in('status', array('read', 'unread')) ->in('status', array('read', 'unread'))
->orderBy('updated', 'desc') ->orderBy('updated', 'desc')
@ -90,9 +90,9 @@ function get_all_status()
} }
// Get all items by 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') ->table('items')
->columns( ->columns(
'items.id', '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 // 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') ->table('items')
->eq('status', $status) ->eq('status', $status)
->in('feed_id', $feed_ids) ->in('feed_id', $feed_ids)
@ -132,7 +132,7 @@ function count_by_status($status, $feed_ids = null)
// Get the number of bookmarks // Get the number of bookmarks
function count_bookmarks() function count_bookmarks()
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('bookmark', 1) ->eq('bookmark', 1)
->in('status', array('read', 'unread')) ->in('status', array('read', 'unread'))
@ -142,7 +142,7 @@ function count_bookmarks()
// Get all bookmarks // Get all bookmarks
function get_bookmarks($offset = null, $limit = null) function get_bookmarks($offset = null, $limit = null)
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->columns( ->columns(
'items.id', 'items.id',
@ -172,7 +172,7 @@ function get_bookmarks($offset = null, $limit = null)
// Get the number of items per feed // Get the number of items per feed
function count_by_feed($feed_id) function count_by_feed($feed_id)
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('feed_id', $feed_id) ->eq('feed_id', $feed_id)
->in('status', array('unread', 'read')) ->in('status', array('unread', 'read'))
@ -182,7 +182,7 @@ function count_by_feed($feed_id)
// Get all items per feed // Get all items per feed
function get_all_by_feed($feed_id, $offset = null, $limit = null, $order_column = 'updated', $order_direction = 'desc') 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') ->table('items')
->columns( ->columns(
'items.id', 'items.id',
@ -211,7 +211,7 @@ function get_all_by_feed($feed_id, $offset = null, $limit = null, $order_column
// Get one item by id // Get one item by id
function get($id) function get($id)
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('id', $id) ->eq('id', $id)
->findOne(); ->findOne();
@ -220,7 +220,7 @@ function get($id)
// Get item naviguation (next/prev items) // Get item naviguation (next/prev items)
function get_nav($item, $status = array('unread'), $bookmark = array(1, 0), $feed_id = null) function get_nav($item, $status = array('unread'), $bookmark = array(1, 0), $feed_id = null)
{ {
$query = Database::get('db') $query = Database::getInstance('db')
->table('items') ->table('items')
->columns('id', 'status', 'title', 'bookmark') ->columns('id', 'status', 'title', 'bookmark')
->neq('status', 'removed') ->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 // Change item status to removed and clear content
function set_removed($id) function set_removed($id)
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('id', $id) ->eq('id', $id)
->save(array('status' => 'removed', 'content' => '')); ->save(array('status' => 'removed', 'content' => ''));
@ -289,7 +289,7 @@ function set_removed($id)
// Change item status to read // Change item status to read
function set_read($id) function set_read($id)
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('id', $id) ->eq('id', $id)
->save(array('status' => 'read')); ->save(array('status' => 'read'));
@ -298,7 +298,7 @@ function set_read($id)
// Change item status to unread // Change item status to unread
function set_unread($id) function set_unread($id)
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('id', $id) ->eq('id', $id)
->save(array('status' => 'unread')); ->save(array('status' => 'unread'));
@ -309,7 +309,7 @@ function set_status($status, array $items)
{ {
if (! in_array($status, array('read', 'unread', 'removed'))) return false; if (! in_array($status, array('read', 'unread', 'removed'))) return false;
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->in('id', $items) ->in('id', $items)
->save(array('status' => $status)); ->save(array('status' => $status));
@ -322,7 +322,7 @@ function set_bookmark_value($id, $value)
Service\push($id); Service\push($id);
} }
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('id', $id) ->eq('id', $id)
->in('status', array('read', 'unread')) ->in('status', array('read', 'unread'))
@ -332,7 +332,7 @@ function set_bookmark_value($id, $value)
// Mark all unread items as read // Mark all unread items as read
function mark_all_as_read() function mark_all_as_read()
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('status', 'unread') ->eq('status', 'unread')
->save(array('status' => 'read')); ->save(array('status' => 'read'));
@ -341,7 +341,7 @@ function mark_all_as_read()
// Mark all read items to removed // Mark all read items to removed
function mark_all_as_removed() function mark_all_as_removed()
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('status', 'read') ->eq('status', 'read')
->eq('bookmark', 0) ->eq('bookmark', 0)
@ -351,7 +351,7 @@ function mark_all_as_removed()
// Mark all items of a feed as read // Mark all items of a feed as read
function mark_feed_as_read($feed_id) function mark_feed_as_read($feed_id)
{ {
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('status', 'unread') ->eq('status', 'unread')
->eq('feed_id', $feed_id) ->eq('feed_id', $feed_id)
@ -364,7 +364,7 @@ function mark_group_as_read($group_id)
// workaround for missing update with join // workaround for missing update with join
$feed_ids = Group\get_feeds_by_group($group_id); $feed_ids = Group\get_feeds_by_group($group_id);
return Database::get('db') return Database::getInstance('db')
->table('items') ->table('items')
->eq('status', 'unread') ->eq('status', 'unread')
->in('feed_id', $feed_ids) ->in('feed_id', $feed_ids)
@ -379,7 +379,7 @@ function autoflush_read()
if ($autoflush > 0) { if ($autoflush > 0) {
// Mark read items removed after X days // Mark read items removed after X days
Database::get('db') Database::getInstance('db')
->table('items') ->table('items')
->eq('bookmark', 0) ->eq('bookmark', 0)
->eq('status', 'read') ->eq('status', 'read')
@ -389,7 +389,7 @@ function autoflush_read()
else if ($autoflush === -1) { else if ($autoflush === -1) {
// Mark read items removed immediately // Mark read items removed immediately
Database::get('db') Database::getInstance('db')
->table('items') ->table('items')
->eq('bookmark', 0) ->eq('bookmark', 0)
->eq('status', 'read') ->eq('status', 'read')
@ -405,7 +405,7 @@ function autoflush_unread()
if ($autoflush > 0) { if ($autoflush > 0) {
// Mark read items removed after X days // Mark read items removed after X days
Database::get('db') Database::getInstance('db')
->table('items') ->table('items')
->eq('bookmark', 0) ->eq('bookmark', 0)
->eq('status', 'unread') ->eq('status', 'unread')
@ -421,7 +421,7 @@ function update_all($feed_id, array $items)
$items_in_feed = array(); $items_in_feed = array();
$db = Database::get('db'); $db = Database::getInstance('db');
$db->startTransaction(); $db->startTransaction();
foreach ($items as $item) { foreach ($items as $item) {
@ -490,7 +490,7 @@ function cleanup($feed_id, array $items_in_feed)
{ {
if (! empty($items_in_feed)) { if (! empty($items_in_feed)) {
$db = Database::get('db'); $db = Database::getInstance('db');
$removed_items = $db $removed_items = $db
->table('items') ->table('items')
@ -558,7 +558,7 @@ function download_content_id($item_id)
if (! Config\get('nocontent')) { if (! Config\get('nocontent')) {
// Save content // Save content
Database::get('db') Database::getInstance('db')
->table('items') ->table('items')
->eq('id', $item['id']) ->eq('id', $item['id'])
->save(array('content' => $content)); ->save(array('content' => $content));

View File

@ -18,7 +18,7 @@ const EXPIRATION = 5184000;
*/ */
function find($token, $sequence) function find($token, $sequence)
{ {
return Database::get('db') return Database::getInstance('db')
->table(TABLE) ->table(TABLE)
->eq('token', $token) ->eq('token', $token)
->eq('sequence', $sequence) ->eq('sequence', $sequence)
@ -34,7 +34,7 @@ function find($token, $sequence)
*/ */
function get_all() function get_all()
{ {
return Database::get('db') return Database::getInstance('db')
->table(TABLE) ->table(TABLE)
->desc('date_creation') ->desc('date_creation')
->columns('id', 'ip', 'user_agent', 'date_creation', 'expiration') ->columns('id', 'ip', 'user_agent', 'date_creation', 'expiration')
@ -111,7 +111,7 @@ function destroy()
if ($credentials !== false) { if ($credentials !== false) {
Database::get('db') Database::getInstance('db')
->table(TABLE) ->table(TABLE)
->eq('token', $credentials['token']) ->eq('token', $credentials['token'])
->remove(); ->remove();
@ -138,7 +138,7 @@ function create($dbname, $username, $ip, $user_agent)
cleanup(); cleanup();
Database::get('db') Database::getInstance('db')
->table(TABLE) ->table(TABLE)
->insert(array( ->insert(array(
'username' => $username, 'username' => $username,
@ -165,7 +165,7 @@ function create($dbname, $username, $ip, $user_agent)
*/ */
function cleanup() function cleanup()
{ {
return Database::get('db') return Database::getInstance('db')
->table(TABLE) ->table(TABLE)
->lt('expiration', time()) ->lt('expiration', time())
->remove(); ->remove();
@ -182,7 +182,7 @@ function update($token)
{ {
$new_sequence = Config\generate_token(); $new_sequence = Config\generate_token();
Database::get('db') Database::getInstance('db')
->table(TABLE) ->table(TABLE)
->eq('token', $token) ->eq('token', $token)
->update(array('sequence' => $new_sequence)); ->update(array('sequence' => $new_sequence));

View File

@ -26,7 +26,7 @@ function logout()
// Get the credentials from the current selected database // Get the credentials from the current selected database
function getCredentials() function getCredentials()
{ {
return Database::get('db') return Database::getInstance('db')
->hashtable('settings') ->hashtable('settings')
->get('username', 'password'); ->get('username', 'password');
} }

View File

@ -6,16 +6,22 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'JsonRPC\\AccessDeniedException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/AccessDeniedException.php',
'JsonRPC\\AuthenticationFailure' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php', 'JsonRPC\\AuthenticationFailure' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
'JsonRPC\\Client' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.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\\InvalidJsonFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
'JsonRPC\\InvalidJsonRpcFormat' => $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\\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\\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\\Mysql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php',
'PicoDb\\Driver\\Postgres' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php', 'PicoDb\\Driver\\Postgres' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php',
'PicoDb\\Driver\\Sqlite' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php', 'PicoDb\\Driver\\Sqlite' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php',
'PicoDb\\Hashtable' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Hashtable.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\\Schema' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Schema.php',
'PicoDb\\Table' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Table.php', 'PicoDb\\Table' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Table.php',
'PicoFeed\\Client\\Client' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Client.php', 'PicoFeed\\Client\\Client' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Client.php',

View File

@ -76,84 +76,6 @@
"description": "The most easy to use validator library for PHP :)", "description": "The most easy to use validator library for PHP :)",
"homepage": "https://github.com/fguillot/simpleValidator" "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", "name": "fguillot/picofeed",
"version": "v0.1.8", "version": "v0.1.8",
@ -202,5 +124,82 @@
], ],
"description": "Modern library to handle RSS/Atom feeds", "description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/fguillot/picoFeed" "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"
} }
] ]

19
vendor/fguillot/json-rpc/.travis.yml vendored Normal file
View File

@ -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

View File

@ -3,6 +3,10 @@ JsonRPC PHP Client and Server
A simple Json-RPC client/server that just works. 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 Features
-------- --------
@ -11,24 +15,19 @@ Features
- Authentication and IP based client restrictions - Authentication and IP based client restrictions
- Minimalist: there is only 2 files - Minimalist: there is only 2 files
- Fully unit tested - Fully unit tested
- Requirements: PHP >= 5.3.4
- License: MIT - License: MIT
Requirements
------------
- The only dependency is the cURL extension
- PHP >= 5.3
Author Author
------ ------
[Frédéric Guillot](http://fredericguillot.com) Frédéric Guillot
Installation with Composer Installation with Composer
-------------------------- --------------------------
```bash ```bash
composer require fguillot/json-rpc dev-master composer require fguillot/json-rpc @stable
``` ```
Examples Examples
@ -214,7 +213,10 @@ All results are stored at the same position of the call.
- `BadFunctionCallException`: Procedure not found on the server - `BadFunctionCallException`: Procedure not found on the server
- `InvalidArgumentException`: Wrong procedure arguments - `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 ### Enable client debugging

View File

@ -6,12 +6,11 @@
"license": "MIT", "license": "MIT",
"authors": [ "authors": [
{ {
"name": "Frédéric Guillot", "name": "Frédéric Guillot"
"homepage": "http://fredericguillot.com"
} }
], ],
"require": { "require": {
"php": ">=5.3.0" "php": ">=5.3.4"
}, },
"autoload": { "autoload": {
"psr-0": {"JsonRPC": "src/"} "psr-0": {"JsonRPC": "src/"}

View File

@ -1,4 +1,4 @@
<phpunit bootstrap="./vendor/autoload.php"> <phpunit bootstrap="./vendor/autoload.php" stopOnError="true" stopOnFailure="true" colors="true">
<testsuites> <testsuites>
<testsuite name="JsonRPC"> <testsuite name="JsonRPC">
<directory>tests</directory> <directory>tests</directory>

View File

@ -0,0 +1,7 @@
<?php
namespace JsonRPC;
class AccessDeniedException extends \Exception
{
}

View File

@ -2,16 +2,19 @@
namespace JsonRPC; namespace JsonRPC;
use Exception;
use RuntimeException; use RuntimeException;
use BadFunctionCallException; use BadFunctionCallException;
use InvalidArgumentException; use InvalidArgumentException;
class ConnectionFailureException extends Exception {};
class ServerErrorException extends Exception {};
/** /**
* JsonRPC client class * JsonRPC client class
* *
* @package JsonRPC * @package JsonRPC
* @author Frederic Guillot * @author Frederic Guillot
* @license Unlicense http://unlicense.org/
*/ */
class Client class Client
{ {
@ -87,47 +90,33 @@ class Client
* @var array * @var array
*/ */
private $headers = array( private $headers = array(
'User-Agent: JSON-RPC PHP Client <https://github.com/fguillot/JsonRPC>',
'Content-Type: application/json', 'Content-Type: application/json',
'Accept: application/json' 'Accept: application/json',
'Connection: close',
); );
/** /**
* SSL certificates verification * SSL certificates verification
*
* @access public * @access public
* @var boolean * @var boolean
*/ */
public $ssl_verify_peer = true; public $ssl_verify_peer = true;
/**
* cURL handle
*
* @access private
*/
private $ch;
/** /**
* Constructor * Constructor
* *
* @access public * @access public
* @param string $url Server URL * @param string $url Server URL
* @param integer $timeout Server URL * @param integer $timeout HTTP timeout
* @param array $headers Custom HTTP headers * @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->url = $url;
$this->timeout = $timeout; $this->timeout = $timeout;
$this->headers = array_merge($this->headers, $headers); $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 * @access public
* @param string $username Username * @param string $username Username
* @param string $password Password * @param string $password Password
* @return Client
*/ */
public function authentication($username, $password) public function authentication($username, $password)
{ {
$this->username = $username; $this->username = $username;
$this->password = $password; $this->password = $password;
return $this;
} }
/** /**
@ -243,7 +235,6 @@ class Client
public function parseResponse(array $payload) public function parseResponse(array $payload)
{ {
if ($this->isBatchResponse($payload)) { if ($this->isBatchResponse($payload)) {
$results = array(); $results = array();
foreach ($payload as $response) { foreach ($payload as $response) {
@ -256,6 +247,113 @@ class Client
return $this->getResult($payload); 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 * Return true if we have a batch response
* *
@ -271,11 +369,11 @@ class Client
/** /**
* Get a RPC call result * Get a RPC call result
* *
* @access public * @access private
* @param array $payload * @param array $payload
* @return mixed * @return mixed
*/ */
public function getResult(array $payload) private function getResult(array $payload)
{ {
if (isset($payload['error']['code'])) { if (isset($payload['error']['code'])) {
$this->handleRpcErrors($payload['error']); $this->handleRpcErrors($payload['error']);
@ -283,68 +381,4 @@ class Client
return isset($payload['result']) ? $payload['result'] : null; 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();
}
} }

View File

@ -19,7 +19,6 @@ class AuthenticationFailure extends Exception {};
* *
* @package JsonRPC * @package JsonRPC
* @author Frederic Guillot * @author Frederic Guillot
* @license Unlicense http://unlicense.org/
*/ */
class Server class Server
{ {
@ -27,9 +26,9 @@ class Server
* Data received from the client * Data received from the client
* *
* @access private * @access private
* @var string * @var array
*/ */
private $payload; private $payload = array();
/** /**
* List of procedures * List of procedures
@ -91,17 +90,28 @@ class Server
* Constructor * Constructor
* *
* @access public * @access public
* @param string $payload Client data * @param string $request
* @param array $callbacks Callbacks
* @param array $classes Classes
*/ */
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->payload = $payload;
$this->callbacks = $callbacks;
$this->classes = $classes;
} }
/** /**
@ -215,9 +225,9 @@ class Server
* @param closure $callback Callback * @param closure $callback Callback
* @return Server * @return Server
*/ */
public function register($name, Closure $callback) public function register($procedure, Closure $callback)
{ {
$this->callbacks[$name] = $callback; $this->callbacks[$procedure] = $callback;
return $this; return $this;
} }
@ -308,18 +318,10 @@ class Server
/** /**
* Parse the payload and test if the parsed JSON is ok * 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)) { if (! is_array($this->payload)) {
throw new InvalidJsonFormat('Malformed payload'); throw new InvalidJsonFormat('Malformed payload');
} }
@ -328,9 +330,9 @@ class Server
/** /**
* Test if all required JSON-RPC parameters are here * Test if all required JSON-RPC parameters are here
* *
* @access public * @access private
*/ */
public function checkRpcFormat() private function checkRpcFormat()
{ {
if (! isset($this->payload['jsonrpc']) || if (! isset($this->payload['jsonrpc']) ||
! isset($this->payload['method']) || ! isset($this->payload['method']) ||
@ -377,10 +379,11 @@ class Server
} }
else { else {
$server = new Server($payload, $this->callbacks, $this->classes); $server = clone($this);
$server->setPayload($payload);
$response = $server->execute(); $response = $server->execute();
if ($response) { if (! empty($response)) {
$responses[] = $response; $responses[] = $response;
} }
} }
@ -457,6 +460,9 @@ class Server
catch (AuthenticationFailure $e) { catch (AuthenticationFailure $e) {
$this->sendAuthenticationFailureResponse(); $this->sendAuthenticationFailureResponse();
} }
catch (AccessDeniedException $e) {
$this->sendForbiddenResponse();
}
catch (Exception $e) { catch (Exception $e) {
foreach ($this->exceptions as $class) { foreach ($this->exceptions as $class) {
@ -537,9 +543,14 @@ class Server
$instance = is_string($class) ? new $class : $class; $instance = is_string($class) ? new $class : $class;
// Execute before action // Execute before action
if (! empty($this->before) && method_exists($instance, $this->before)) { 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); $instance->{$this->before}($this->getUsername(), $this->getPassword(), get_class($class), $method);
} }
}
$reflection = new ReflectionMethod($class, $method); $reflection = new ReflectionMethod($class, $method);

View File

@ -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)); $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() public function testPrepareRequest()
{ {
$client = new Client('http://localhost/'); $client = new Client('http://localhost/');

View File

@ -2,6 +2,14 @@
use JsonRPC\Server; use JsonRPC\Server;
class C
{
public function doSomething()
{
return 'something';
}
}
class ServerProtocolTest extends PHPUnit_Framework_TestCase class ServerProtocolTest extends PHPUnit_Framework_TestCase
{ {
public function testPositionalParameters() public function testPositionalParameters()
@ -165,7 +173,9 @@ class ServerProtocolTest extends PHPUnit_Framework_TestCase
{"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"}, {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
{"foo": "boo"}, {"foo": "boo"},
{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"}, {"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) { $server->register('sum', function($a, $b, $c) {
@ -180,6 +190,10 @@ class ServerProtocolTest extends PHPUnit_Framework_TestCase
return array('hello', 5); return array('hello', 5);
}); });
$server->attach(new C);
$server->bind('doStuff', 'C', 'doSomething');
$response = $server->execute(); $response = $server->execute();
$this->assertEquals( $this->assertEquals(
@ -188,7 +202,9 @@ class ServerProtocolTest extends PHPUnit_Framework_TestCase
{"jsonrpc": "2.0", "result": 19, "id": "2"}, {"jsonrpc": "2.0", "result": 19, "id": "2"},
{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}, {"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", "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), ]', true),
json_decode($response, true) json_decode($response, true)
); );

19
vendor/fguillot/picodb/.travis.yml vendored Normal file
View File

@ -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

View File

@ -2,7 +2,10 @@ PicoDb
====== ======
PicoDb is a minimalist database query builder for PHP. 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 Features
-------- --------
@ -11,15 +14,21 @@ Features
- Supported drivers: Sqlite, Mysql, Postgresql - Supported drivers: Sqlite, Mysql, Postgresql
- Requires only PDO - Requires only PDO
- Use prepared statements - 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 - License: MIT
Requirements Requirements
------------ ------------
- PHP >= 5.3 - PHP >= 5.3
- PDO - PDO extension
- A database: Sqlite, Mysql or Postgresql - Sqlite or Mysql or Postgresql
Author
------
Frédéric Guillot
Documentation Documentation
------------- -------------
@ -30,53 +39,93 @@ Documentation
composer require fguillot/picodb @stable composer require fguillot/picodb @stable
``` ```
### Connect to your database ### Database connection
Sqlite:
```php ```php
use PicoDb\Database; use PicoDb\Database;
// Sqlite driver // Sqlite driver
$db = new Database(['driver' => 'sqlite', 'filename' => ':memory:']); $db = new Database(['driver' => 'sqlite', 'filename' => ':memory:']);
```
// Mysql driver The Sqlite driver enable foreign keys by default.
// Optional options: "schema_table" (the default table name is "schema_version")
$db = new Database(array( Mysql:
```php
// Optional attributes:
// "charset"
// "schema_table" (the default table name is "schema_version")
// "port"
$db = new Database([
'driver' => 'mysql', 'driver' => 'mysql',
'hostname' => 'localhost', 'hostname' => 'localhost',
'username' => 'root', 'username' => 'root',
'password' => '', 'password' => '',
'database' => 'my_db_name', 'database' => 'my_db_name',
'charset' => 'utf8', ]);
));
``` ```
### Execute a SQL request Postgres:
```php ```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 ```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 or
```php ```php
$db->table('toto')->insert(['column1' => 'test']); $db->table('mytable')->insert(['column1' => 'test']);
```
### Fetch last inserted id
```php
$db->getLastId();
``` ```
### Transactions ### Transactions
```php ```php
$db->transaction(function ($db) { $db->transaction(function ($db) {
$db->table('toto')->save(['column1' => 'foo']); $db->table('mytable')->save(['column1' => 'foo']);
$db->table('toto')->save(['column1' => 'bar']); $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 or
```php ```php
@ -91,55 +140,65 @@ $db->cancelTransaction();
### Fetch all data ### Fetch all data
```php ```php
$records = $db->table('toto')->findAll(); $records = $db->table('mytable')->findAll();
foreach ($records as $record) { foreach ($records as $record) {
var_dump($record['column1']); var_dump($record['column1']);
} }
``` ```
### Update something ### Updates
```php ```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. or
### Remove rows
```php ```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 ### Sorting
```php ```php
$db->table('toto')->asc('column1')->findAll(); $db->table('mytable')->asc('column1')->findAll();
``` ```
or or
```php ```php
$db->table('toto')->desc('column1')->findAll(); $db->table('mytable')->desc('column1')->findAll();
``` ```
or or
```php ```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 ### Limit and offset
```php ```php
$db->table('toto')->limit(10)->offset(5)->findAll(); $db->table('mytable')->limit(10)->offset(5)->findAll();
``` ```
### Fetch only some columns ### Fetch only some columns
```php ```php
$db->table('toto')->columns('column1', 'column2')->findAll(); $db->table('mytable')->columns('column1', 'column2')->findAll();
``` ```
### Fetch only one column ### Fetch only one column
@ -147,37 +206,87 @@ $db->table('toto')->columns('column1', 'column2')->findAll();
Many rows: Many rows:
```php ```php
$db->table('toto')->findAllByColumn('column1'); $db->table('mytable')->findAllByColumn('column1');
``` ```
One row: One row:
```php ```php
$db->table('toto')->findOneColumn('column1'); $db->table('mytable')->findOneColumn('column1');
``` ```
### Equals condition ### Custom select
```php ```php
$db->table('toto') $db->table('mytable')->select(1)->eq('id', 42)->findOne();
->equals('column1', 'hey') ```
->findAll();
### 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 or
```php ```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') ->eq('column1', 'hey')
->findAll(); ->findAll();
``` ```
Yout got: 'SELECT * FROM toto WHERE column1=?'
### IN condition ### IN condition
```php ```php
$db->table('toto') $db->table('mytable')
->in('column1', ['hey', 'bla']) ->in('column1', ['hey', 'bla'])
->findAll(); ->findAll();
``` ```
@ -187,7 +296,7 @@ $db->table('toto')
Case-sensitive (only Mysql and Postgres): Case-sensitive (only Mysql and Postgres):
```php ```php
$db->table('toto') $db->table('mytable')
->like('column1', '%Foo%') ->like('column1', '%Foo%')
->findAll(); ->findAll();
``` ```
@ -195,82 +304,66 @@ $db->table('toto')
Not case-sensitive: Not case-sensitive:
```php ```php
$db->table('toto') $db->table('mytable')
->ilike('column1', '%foo%') ->ilike('column1', '%foo%')
->findAll(); ->findAll();
``` ```
### Lower than ### Lower than condition
```php ```php
$db->table('toto') $db->table('mytable')
->lowerThan('column1', 2)
->findAll();
```
or
```php
$db->table('toto')
->lt('column1', 2) ->lt('column1', 2)
->findAll(); ->findAll();
``` ```
### Lower than or equals ### Lower than or equal condition
```php ```php
$db->table('toto') $db->table('mytable')
->lowerThanOrEquals('column1', 2)
->findAll();
```
or
```php
$db->table('toto')
->lte('column1', 2) ->lte('column1', 2)
->findAll(); ->findAll();
``` ```
### Greater than ### Greater than condition
```php ```php
$db->table('toto') $db->table('mytable')
->greaterThan('column1', 3)
->findAll();
```
or
```php
$db->table('toto')
->gt('column1', 3) ->gt('column1', 3)
->findAll(); ->findAll();
``` ```
### Greater than or equals ### Greater than or equal condition
```php ```php
$db->table('toto') $db->table('mytable')
->greaterThanOrEquals('column1', 3) ->gte('column1', 3)
->findAll(); ->findAll();
``` ```
or ### IS NULL condition
```php ```php
$db->table('toto') $db->table('mytable')
->gte('column1', 3) ->isNull('column1')
->findAll();
```
### IS NOT NULL condition
```php
$db->table('mytable')
->notNull('column1')
->findAll(); ->findAll();
``` ```
### Multiple conditions ### Multiple conditions
Each condition is joined by a AND. Add conditions are joined by a `AND`.
```php ```php
$db->table('toto') $db->table('mytable')
->like('column2', '%toto') ->like('column2', '%mytable')
->gte('column1', 3) ->gte('column1', 3)
->findAll(); ->findAll();
``` ```
@ -278,9 +371,9 @@ $db->table('toto')
How to make a OR condition: How to make a OR condition:
```php ```php
$db->table('toto') $db->table('mytable')
->beginOr() ->beginOr()
->like('column2', '%toto') ->like('column2', '%mytable')
->gte('column1', 3) ->gte('column1', 3)
->closeOr() ->closeOr()
->eq('column5', 'titi') ->eq('column5', 'titi')
@ -292,7 +385,7 @@ $db->table('toto')
Log generated queries: Log generated queries:
```php ```php
$db->log_queries = true; $db->logQueries = true;
``` ```
Mesure each query time: Mesure each query time:
@ -304,7 +397,7 @@ $db->stopwatch = true;
Get the number of queries executed: Get the number of queries executed:
```php ```php
echo $db->nb_queries; echo $db->nbQueries;
``` ```
Get log messages: Get log messages:
@ -319,19 +412,19 @@ How to use a table as a key/value store:
```php ```php
$db->execute( $db->execute(
'CREATE TABLE toto ( 'CREATE TABLE mytable (
column1 TEXT NOT NULL UNIQUE, column1 TEXT NOT NULL UNIQUE,
column2 TEXT default NULL column2 TEXT default NULL
)' )'
); );
$db->table('toto')->insert(['column1' => 'option1', 'column2' => 'value1']); $db->table('mytable')->insert(['column1' => 'option1', 'column2' => 'value1']);
``` ```
Add/Replace some values: Add/Replace some values:
```php ```php
$db->hashtable('toto') $db->hashtable('mytable')
->columnKey('column1') ->columnKey('column1')
->columnValue('column2') ->columnValue('column2')
->put(['option1' => 'new value', 'option2' => 'value2'])); ->put(['option1' => 'new value', 'option2' => 'value2']));
@ -340,7 +433,7 @@ $db->hashtable('toto')
Get all values: Get all values:
```php ```php
$result = $db->hashtable('toto')->columnKey('column1')->columnValue('column2')->get(); $result = $db->hashtable('mytable')->columnKey('column1')->columnValue('column2')->get();
print_r($result); print_r($result);
Array Array
@ -353,18 +446,18 @@ Array
or or
```php ```php
$result = $db->hashtable('toto')->getAll('column1', 'column2'); $result = $db->hashtable('mytable')->getAll('column1', 'column2');
``` ```
Get a specific value: Get a specific value:
```php ```php
$db->hashtable('toto') $db->hashtable('mytable')
->columnKey('column1') ->columnKey('column1')
->columnValue('column2') ->columnValue('column2')
->put(['option3' => 'value3']); ->put(['option3' => 'value3']);
$result = $db->hashtable('toto') $result = $db->hashtable('mytable')
->columnKey('column1') ->columnKey('column1')
->columnValue('column2') ->columnValue('column2')
->get('option1', 'option3'); ->get('option1', 'option3');
@ -417,10 +510,9 @@ function version_2($pdo)
#### Run schema update automatically #### Run schema update automatically
- The method "check()" executes all migrations until to reach the correct version number. - The method `check()` execute all migrations until the version specified
- If we are already on the last version nothing will happen. - If an error occurs, the transaction is rollbacked
- The schema version for the driver Sqlite is stored inside a variable (PRAGMA user_version) - Foreign keys checks are disabled if possible during the migration
- You can use that with a dependency injection controller.
Example: Example:
@ -447,7 +539,7 @@ else {
Setup a new instance: Setup a new instance:
```php ```php
PicoDb\Database::bootstrap('myinstance', function() { PicoDb\Database::setInstance('myinstance', function() {
$db = new PicoDb\Database(array( $db = new PicoDb\Database(array(
'driver' => 'sqlite', 'driver' => 'sqlite',
@ -466,5 +558,5 @@ PicoDb\Database::bootstrap('myinstance', function() {
Get this instance anywhere in your code: Get this instance anywhere in your code:
```php ```php
PicoDb\Database::get('myinstance')->table(...) PicoDb\Database::getInstance('myinstance')->table(...)
``` ```

View File

@ -0,0 +1,291 @@
<?php
namespace PicoDb;
/**
* Handle SQL conditions
*
* @author Frederic Guillot
*/
class Condition
{
/**
* Database instance
*
* @access private
* @var Database
*/
private $db;
/**
* Condition values
*
* @access private
* @var array
*/
private $values = array();
/**
* SQL conditions
*
* @access private
* @var array
*/
private $conditions = array();
/**
* SQL OR conditions
*
* @access private
* @var array
*/
private $or = array();
/**
* OR condition started
*
* @access private
* @var boolean
*/
private $beginOr = false;
/**
* Constructor
*
* @access public
* @param Database $db
*/
public function __construct(Database $db)
{
$this->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');
}
}

View File

@ -3,21 +3,24 @@
namespace PicoDb; namespace PicoDb;
use Closure; use Closure;
use PDO;
use PDOException; use PDOException;
use LogicException; use LogicException;
use RuntimeException; use PicoDb\Driver\Sqlite;
use Picodb\Driver\Sqlite; use PicoDb\Driver\Mysql;
use Picodb\Driver\Mysql; use PicoDb\Driver\Postgres;
use Picodb\Driver\Postgres;
/**
* Database
*
* @author Frederic Guillot
*/
class Database class Database
{ {
/** /**
* Database instances * Database instances
* *
* @access private
* @static * @static
* @access private
* @var array * @var array
*/ */
private static $instances = array(); private static $instances = array();
@ -31,12 +34,11 @@ class Database
private $logs = array(); private $logs = array();
/** /**
* PDO instance * Driver instance
* *
* @access private * @access private
* @var PDO
*/ */
private $pdo; private $driver;
/** /**
* Flag to calculate query time * Flag to calculate query time
@ -52,7 +54,7 @@ class Database
* @access public * @access public
* @var boolean * @var boolean
*/ */
public $log_queries = false; public $logQueries = false;
/** /**
* Number of SQL queries executed * Number of SQL queries executed
@ -60,10 +62,10 @@ class Database
* @access public * @access public
* @var integer * @var integer
*/ */
public $nb_queries = 0; public $nbQueries = 0;
/** /**
* Constructor, iniatlize a PDO driver * Initialize the driver
* *
* @access public * @access public
* @param array $settings Connection settings * @param array $settings Connection settings
@ -71,31 +73,22 @@ class Database
public function __construct(array $settings) public function __construct(array $settings)
{ {
if (! isset($settings['driver'])) { 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']) { switch ($settings['driver']) {
case 'sqlite': case 'sqlite':
require_once __DIR__.'/Driver/Sqlite.php'; $this->driver = new Sqlite($settings);
$this->pdo = new Sqlite($settings);
break; break;
case 'mysql': case 'mysql':
require_once __DIR__.'/Driver/Mysql.php'; $this->driver = new Mysql($settings);
$this->pdo = new Mysql($settings);
break; break;
case 'postgres': case 'postgres':
require_once __DIR__.'/Driver/Postgres.php'; $this->driver = new Postgres($settings);
$this->pdo = new Postgres($settings);
break; break;
default: 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 string $name Instance name
* @param Closure $callback Callback * @param Closure $callback Callback
*/ */
public static function bootstrap($name, Closure $callback) public static function setInstance($name, Closure $callback)
{ {
self::$instances[$name] = $callback; self::$instances[$name] = $callback;
} }
@ -129,10 +122,10 @@ class Database
* @param string $name Instance name * @param string $name Instance name
* @return Database * @return Database
*/ */
public static function get($name) public static function getInstance($name)
{ {
if (! isset(self::$instances[$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])) { if (is_callable(self::$instances[$name])) {
@ -168,11 +161,33 @@ class Database
* Get the PDO connection * Get the PDO connection
* *
* @access public * @access public
* @return PDO * @return \PDO
*/ */
public function getConnection() 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() public function closeConnection()
{ {
$this->pdo = null; $this->driver->closeConnection();
} }
/** /**
@ -201,25 +216,44 @@ class Database
} }
if (! empty($table)) { 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 * Execute a prepared statement
* *
* Note: returns false on duplicate keys instead of SQLException
*
* @access public * @access public
* @param string $sql SQL query * @param string $sql SQL query
* @param array $values Values * @param array $values Values
* @return PDOStatement|false * @return \PDOStatement|false
*/ */
public function execute($sql, array $values = array()) public function execute($sql, array $values = array())
{ {
try { try {
if ($this->log_queries) { if ($this->logQueries) {
$this->setLogMessage($sql); $this->setLogMessage($sql);
} }
@ -227,28 +261,19 @@ class Database
$start = microtime(true); $start = microtime(true);
} }
$rq = $this->pdo->prepare($sql); $rq = $this->getConnection()->prepare($sql);
$rq->execute($values); $rq->execute($values);
if ($this->stopwatch) { if ($this->stopwatch) {
$this->setLogMessage('DURATION='.(microtime(true) - $start)); $this->setLogMessage('DURATION='.(microtime(true) - $start));
} }
$this->nb_queries++; $this->nbQueries++;
return $rq; return $rq;
} }
catch (PDOException $e) { catch (PDOException $e) {
return $this->handleSqlError($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() : ''));
} }
} }
@ -261,12 +286,38 @@ class Database
*/ */
public function transaction(Closure $callback) public function transaction(Closure $callback)
{ {
$this->pdo->beginTransaction(); try {
$result = $callback($this); // Rollback is done in the execute() method
$this->startTransaction();
$result = $callback($this);
$this->closeTransaction(); $this->closeTransaction();
return $result === null ? true : $result; 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() : ''));
}
/** /**
* Begin a transaction * Begin a transaction
@ -275,8 +326,8 @@ class Database
*/ */
public function startTransaction() public function startTransaction()
{ {
if (! $this->pdo->inTransaction()) { if (! $this->getConnection()->inTransaction()) {
$this->pdo->beginTransaction(); $this->getConnection()->beginTransaction();
} }
} }
@ -287,8 +338,8 @@ class Database
*/ */
public function closeTransaction() public function closeTransaction()
{ {
if ($this->pdo->inTransaction()) { if ($this->getConnection()->inTransaction()) {
$this->pdo->commit(); $this->getConnection()->commit();
} }
} }
@ -299,8 +350,8 @@ class Database
*/ */
public function cancelTransaction() public function cancelTransaction()
{ {
if ($this->pdo->inTransaction()) { if ($this->getConnection()->inTransaction()) {
$this->pdo->rollback(); $this->getConnection()->rollback();
} }
} }
@ -308,11 +359,11 @@ class Database
* Get a table instance * Get a table instance
* *
* @access public * @access public
* @return Picodb\Table * @param string $table_name
* @return Table
*/ */
public function table($table_name) public function table($table_name)
{ {
require_once __DIR__.'/Table.php';
return new Table($this, $table_name); return new Table($this, $table_name);
} }
@ -320,12 +371,11 @@ class Database
* Get a hashtable instance * Get a hashtable instance
* *
* @access public * @access public
* @return Picodb\Hashtable * @param string $table_name
* @return Hashtable
*/ */
public function hashtable($table_name) public function hashtable($table_name)
{ {
require_once __DIR__.'/Table.php';
require_once __DIR__.'/Hashtable.php';
return new Hashtable($this, $table_name); return new Hashtable($this, $table_name);
} }
@ -333,11 +383,10 @@ class Database
* Get a schema instance * Get a schema instance
* *
* @access public * @access public
* @return Picodb\Schema * @return Schema
*/ */
public function schema() public function schema()
{ {
require_once __DIR__.'/Schema.php';
return new Schema($this); return new Schema($this);
} }
} }

View File

@ -0,0 +1,192 @@
<?php
namespace PicoDb\Driver;
use PDO;
use LogicException;
use PDOException;
/**
* Base Driver class
*
* @author Frederic Guillot
*/
abstract class Base
{
/**
* List of required settings options
*
* @access protected
* @var array
*/
protected $requiredAtttributes = array();
/**
* PDO connection
*
* @access protected
* @var PDO
*/
protected $pdo = null;
/**
* Create a new PDO connection
*
* @abstract
* @access public
* @param array $settings
*/
abstract public function createConnection(array $settings);
/**
* Enable foreign keys
*
* @abstract
* @access public
*/
abstract public function enableForeignKeys();
/**
* Disable foreign keys
*
* @abstract
* @access public
*/
abstract public function disableForeignKeys();
/**
* Return true if the error code is a duplicate key
*
* @abstract
* @access public
* @param integer $code
* @return boolean
*/
abstract public function isDuplicateKeyError($code);
/**
* Escape identifier
*
* @abstract
* @access public
* @param string $identifier
* @return string
*/
abstract public function escape($identifier);
/**
* Get non standard operator
*
* @abstract
* @access public
* @param string $operator
* @return string
*/
abstract public function getOperator($operator);
/**
* Get last inserted id
*
* @abstract
* @access public
* @return integer
*/
abstract public function getLastId();
/**
* Get current schema version
*
* @abstract
* @access public
* @return integer
*/
abstract public function getSchemaVersion();
/**
* Set current schema version
*
* @abstract
* @access public
* @param integer $version
*/
abstract public function setSchemaVersion($version);
/**
* Constructor
*
* @access public
* @param array $settings
*/
public function __construct(array $settings)
{
foreach ($this->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;
}
}
}

View File

@ -3,29 +3,46 @@
namespace PicoDb\Driver; namespace PicoDb\Driver;
use PDO; 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
public function __construct(array $settings) *
{ * @access protected
$required_atttributes = array( * @var array
*/
protected $requiredAtttributes = array(
'hostname', 'hostname',
'username', 'username',
'password', 'password',
'database', 'database',
'charset',
); );
foreach ($required_atttributes as $attribute) { /**
if (! isset($settings[$attribute])) { * Table to store the schema version
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"'); *
} * @access private
} * @var array
*/
private $schemaTable = 'schema_version';
$dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$settings['charset']; /**
* Create a new PDO connection
*
* @access public
* @param array $settings
*/
public function createConnection(array $settings)
{
$charset = empty($settings['charset']) ? 'utf8' : $settings['charset'];
$dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$charset;
if (! empty($settings['port'])) { if (! empty($settings['port'])) {
$dsn .= ';port='.$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', 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'])) { 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() 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(); $rq->execute();
$result = $rq->fetch(PDO::FETCH_ASSOC); $result = $rq->fetchColumn();
if (isset($result['version'])) { if ($result !== false) {
return (int) $result['version']; return (int) $result;
} }
else { else {
$this->exec('INSERT INTO `'.$this->schema_table.'` VALUES(0)'); $this->pdo->exec('INSERT INTO `'.$this->schemaTable.'` VALUES(0)');
} }
return 0; return 0;
} }
/**
* Set current schema version
*
* @access public
* @param integer $version
*/
public function setSchemaVersion($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)); $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 {
$sql = sprintf(
'REPLACE INTO %s (%s, %s) VALUES %s',
$this->escape($table),
$this->escape($keyColumn),
$this->escape($valueColumn),
implode(', ', array_fill(0, count($dictionary), '(?, ?)'))
);
$values = array();
foreach ($dictionary as $key => $value) {
$values[] = $key;
$values[] = $value;
} }
public function escapeIdentifier($value) $rq = $this->pdo->prepare($sql);
{ $rq->execute($values);
return '`'.$value.'`';
}
public function operatorLikeCaseSensitive() return true;
{ }
return 'LIKE BINARY'; catch (PDOException $e) {
return false;
} }
public function operatorLikeNotCaseSensitive()
{
return 'LIKE';
}
public function getDuplicateKeyErrorCode()
{
return array(23000);
} }
} }

View File

@ -3,88 +3,170 @@
namespace PicoDb\Driver; namespace PicoDb\Driver;
use PDO; 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
public function __construct(array $settings) *
{ * @access protected
$required_atttributes = array( * @var array
*/
protected $requiredAtttributes = array(
'hostname', 'hostname',
'username', 'username',
'password', 'password',
'database', 'database',
); );
foreach ($required_atttributes as $attribute) { /**
if (! isset($settings[$attribute])) { * Table to store the schema version
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"'); *
} * @access private
} * @var array
*/
private $schemaTable = 'schema_version';
/**
* Create a new PDO connection
*
* @access public
* @param array $settings
*/
public function createConnection(array $settings)
{
$dsn = 'pgsql:host='.$settings['hostname'].';dbname='.$settings['database']; $dsn = 'pgsql:host='.$settings['hostname'].';dbname='.$settings['database'];
if (! empty($settings['port'])) { if (! empty($settings['port'])) {
$dsn .= ';port='.$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'])) { 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() 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(); $rq->execute();
$result = $rq->fetch(PDO::FETCH_ASSOC); $result = $rq->fetchColumn();
if (isset($result['version'])) { if ($result !== false) {
return (int) $result['version']; return (int) $result;
} }
else { else {
$this->exec('INSERT INTO '.$this->schema_table.' VALUES(0)'); $this->pdo->exec('INSERT INTO '.$this->schemaTable.' VALUES(0)');
} }
return 0; return 0;
} }
/**
* Set current schema version
*
* @access public
* @param integer $version
*/
public function setSchemaVersion($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)); $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);
}
} }

View File

@ -3,67 +3,166 @@
namespace PicoDb\Driver; namespace PicoDb\Driver;
use PDO; use PDO;
use LogicException; use PDOException;
class Sqlite extends PDO /**
* Sqlite Driver
*
* @author Frederic Guillot
*/
class Sqlite extends Base
{ {
public function __construct(array $settings) /**
* List of required settings options
*
* @access protected
* @var array
*/
protected $requiredAtttributes = array('filename');
/**
* Create a new PDO connection
*
* @access public
* @param array $settings
*/
public function createConnection(array $settings)
{ {
$required_atttributes = array( $this->pdo = new PDO('sqlite:'.$settings['filename']);
'filename', $this->enableForeignKeys();
);
foreach ($required_atttributes as $attribute) {
if (! isset($settings[$attribute])) {
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
}
} }
parent::__construct('sqlite:'.$settings['filename']); /**
* Enable foreign keys
$this->exec('PRAGMA foreign_keys = ON'); *
} * @access public
*/
public function getSchemaVersion() public function enableForeignKeys()
{ {
$rq = $this->prepare('PRAGMA user_version'); $this->pdo->exec('PRAGMA foreign_keys = ON');
$rq->execute();
$result = $rq->fetch(PDO::FETCH_ASSOC);
if (isset($result['user_version'])) {
return (int) $result['user_version'];
} }
return 0; /**
} * Disable foreign keys
*
public function setSchemaVersion($version) * @access public
*/
public function disableForeignKeys()
{ {
$this->exec('PRAGMA user_version='.$version); $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';
}
return '';
}
/**
* Get last inserted id
*
* @access public
* @return integer
*/
public function getLastId() 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();
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));
} }
public function getDuplicateKeyErrorCode() $this->pdo->commit();
{
return array(23000); return true;
}
catch (PDOException $e) {
$this->pdo->rollback();
return false;
}
} }
} }

View File

@ -4,21 +4,40 @@ namespace PicoDb;
use PDO; use PDO;
/**
* Hashtable (key/value)
*
* @author Frederic Guillot
* @author Mathias Kresin
*/
class Hashtable extends Table 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 * Set the key column
* *
* @access public * @access public
* @param string $column * @param string $column
* @return \PicoDb\Table * @return Table
*/ */
public function columnKey($column) public function columnKey($column)
{ {
$this->column_key = $column; $this->keyColumn = $column;
return $this; return $this;
} }
@ -27,11 +46,11 @@ class Hashtable extends Table
* *
* @access public * @access public
* @param string $column * @param string $column
* @return \PicoDb\Table * @return Table
*/ */
public function columnValue($column) public function columnValue($column)
{ {
$this->column_value = $column; $this->valueColumn = $column;
return $this; return $this;
} }
@ -39,64 +58,12 @@ class Hashtable extends Table
* Insert or update * Insert or update
* *
* @access public * @access public
* @param array $data * @param array $hashmap
* @return boolean * @return boolean
*/ */
public function put(array $data) public function put(array $hashmap)
{ {
switch ($this->db->getConnection()->getAttribute(PDO::ATTR_DRIVER_NAME)) { return $this->db->getDriver()->upsert($this->getName(), $this->keyColumn, $this->valueColumn, $hashmap);
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;
} }
/** /**
@ -111,13 +78,13 @@ class Hashtable extends Table
// setup where condition // setup where condition
if (func_num_args() > 0) { 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 // 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); $rows = $rq->fetchAll(PDO::FETCH_NUM);
foreach ($rows as $row) { foreach ($rows as $row) {
@ -137,8 +104,8 @@ class Hashtable extends Table
*/ */
public function getAll($key, $value) public function getAll($key, $value)
{ {
$this->column_key = $key; $this->keyColumn = $key;
$this->column_value = $value; $this->valueColumn = $value;
return $this->get(); return $this->get();
} }
} }

View File

@ -0,0 +1,14 @@
<?php
namespace PicoDb;
use Exception;
/**
* SQLException
*
* @author Frederic Guillot
*/
class SQLException extends Exception
{
}

View File

@ -4,18 +4,42 @@ namespace PicoDb;
use PDOException; use PDOException;
/**
* Schema migration class
*
* @author Frederic Guillot
*/
class Schema class Schema
{ {
/**
* Database instance
*
* @access protected
* @var Database
*/
protected $db = null; protected $db = null;
/**
* Constructor
*
* @access public
* @param Database $db
*/
public function __construct(Database $db) public function __construct(Database $db)
{ {
$this->db = $db; $this->db = $db;
} }
/**
* Check the schema version and run the migrations
*
* @access public
* @param integer $last_version
* @return boolean
*/
public function check($last_version = 1) public function check($last_version = 1)
{ {
$current_version = $this->db->getConnection()->getSchemaVersion(); $current_version = $this->db->getDriver()->getSchemaVersion();
if ($current_version < $last_version) { if ($current_version < $last_version) {
return $this->migrateTo($current_version, $last_version); return $this->migrateTo($current_version, $last_version);
@ -24,11 +48,20 @@ class Schema
return true; 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) public function migrateTo($current_version, $next_version)
{ {
try { try {
$this->db->startTransaction(); $this->db->startTransaction();
$this->db->getDriver()->disableForeignKeys();
for ($i = $current_version + 1; $i <= $next_version; $i++) { for ($i = $current_version + 1; $i <= $next_version; $i++) {
@ -36,15 +69,17 @@ class Schema
if (function_exists($function_name)) { if (function_exists($function_name)) {
call_user_func($function_name, $this->db->getConnection()); 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(); $this->db->closeTransaction();
} }
catch (PDOException $e) { catch (PDOException $e) {
$this->db->setLogMessage($function_name.' => '.$e->getMessage()); $this->db->setLogMessage($function_name.' => '.$e->getMessage());
$this->db->cancelTransaction(); $this->db->cancelTransaction();
$this->db->getDriver()->enableForeignKeys();
return false; return false;
} }

View File

@ -3,45 +3,167 @@
namespace PicoDb; namespace PicoDb;
use PDO; 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 class Table
{ {
/**
* Sorting direction
*
* @access public
* @var string
*/
const SORT_ASC = 'ASC'; const SORT_ASC = 'ASC';
const SORT_DESC = 'DESC'; const SORT_DESC = 'DESC';
protected $db; /**
protected $table_name = ''; * Condition instance
protected $values = array(); *
* @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 $columns = array();
private $sql_limit = ''; /**
private $sql_offset = ''; * Columns to sum during update
private $sql_order = ''; *
* @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 $joins = array();
private $condition = ''; /**
private $conditions = array(); * Use DISTINCT or not?
private $or_conditions = array(); *
private $is_or_condition = false; * @access private
* @var boolean
*/
private $distinct = false; 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 * Constructor
* *
* @access public * @access public
* @param \PicoDb\Database $db * @param Database $db
* @param string $table_name * @param string $name
*/ */
public function __construct(Database $db, $table_name) public function __construct(Database $db, $name)
{ {
$this->db = $db; $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) public function save(array $data)
{ {
if (! empty($this->conditions)) { return $this->condition->hasCondition() ? $this->update($data) : $this->insert($data);
return $this->update($data);
}
else {
return $this->insert($data);
}
} }
/** /**
@ -70,25 +187,34 @@ class Table
* @param array $data * @param array $data
* @return boolean * @return boolean
*/ */
public function update(array $data) public function update(array $data = array())
{ {
$columns = array(); $columns = array();
$values = array(); $values = array();
// Split columns and values
foreach ($data as $column => $value) { foreach ($data as $column => $value) {
$columns[] = $this->db->escapeIdentifier($column).'=?'; $columns[] = $this->db->escapeIdentifier($column).'=?';
$values[] = $value; $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; $values[] = $value;
} }
// Append condition values
foreach ($this->condition->getValues() as $value) {
$values[] = $value;
}
// Build SQL query
$sql = sprintf( $sql = sprintf(
'UPDATE %s SET %s %s', 'UPDATE %s SET %s %s',
$this->db->escapeIdentifier($this->table_name), $this->db->escapeIdentifier($this->name),
implode(', ', $columns), implode(', ', $columns),
$this->buildCondition() $this->condition->build()
); );
return $this->db->execute($sql, $values) !== false; return $this->db->execute($sql, $values) !== false;
@ -111,7 +237,7 @@ class Table
$sql = sprintf( $sql = sprintf(
'INSERT INTO %s (%s) VALUES (%s)', 'INSERT INTO %s (%s) VALUES (%s)',
$this->db->escapeIdentifier($this->table_name), $this->db->escapeIdentifier($this->name),
implode(', ', $columns), implode(', ', $columns),
implode(', ', array_fill(0, count($data), '?')) implode(', ', array_fill(0, count($data), '?'))
); );
@ -129,27 +255,14 @@ class Table
{ {
$sql = sprintf( $sql = sprintf(
'DELETE FROM %s %s', 'DELETE FROM %s %s',
$this->db->escapeIdentifier($this->table_name), $this->db->escapeIdentifier($this->name),
$this->buildCondition() $this->condition->build()
); );
$result = $this->db->execute($sql, $this->values); $result = $this->db->execute($sql, $this->condition->getValues());
return $result->rowCount() > 0; 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 * Fetch all rows
* *
@ -158,11 +271,11 @@ class Table
*/ */
public function findAll() 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); $results = $rq->fetchAll(PDO::FETCH_ASSOC);
if (is_callable($this->filter_callback) && ! empty($results)) { if (is_callable($this->callback) && ! empty($results)) {
return call_user_func($this->filter_callback, $results); return call_user_func($this->callback, $results);
} }
return $results; return $results;
@ -173,12 +286,12 @@ class Table
* *
* @access public * @access public
* @param string $column * @param string $column
* @return boolean * @return mixed
*/ */
public function findAllByColumn($column) public function findAllByColumn($column)
{ {
$this->columns = array($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); return $rq->fetchAll(PDO::FETCH_COLUMN, 0);
} }
@ -187,8 +300,7 @@ class Table
* Fetch one row * Fetch one row
* *
* @access public * @access public
* @param array $data * @return array|null
* @return boolean
*/ */
public function findOne() public function findOne()
{ {
@ -210,35 +322,40 @@ class Table
$this->limit(1); $this->limit(1);
$this->columns = array($column); $this->columns = array($column);
$rq = $this->db->execute($this->buildSelectQuery(), $this->values); return $this->db->execute($this->buildSelectQuery(), $this->condition->getValues())->fetchColumn();
return $rq->fetchColumn();
} }
/** /**
* Build a select query * Build a subquery with an alias
* *
* @access public * @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[] = '('.$sql.') AS '.$this->db->escapeIdentifier($alias);
$this->columns[$key] = $this->db->escapeIdentifier($value, $this->table_name); return $this;
} }
return sprintf( /**
'SELECT %s %s FROM %s %s %s %s %s %s %s', * Exists
$this->distinct ? 'DISTINCT' : '', *
empty($this->columns) ? '*' : implode(', ', $this->columns), * @access public
$this->db->escapeIdentifier($this->table_name), * @return integer
implode(' ', $this->joins), */
$this->buildCondition(), public function exists()
empty($this->group_by) ? '' : 'GROUP BY '.implode(', ', $this->group_by), {
$this->sql_order, $sql = sprintf(
$this->sql_limit, 'SELECT 1 FROM %s '.implode(' ', $this->joins).$this->condition->build(),
$this->sql_offset $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() public function count()
{ {
$sql = sprintf( $sql = sprintf(
'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->buildCondition().$this->sql_order.$this->sql_limit.$this->sql_offset, 'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->condition->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
$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(); $result = $rq->fetchColumn();
return $result ? (int) $result : 0; return $result ? (int) $result : 0;
@ -270,12 +387,12 @@ class Table
public function sum($column) public function sum($column)
{ {
$sql = sprintf( $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($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(); $result = $rq->fetchColumn();
return $result ? (float) $result : 0; return $result ? (float) $result : 0;
@ -290,7 +407,7 @@ class Table
* @param string $local_column Local column * @param string $local_column Local column
* @param string $local_table Local table * @param string $local_table Local table
* @param string $alias Join table alias * @param string $alias Join table alias
* @return \PicoDb\Table * @return Table
*/ */
public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '') public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '')
{ {
@ -298,7 +415,7 @@ class Table
'LEFT JOIN %s ON %s=%s', 'LEFT JOIN %s ON %s=%s',
$this->db->escapeIdentifier($table), $this->db->escapeIdentifier($table),
$this->db->escapeIdentifier($alias ?: $table).'.'.$this->db->escapeIdentifier($foreign_column), $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; return $this;
@ -313,7 +430,7 @@ class Table
* @param string $column1 * @param string $column1
* @param string $table2 * @param string $table2
* @param string $column2 * @param string $column2
* @return \PicoDb\Table * @return Table
*/ */
public function left($table1, $alias1, $column1, $table2, $column2) public function left($table1, $alias1, $column1, $table2, $column2)
{ {
@ -328,100 +445,24 @@ class Table
return $this; 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 * Order by
* *
* @access public * @access public
* @param string $column Column name * @param string $column Column name
* @param string $order Direction ASC or DESC * @param string $order Direction ASC or DESC
* @return \PicoDb\Table * @return Table
*/ */
public function orderBy($column, $order = self::SORT_ASC) public function orderBy($column, $order = self::SORT_ASC)
{ {
$order = strtoupper($order); $order = strtoupper($order);
$order = $order === self::SORT_ASC || $order === self::SORT_DESC ? $order : self::SORT_ASC; $order = $order === self::SORT_ASC || $order === self::SORT_DESC ? $order : self::SORT_ASC;
if ($this->sql_order === '') { if ($this->sqlOrder === '') {
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order; $this->sqlOrder = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order;
} }
else { else {
$this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.$order; $this->sqlOrder .= ', '.$this->db->escapeIdentifier($column).' '.$order;
} }
return $this; return $this;
@ -432,17 +473,11 @@ class Table
* *
* @access public * @access public
* @param string $column * @param string $column
* @return \PicoDb\Table * @return Table
*/ */
public function asc($column) public function asc($column)
{ {
if ($this->sql_order === '') { $this->orderBy($column, self::SORT_ASC);
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.self::SORT_ASC;
}
else {
$this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.self::SORT_ASC;
}
return $this; return $this;
} }
@ -451,17 +486,11 @@ class Table
* *
* @access public * @access public
* @param string $column * @param string $column
* @return \PicoDb\Table * @return Table
*/ */
public function desc($column) public function desc($column)
{ {
if ($this->sql_order === '') { $this->orderBy($column, self::SORT_DESC);
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.self::SORT_DESC;
}
else {
$this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.self::SORT_DESC;
}
return $this; return $this;
} }
@ -470,12 +499,12 @@ class Table
* *
* @access public * @access public
* @param integer $value * @param integer $value
* @return \PicoDb\Table * @return Table
*/ */
public function limit($value) public function limit($value)
{ {
if (! is_null($value)) { if (! is_null($value)) {
$this->sql_limit = ' LIMIT '.(int) $value; $this->sqlLimit = ' LIMIT '.(int) $value;
} }
return $this; return $this;
@ -486,12 +515,12 @@ class Table
* *
* @access public * @access public
* @param integer $value * @param integer $value
* @return \PicoDb\Table * @return Table
*/ */
public function offset($value) public function offset($value)
{ {
if (! is_null($value)) { if (! is_null($value)) {
$this->sql_offset = ' OFFSET '.(int) $value; $this->sqlOffset = ' OFFSET '.(int) $value;
} }
return $this; return $this;
@ -501,11 +530,24 @@ class Table
* Group by * Group by
* *
* @access public * @access public
* @return \PicoDb\Table * @return Table
*/ */
public function groupBy() 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; return $this;
} }
@ -513,7 +555,7 @@ class Table
* Define the columns for the select * Define the columns for the select
* *
* @access public * @access public
* @return \PicoDb\Table * @return Table
*/ */
public function columns() public function columns()
{ {
@ -521,11 +563,25 @@ class Table
return $this; 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 * Distinct
* *
* @access public * @access public
* @return \PicoDb\Table * @return Table
*/ */
public function distinct() public function distinct()
{ {
@ -534,119 +590,58 @@ class Table
return $this; 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 * Magic method for sql conditions
* *
* @access public * @access public
* @param string $name * @param string $name
* @param array $arguments * @param array $arguments
* @return \PicoDb\Table * @return Table
*/ */
public function __call($name, array $arguments) public function __call($name, array $arguments)
{ {
$column = $arguments[0]; call_user_func_array(array($this->condition, $name), $arguments);
$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];
}
}
}
return $this; return $this;
} }
} }

15
vendor/fguillot/picodb/phpunit.xml vendored Normal file
View File

@ -0,0 +1,15 @@
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
colors="true"
stopOnError="true"
stopOnFailure="true">
<testsuites>
<testsuite name="Sqlite Test Suite">
<file>tests/SqliteDriverTest.php</file>
<file>tests/SqliteDatabaseTest.php</file>
<file>tests/SqliteSchemaTest.php</file>
<file>tests/SqliteTableTest.php</file>
</testsuite>
</testsuites>
</phpunit>

View File

@ -0,0 +1,100 @@
<?php
require_once 'vendor/autoload.php';
use PicoDb\Database;
class MysqlDatabaseTest extends PHPUnit_Framework_TestCase
{
private $db;
public function setUp()
{
$this->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;
}));
}
}

View File

@ -0,0 +1,65 @@
<?php
require_once 'vendor/autoload.php';
use PicoDb\Driver\Mysql;
class MysqlDriverTest extends PHPUnit_Framework_TestCase
{
private $driver;
public function setUp()
{
$this->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'));
}
}

View File

@ -0,0 +1,34 @@
<?php
require_once 'vendor/autoload.php';
require_once __DIR__.'/SchemaFixture.php';
class MysqlSchemaTest extends PHPUnit_Framework_TestCase
{
private $db;
public function setUp()
{
$this->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]);
}
}

View File

@ -0,0 +1,313 @@
<?php
require_once 'vendor/autoload.php';
use PicoDb\Database;
use PicoDb\Table;
class MysqlTableTest extends PHPUnit_Framework_TestCase
{
private $db;
public function setUp()
{
$this->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')
);
}
}

View File

@ -0,0 +1,99 @@
<?php
require_once 'vendor/autoload.php';
use PicoDb\Database;
class PostgresDatabaseTest extends PHPUnit_Framework_TestCase
{
private $db;
public function setUp()
{
$this->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;
}));
}
}

View File

@ -0,0 +1,70 @@
<?php
require_once 'vendor/autoload.php';
use PicoDb\Driver\Postgres;
class PostgresDriverTest extends PHPUnit_Framework_TestCase
{
private $driver;
public function setUp()
{
$this->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'));
}
}

View File

@ -0,0 +1,34 @@
<?php
require_once 'vendor/autoload.php';
require_once __DIR__.'/SchemaFixture.php';
class PostgresSchemaTest extends PHPUnit_Framework_TestCase
{
private $db;
public function setUp()
{
$this->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]);
}
}

View File

@ -0,0 +1,312 @@
<?php
require_once 'vendor/autoload.php';
use PicoDb\Database;
use PicoDb\Table;
class PostgresTableTest extends PHPUnit_Framework_TestCase
{
private $db;
public function setUp()
{
$this->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')
);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Schema;
function version_1($pdo)
{
$pdo->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');
}

View File

@ -0,0 +1,119 @@
<?php
require_once 'vendor/autoload.php';
use PicoDb\Database;
class SqliteDatabaseTest extends PHPUnit_Framework_TestCase
{
private $db;
public function setUp()
{
$this->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');
}
}

View File

@ -0,0 +1,62 @@
<?php
require_once 'vendor/autoload.php';
use PicoDb\Driver\Sqlite;
class SqliteDriverTest extends PHPUnit_Framework_TestCase
{
private $driver;
public function setUp()
{
$this->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'));
}
}

View File

@ -0,0 +1,30 @@
<?php
require_once 'vendor/autoload.php';
require_once __DIR__.'/SchemaFixture.php';
class SqliteSchemaTest extends PHPUnit_Framework_TestCase
{
private $db;
public function setUp()
{
$this->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]);
}
}

View File

@ -0,0 +1,327 @@
<?php
require_once 'vendor/autoload.php';
use PicoDb\Database;
use PicoDb\Table;
class SqliteTableTest extends PHPUnit_Framework_TestCase
{
private $db;
public function setUp()
{
$this->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')
);
}
}