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';
PicoDb\Database::bootstrap('db', function() {
PicoDb\Database::setInstance('db', function() {
$db = new PicoDb\Database(array(
'driver' => 'sqlite',

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,16 +6,22 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'JsonRPC\\AccessDeniedException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/AccessDeniedException.php',
'JsonRPC\\AuthenticationFailure' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
'JsonRPC\\Client' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php',
'JsonRPC\\ConnectionFailureException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php',
'JsonRPC\\InvalidJsonFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
'JsonRPC\\InvalidJsonRpcFormat' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
'JsonRPC\\Server' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Server.php',
'JsonRPC\\ServerErrorException' => $vendorDir . '/fguillot/json-rpc/src/JsonRPC/Client.php',
'PicoDb\\Condition' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Condition.php',
'PicoDb\\Database' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Database.php',
'PicoDb\\Driver\\Base' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Base.php',
'PicoDb\\Driver\\Mysql' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Mysql.php',
'PicoDb\\Driver\\Postgres' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Postgres.php',
'PicoDb\\Driver\\Sqlite' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Driver/Sqlite.php',
'PicoDb\\Hashtable' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Hashtable.php',
'PicoDb\\SQLException' => $vendorDir . '/fguillot/picodb/lib/PicoDb/SQLException.php',
'PicoDb\\Schema' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Schema.php',
'PicoDb\\Table' => $vendorDir . '/fguillot/picodb/lib/PicoDb/Table.php',
'PicoFeed\\Client\\Client' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Client/Client.php',

View File

@ -76,84 +76,6 @@
"description": "The most easy to use validator library for PHP :)",
"homepage": "https://github.com/fguillot/simpleValidator"
},
{
"name": "fguillot/json-rpc",
"version": "v0.0.3",
"version_normalized": "0.0.3.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/JsonRPC.git",
"reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8",
"reference": "ef2f1aa1c07f0e3e8878c53b3a5fc81daedbebb8",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-05-20 15:08:40",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"JsonRPC": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot",
"homepage": "http://fredericguillot.com"
}
],
"description": "Simple Json-RPC client/server library that just works",
"homepage": "https://github.com/fguillot/JsonRPC"
},
{
"name": "fguillot/picodb",
"version": "v0.0.3",
"version_normalized": "0.0.3.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoDb.git",
"reference": "f65d11cb52de34e0fd236a34184ca1a310da244a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/picoDb/zipball/f65d11cb52de34e0fd236a34184ca1a310da244a",
"reference": "f65d11cb52de34e0fd236a34184ca1a310da244a",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-05-17 23:57:05",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"PicoDb": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot",
"homepage": "http://fredericguillot.com"
}
],
"description": "Minimalist database query builder",
"homepage": "https://github.com/fguillot/picoDb"
},
{
"name": "fguillot/picofeed",
"version": "v0.1.8",
@ -202,5 +124,82 @@
],
"description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/fguillot/picoFeed"
},
{
"name": "fguillot/json-rpc",
"version": "v1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/JsonRPC.git",
"reference": "9a117e964c4c6ad026da7ae1ca155f7686e3deaf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/9a117e964c4c6ad026da7ae1ca155f7686e3deaf",
"reference": "9a117e964c4c6ad026da7ae1ca155f7686e3deaf",
"shasum": ""
},
"require": {
"php": ">=5.3.4"
},
"time": "2015-08-07 22:31:21",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"JsonRPC": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot"
}
],
"description": "Simple Json-RPC client/server library that just works",
"homepage": "https://github.com/fguillot/JsonRPC"
},
{
"name": "fguillot/picodb",
"version": "v1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoDb.git",
"reference": "8a311523d114180e04a1e08ced6766f26d7ebbae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/picoDb/zipball/8a311523d114180e04a1e08ced6766f26d7ebbae",
"reference": "8a311523d114180e04a1e08ced6766f26d7ebbae",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-08-13 01:44:29",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"PicoDb": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot",
"homepage": "http://fredericguillot.com"
}
],
"description": "Minimalist database query builder",
"homepage": "https://github.com/fguillot/picoDb"
}
]

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.
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fguillot/JsonRPC/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/fguillot/JsonRPC/?branch=master)
[![Build Status](https://travis-ci.org/fguillot/JsonRPC.svg?branch=master)](https://travis-ci.org/fguillot/JsonRPC)
Features
--------
@ -11,24 +15,19 @@ Features
- Authentication and IP based client restrictions
- Minimalist: there is only 2 files
- Fully unit tested
- Requirements: PHP >= 5.3.4
- License: MIT
Requirements
------------
- The only dependency is the cURL extension
- PHP >= 5.3
Author
------
[Frédéric Guillot](http://fredericguillot.com)
Frédéric Guillot
Installation with Composer
--------------------------
```bash
composer require fguillot/json-rpc dev-master
composer require fguillot/json-rpc @stable
```
Examples
@ -214,7 +213,10 @@ All results are stored at the same position of the call.
- `BadFunctionCallException`: Procedure not found on the server
- `InvalidArgumentException`: Wrong procedure arguments
- `RuntimeException`: Protocol error, authentication failure or connection failure, the message describe the exact error
- `JsonRPC\AccessDeniedException`: Access denied
- `JsonRPC\ConnectionFailureException`: Connection failure
- `JsonRPC\ServerErrorException`: Internal server error
- `RuntimeException`: Protocol error
### Enable client debugging

View File

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

View File

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

View File

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

View File

@ -2,16 +2,19 @@
namespace JsonRPC;
use Exception;
use RuntimeException;
use BadFunctionCallException;
use InvalidArgumentException;
class ConnectionFailureException extends Exception {};
class ServerErrorException extends Exception {};
/**
* JsonRPC client class
*
* @package JsonRPC
* @author Frederic Guillot
* @license Unlicense http://unlicense.org/
* @author Frederic Guillot
*/
class Client
{
@ -87,47 +90,33 @@ class Client
* @var array
*/
private $headers = array(
'User-Agent: JSON-RPC PHP Client <https://github.com/fguillot/JsonRPC>',
'Content-Type: application/json',
'Accept: application/json'
'Accept: application/json',
'Connection: close',
);
/**
* SSL certificates verification
*
* @access public
* @var boolean
*/
public $ssl_verify_peer = true;
/**
* cURL handle
*
* @access private
*/
private $ch;
/**
* Constructor
*
* @access public
* @param string $url Server URL
* @param integer $timeout Server URL
* @param integer $timeout HTTP timeout
* @param array $headers Custom HTTP headers
*/
public function __construct($url, $timeout = 5, $headers = array())
public function __construct($url, $timeout = 3, $headers = array())
{
$this->url = $url;
$this->timeout = $timeout;
$this->headers = array_merge($this->headers, $headers);
$this->ch = curl_init();
}
/**
* Destructor
*
* @access public
*/
public function __destruct()
{
curl_close($this->ch);
}
/**
@ -154,11 +143,14 @@ class Client
* @access public
* @param string $username Username
* @param string $password Password
* @return Client
*/
public function authentication($username, $password)
{
$this->username = $username;
$this->password = $password;
return $this;
}
/**
@ -243,7 +235,6 @@ class Client
public function parseResponse(array $payload)
{
if ($this->isBatchResponse($payload)) {
$results = array();
foreach ($payload as $response) {
@ -256,6 +247,113 @@ class Client
return $this->getResult($payload);
}
/**
* Throw an exception according the RPC error
*
* @access public
* @param array $error
* @throws BadFunctionCallException
* @throws InvalidArgumentException
* @throws RuntimeException
*/
public function handleRpcErrors(array $error)
{
switch ($error['code']) {
case -32601:
throw new BadFunctionCallException('Procedure not found: '. $error['message']);
case -32602:
throw new InvalidArgumentException('Invalid arguments: '. $error['message']);
default:
throw new RuntimeException('Invalid request/response: '. $error['message'], $error['code']);
}
}
/**
* Throw an exception according the HTTP response
*
* @access public
* @param array $headers
* @throws AccessDeniedException
* @throws ServerErrorException
*/
public function handleHttpErrors(array $headers)
{
$exceptions = array(
'401' => 'JsonRPC\AccessDeniedException',
'403' => 'JsonRPC\AccessDeniedException',
'404' => 'JsonRPC\ConnectionFailureException',
'500' => 'JsonRPC\ServerErrorException',
);
foreach ($headers as $header) {
foreach ($exceptions as $code => $exception) {
if (strpos($header, 'HTTP/1.0 '.$code) !== false || strpos($header, 'HTTP/1.1 '.$code) !== false) {
throw new $exception('Response: '.$header);
}
}
}
}
/**
* Do the HTTP request
*
* @access private
* @param array $payload
* @return array
*/
private function doRequest(array $payload)
{
$stream = @fopen(trim($this->url), 'r', false, $this->getContext($payload));
if (! is_resource($stream)) {
throw new ConnectionFailureException('Unable to establish a connection');
}
$metadata = stream_get_meta_data($stream);
$this->handleHttpErrors($metadata['wrapper_data']);
$response = json_decode(stream_get_contents($stream), true);
if ($this->debug) {
error_log('==> Request: '.PHP_EOL.json_encode($payload, JSON_PRETTY_PRINT));
error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT));
}
return is_array($response) ? $response : array();
}
/**
* Prepare stream context
*
* @access private
* @param array $payload
* @return resource
*/
private function getContext(array $payload)
{
$headers = $this->headers;
if (! empty($this->username) && ! empty($this->password)) {
$headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password);
}
return stream_context_create(array(
'http' => array(
'method' => 'POST',
'protocol_version' => 1.1,
'timeout' => $this->timeout,
'max_redirects' => 2,
'header' => implode("\r\n", $headers),
'content' => json_encode($payload),
'ignore_errors' => true,
),
"ssl" => array(
"verify_peer" => $this->ssl_verify_peer,
"verify_peer_name" => $this->ssl_verify_peer,
)
));
}
/**
* Return true if we have a batch response
*
@ -271,11 +369,11 @@ class Client
/**
* Get a RPC call result
*
* @access public
* @access private
* @param array $payload
* @return mixed
*/
public function getResult(array $payload)
private function getResult(array $payload)
{
if (isset($payload['error']['code'])) {
$this->handleRpcErrors($payload['error']);
@ -283,68 +381,4 @@ class Client
return isset($payload['result']) ? $payload['result'] : null;
}
/**
* Throw an exception according the RPC error
*
* @access public
* @param integer $code
*/
public function handleRpcErrors($error)
{
switch ($error['code']) {
case -32601:
throw new BadFunctionCallException('Procedure not found: '. $error['message']);
case -32602:
throw new InvalidArgumentException('Invalid arguments: '. $error['message']);
default:
throw new RuntimeException('Invalid request/response: '. $error['message'], $error['code']);
}
}
/**
* Do the HTTP request
*
* @access public
* @param string $payload Data to send
*/
public function doRequest($payload)
{
curl_setopt_array($this->ch, array(
CURLOPT_URL => $this->url,
CURLOPT_HEADER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => $this->timeout,
CURLOPT_USERAGENT => 'JSON-RPC PHP Client',
CURLOPT_HTTPHEADER => $this->headers,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_SSL_VERIFYPEER => $this->ssl_verify_peer,
CURLOPT_POSTFIELDS => json_encode($payload)
));
if ($this->username && $this->password) {
curl_setopt($this->ch, CURLOPT_USERPWD, $this->username.':'.$this->password);
}
$http_body = curl_exec($this->ch);
$http_code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
if (curl_errno($this->ch)) {
throw new RuntimeException(curl_error($this->ch));
}
if ($http_code === 401 || $http_code === 403) {
throw new RuntimeException('Access denied');
}
$response = json_decode($http_body, true);
if ($this->debug) {
error_log('==> Request: '.PHP_EOL.json_encode($payload, JSON_PRETTY_PRINT));
error_log('==> Response: '.PHP_EOL.json_encode($response, JSON_PRETTY_PRINT));
}
return is_array($response) ? $response : array();
}
}

View File

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

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));
}
/**
* @expectedException JsonRPC\ServerErrorException
*/
public function testServerError()
{
$client = new Client('http://localhost/');
$client->handleHttpErrors(array('HTTP/1.0 301 Moved Permantenly', 'Connection: close', 'HTTP/1.1 500 Internal Server Error'));
}
/**
* @expectedException JsonRPC\ConnectionFailureException
*/
public function testBadUrl()
{
$client = new Client('http://something_not_found/', 1);
$client->execute('plop');
}
/**
* @expectedException JsonRPC\ConnectionFailureException
*/
public function test404()
{
$client = new Client('http://localhost/');
$client->handleHttpErrors(array('HTTP/1.1 404 Not Found'));
}
/**
* @expectedException JsonRPC\AccessDeniedException
*/
public function testAccessForbiddenError()
{
$client = new Client('http://localhost/');
$client->handleHttpErrors(array('HTTP/1.0 301 Moved Permantenly', 'Connection: close', 'HTTP/1.1 403 Forbidden'));
}
/**
* @expectedException JsonRPC\AccessDeniedException
*/
public function testAccessNotAllowedError()
{
$client = new Client('http://localhost/');
$client->handleHttpErrors(array('HTTP/1.0 301 Moved Permantenly', 'Connection: close', 'HTTP/1.0 401 Unauthorized'));
}
public function testPrepareRequest()
{
$client = new Client('http://localhost/');

View File

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

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.
**It's not an ORM**.
[![Build Status](https://travis-ci.org/fguillot/picoDb.svg?branch=master)](https://travis-ci.org/fguillot/picoDb)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fguillot/picoDb/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/fguillot/picoDb/?branch=master)
Features
--------
@ -11,15 +14,21 @@ Features
- Supported drivers: Sqlite, Mysql, Postgresql
- Requires only PDO
- Use prepared statements
- Handle schema versions (migrations)
- Handle schema migrations
- Fully unit tested on PHP 5.3, 5.4, 5.5, 5.6 and 7.0
- License: MIT
Requirements
------------
- PHP >= 5.3
- PDO
- A database: Sqlite, Mysql or Postgresql
- PDO extension
- Sqlite or Mysql or Postgresql
Author
------
Frédéric Guillot
Documentation
-------------
@ -30,53 +39,93 @@ Documentation
composer require fguillot/picodb @stable
```
### Connect to your database
### Database connection
Sqlite:
```php
use PicoDb\Database;
// Sqlite driver
$db = new Database(['driver' => 'sqlite', 'filename' => ':memory:']);
```
// Mysql driver
// Optional options: "schema_table" (the default table name is "schema_version")
$db = new Database(array(
The Sqlite driver enable foreign keys by default.
Mysql:
```php
// Optional attributes:
// "charset"
// "schema_table" (the default table name is "schema_version")
// "port"
$db = new Database([
'driver' => 'mysql',
'hostname' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'my_db_name',
'charset' => 'utf8',
));
]);
```
### Execute a SQL request
Postgres:
```php
$db->execute('CREATE TABLE toto (column1 TEXT)');
// Optional attributes:
// "schema_table" (the default table name is "schema_version")
// "port"
$db = new Database([
'driver' => 'postgres',
'hostname' => 'localhost',
'username' => 'root',
'password' => '',
'database' => 'my_db_name',
]);
```
### Insert some data
### Execute any SQL query
```php
$db->table('toto')->save(['column1' => 'test']);
$db->execute('CREATE TABLE mytable (column1 TEXT)');
```
- Returns a `PDOStatement` if successful
- Returns `false` if there is a duplicate key error
- Throws a `SQLException` for other errors
### Insertion
```php
$db->table('mytable')->save(['column1' => 'test']);
```
or
```php
$db->table('toto')->insert(['column1' => 'test']);
$db->table('mytable')->insert(['column1' => 'test']);
```
### Fetch last inserted id
```php
$db->getLastId();
```
### Transactions
```php
$db->transaction(function ($db) {
$db->table('toto')->save(['column1' => 'foo']);
$db->table('toto')->save(['column1' => 'bar']);
$db->table('mytable')->save(['column1' => 'foo']);
$db->table('mytable')->save(['column1' => 'bar']);
});
```
- Returns `true` if the callback returns null
- Returns the callback return value otherwise
- Throws an SQLException if something is wrong
or
```php
@ -91,55 +140,65 @@ $db->cancelTransaction();
### Fetch all data
```php
$records = $db->table('toto')->findAll();
$records = $db->table('mytable')->findAll();
foreach ($records as $record) {
var_dump($record['column1']);
}
```
### Update something
### Updates
```php
$db->table('toto')->eq('id', 1)->save(['column1' => 'hey']);
$db->table('mytable')->eq('id', 1)->save(['column1' => 'hey']);
```
You just need to add a condition to perform an update.
### Remove rows
or
```php
$db->table('toto')->lowerThan('column1', 10)->remove();
$db->table('mytable')->eq('id', 1)->update(['column1' => 'hey']);
```
### Remove records
```php
$db->table('mytable')->lt('column1', 10)->remove();
```
### Sorting
```php
$db->table('toto')->asc('column1')->findAll();
$db->table('mytable')->asc('column1')->findAll();
```
or
```php
$db->table('toto')->desc('column1')->findAll();
$db->table('mytable')->desc('column1')->findAll();
```
or
```php
#db->table('toto')->orderBy('column1', 'ASC')->findAll();
$db->table('mytable')->orderBy('column1', 'ASC')->findAll();
```
Multiple sorting:
```php
$db->table('mytable')->asc('column1')->desc('column2')->findAll();
```
### Limit and offset
```php
$db->table('toto')->limit(10)->offset(5)->findAll();
$db->table('mytable')->limit(10)->offset(5)->findAll();
```
### Fetch only some columns
```php
$db->table('toto')->columns('column1', 'column2')->findAll();
$db->table('mytable')->columns('column1', 'column2')->findAll();
```
### Fetch only one column
@ -147,37 +206,87 @@ $db->table('toto')->columns('column1', 'column2')->findAll();
Many rows:
```php
$db->table('toto')->findAllByColumn('column1');
$db->table('mytable')->findAllByColumn('column1');
```
One row:
```php
$db->table('toto')->findOneColumn('column1');
$db->table('mytable')->findOneColumn('column1');
```
### Equals condition
### Custom select
```php
$db->table('toto')
->equals('column1', 'hey')
->findAll();
$db->table('mytable')->select(1)->eq('id', 42)->findOne();
```
### Distinct
```php
$db->table('mytable')->distinct('columnA')->findOne();
```
### Group by
```php
$db->table('mytable')->groupBy('columnA')->findAll();
```
### Count
```php
$db->table('mytable')->count();
```
### Sum
```php
$db->table('mytable')->sum('columnB');
```
### Sum column values during update
Add the value 42 to the existing value of the column "mycolumn":
```php
$db->table('mytable')->sumColumn('mycolumn', 42)->update();
```
### Exists
Returns true if a record exists otherwise false.
```php
$db->table('mytable')->eq('column1', 12)->exists();
```
### Left joins
```php
// SELECT * FROM mytable LEFT JOIN my_other_table AS t1 ON t1.id=mytable.foreign_key
$db->table('mytable')->left('my_other_table', 't1', 'id', 'mytable', 'foreign_key')->findAll();
```
or
```php
$db->table('toto')
// SELECT * FROM mytable LEFT JOIN my_other_table ON my_other_table.id=mytable.foreign_key
$db->table('mytable')->join('my_other_table', 'id', 'foreign_key')->findAll();
```
### Equals condition
```php
$db->table('mytable')
->eq('column1', 'hey')
->findAll();
```
Yout got: 'SELECT * FROM toto WHERE column1=?'
### IN condition
```php
$db->table('toto')
$db->table('mytable')
->in('column1', ['hey', 'bla'])
->findAll();
```
@ -187,7 +296,7 @@ $db->table('toto')
Case-sensitive (only Mysql and Postgres):
```php
$db->table('toto')
$db->table('mytable')
->like('column1', '%Foo%')
->findAll();
```
@ -195,82 +304,66 @@ $db->table('toto')
Not case-sensitive:
```php
$db->table('toto')
$db->table('mytable')
->ilike('column1', '%foo%')
->findAll();
```
### Lower than
### Lower than condition
```php
$db->table('toto')
->lowerThan('column1', 2)
->findAll();
```
or
```php
$db->table('toto')
$db->table('mytable')
->lt('column1', 2)
->findAll();
```
### Lower than or equals
### Lower than or equal condition
```php
$db->table('toto')
->lowerThanOrEquals('column1', 2)
->findAll();
```
or
```php
$db->table('toto')
$db->table('mytable')
->lte('column1', 2)
->findAll();
```
### Greater than
### Greater than condition
```php
$db->table('toto')
->greaterThan('column1', 3)
->findAll();
```
or
```php
$db->table('toto')
$db->table('mytable')
->gt('column1', 3)
->findAll();
```
### Greater than or equals
### Greater than or equal condition
```php
$db->table('toto')
->greaterThanOrEquals('column1', 3)
->findAll();
```
or
```php
$db->table('toto')
$db->table('mytable')
->gte('column1', 3)
->findAll();
```
### Multiple conditions
Each condition is joined by a AND.
### IS NULL condition
```php
$db->table('toto')
->like('column2', '%toto')
$db->table('mytable')
->isNull('column1')
->findAll();
```
### IS NOT NULL condition
```php
$db->table('mytable')
->notNull('column1')
->findAll();
```
### Multiple conditions
Add conditions are joined by a `AND`.
```php
$db->table('mytable')
->like('column2', '%mytable')
->gte('column1', 3)
->findAll();
```
@ -278,9 +371,9 @@ $db->table('toto')
How to make a OR condition:
```php
$db->table('toto')
$db->table('mytable')
->beginOr()
->like('column2', '%toto')
->like('column2', '%mytable')
->gte('column1', 3)
->closeOr()
->eq('column5', 'titi')
@ -292,7 +385,7 @@ $db->table('toto')
Log generated queries:
```php
$db->log_queries = true;
$db->logQueries = true;
```
Mesure each query time:
@ -304,7 +397,7 @@ $db->stopwatch = true;
Get the number of queries executed:
```php
echo $db->nb_queries;
echo $db->nbQueries;
```
Get log messages:
@ -319,19 +412,19 @@ How to use a table as a key/value store:
```php
$db->execute(
'CREATE TABLE toto (
'CREATE TABLE mytable (
column1 TEXT NOT NULL UNIQUE,
column2 TEXT default NULL
)'
);
$db->table('toto')->insert(['column1' => 'option1', 'column2' => 'value1']);
$db->table('mytable')->insert(['column1' => 'option1', 'column2' => 'value1']);
```
Add/Replace some values:
```php
$db->hashtable('toto')
$db->hashtable('mytable')
->columnKey('column1')
->columnValue('column2')
->put(['option1' => 'new value', 'option2' => 'value2']));
@ -340,7 +433,7 @@ $db->hashtable('toto')
Get all values:
```php
$result = $db->hashtable('toto')->columnKey('column1')->columnValue('column2')->get();
$result = $db->hashtable('mytable')->columnKey('column1')->columnValue('column2')->get();
print_r($result);
Array
@ -353,18 +446,18 @@ Array
or
```php
$result = $db->hashtable('toto')->getAll('column1', 'column2');
$result = $db->hashtable('mytable')->getAll('column1', 'column2');
```
Get a specific value:
```php
$db->hashtable('toto')
$db->hashtable('mytable')
->columnKey('column1')
->columnValue('column2')
->put(['option3' => 'value3']);
$result = $db->hashtable('toto')
$result = $db->hashtable('mytable')
->columnKey('column1')
->columnValue('column2')
->get('option1', 'option3');
@ -417,10 +510,9 @@ function version_2($pdo)
#### Run schema update automatically
- The method "check()" executes all migrations until to reach the correct version number.
- If we are already on the last version nothing will happen.
- The schema version for the driver Sqlite is stored inside a variable (PRAGMA user_version)
- You can use that with a dependency injection controller.
- The method `check()` execute all migrations until the version specified
- If an error occurs, the transaction is rollbacked
- Foreign keys checks are disabled if possible during the migration
Example:
@ -447,7 +539,7 @@ else {
Setup a new instance:
```php
PicoDb\Database::bootstrap('myinstance', function() {
PicoDb\Database::setInstance('myinstance', function() {
$db = new PicoDb\Database(array(
'driver' => 'sqlite',
@ -466,5 +558,5 @@ PicoDb\Database::bootstrap('myinstance', function() {
Get this instance anywhere in your code:
```php
PicoDb\Database::get('myinstance')->table(...)
PicoDb\Database::getInstance('myinstance')->table(...)
```

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;
use Closure;
use PDO;
use PDOException;
use LogicException;
use RuntimeException;
use Picodb\Driver\Sqlite;
use Picodb\Driver\Mysql;
use Picodb\Driver\Postgres;
use PicoDb\Driver\Sqlite;
use PicoDb\Driver\Mysql;
use PicoDb\Driver\Postgres;
/**
* Database
*
* @author Frederic Guillot
*/
class Database
{
/**
* Database instances
*
* @access private
* @static
* @access private
* @var array
*/
private static $instances = array();
@ -31,12 +34,11 @@ class Database
private $logs = array();
/**
* PDO instance
* Driver instance
*
* @access private
* @var PDO
*/
private $pdo;
private $driver;
/**
* Flag to calculate query time
@ -52,7 +54,7 @@ class Database
* @access public
* @var boolean
*/
public $log_queries = false;
public $logQueries = false;
/**
* Number of SQL queries executed
@ -60,10 +62,10 @@ class Database
* @access public
* @var integer
*/
public $nb_queries = 0;
public $nbQueries = 0;
/**
* Constructor, iniatlize a PDO driver
* Initialize the driver
*
* @access public
* @param array $settings Connection settings
@ -71,31 +73,22 @@ class Database
public function __construct(array $settings)
{
if (! isset($settings['driver'])) {
throw new LogicException('You must define a database driver.');
throw new LogicException('You must define a database driver');
}
switch ($settings['driver']) {
case 'sqlite':
require_once __DIR__.'/Driver/Sqlite.php';
$this->pdo = new Sqlite($settings);
$this->driver = new Sqlite($settings);
break;
case 'mysql':
require_once __DIR__.'/Driver/Mysql.php';
$this->pdo = new Mysql($settings);
$this->driver = new Mysql($settings);
break;
case 'postgres':
require_once __DIR__.'/Driver/Postgres.php';
$this->pdo = new Postgres($settings);
$this->driver = new Postgres($settings);
break;
default:
throw new LogicException('This database driver is not supported.');
throw new LogicException('This database driver is not supported');
}
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
/**
@ -116,7 +109,7 @@ class Database
* @param string $name Instance name
* @param Closure $callback Callback
*/
public static function bootstrap($name, Closure $callback)
public static function setInstance($name, Closure $callback)
{
self::$instances[$name] = $callback;
}
@ -129,10 +122,10 @@ class Database
* @param string $name Instance name
* @return Database
*/
public static function get($name)
public static function getInstance($name)
{
if (! isset(self::$instances[$name])) {
throw new LogicException('No database instance created with that name.');
throw new LogicException('No database instance created with that name');
}
if (is_callable(self::$instances[$name])) {
@ -168,11 +161,33 @@ class Database
* Get the PDO connection
*
* @access public
* @return PDO
* @return \PDO
*/
public function getConnection()
{
return $this->pdo;
return $this->driver->getConnection();
}
/**
* Get the Driver instance
*
* @access public
* @return Sqlite|Postgres|Mysql
*/
public function getDriver()
{
return $this->driver;
}
/**
* Get the last inserted id
*
* @access public
* @return integer
*/
public function getLastId()
{
return $this->driver->getLastId();
}
/**
@ -182,7 +197,7 @@ class Database
*/
public function closeConnection()
{
$this->pdo = null;
$this->driver->closeConnection();
}
/**
@ -201,25 +216,44 @@ class Database
}
if (! empty($table)) {
return $this->pdo->escapeIdentifier($table).'.'.$this->pdo->escapeIdentifier($value);
return $this->driver->escape($table).'.'.$this->driver->escape($value);
}
return $this->pdo->escapeIdentifier($value);
return $this->driver->escape($value);
}
/**
* Escape an identifier list
*
* @access public
* @param array $identifiers List of identifiers
* @param string $table Table name
* @return string[]
*/
public function escapeIdentifierList(array $identifiers, $table = '')
{
foreach ($identifiers as $key => $value) {
$identifiers[$key] = $this->escapeIdentifier($value, $table);
}
return $identifiers;
}
/**
* Execute a prepared statement
*
* Note: returns false on duplicate keys instead of SQLException
*
* @access public
* @param string $sql SQL query
* @param array $values Values
* @return PDOStatement|false
* @return \PDOStatement|false
*/
public function execute($sql, array $values = array())
{
try {
if ($this->log_queries) {
if ($this->logQueries) {
$this->setLogMessage($sql);
}
@ -227,28 +261,19 @@ class Database
$start = microtime(true);
}
$rq = $this->pdo->prepare($sql);
$rq = $this->getConnection()->prepare($sql);
$rq->execute($values);
if ($this->stopwatch) {
$this->setLogMessage('DURATION='.(microtime(true) - $start));
}
$this->nb_queries++;
$this->nbQueries++;
return $rq;
}
catch (PDOException $e) {
$this->cancelTransaction();
if (in_array($e->getCode(), $this->pdo->getDuplicateKeyErrorCode())) {
return false;
}
$this->setLogMessage($e->getMessage());
throw new RuntimeException('SQL error'.($this->log_queries ? ': '.$e->getMessage() : ''));
return $this->handleSqlError($e);
}
}
@ -261,11 +286,37 @@ class Database
*/
public function transaction(Closure $callback)
{
$this->pdo->beginTransaction();
$result = $callback($this); // Rollback is done in the execute() method
$this->closeTransaction();
try {
return $result === null ? true : $result;
$this->startTransaction();
$result = $callback($this);
$this->closeTransaction();
return $result === null ? true : $result;
}
catch (PDOException $e) {
return $this->handleSqlError($e);
}
}
/**
* Handle PDOException
*
* @access private
* @param PDOException $e
* @return bool
* @throws SQLException
*/
private function handleSqlError(PDOException $e)
{
$this->cancelTransaction();
$this->setLogMessage($e->getMessage());
if ($this->driver->isDuplicateKeyError($e->getCode())) {
return false;
}
throw new SQLException('SQL error'.($this->logQueries ? ': '.$e->getMessage() : ''));
}
/**
@ -275,8 +326,8 @@ class Database
*/
public function startTransaction()
{
if (! $this->pdo->inTransaction()) {
$this->pdo->beginTransaction();
if (! $this->getConnection()->inTransaction()) {
$this->getConnection()->beginTransaction();
}
}
@ -287,8 +338,8 @@ class Database
*/
public function closeTransaction()
{
if ($this->pdo->inTransaction()) {
$this->pdo->commit();
if ($this->getConnection()->inTransaction()) {
$this->getConnection()->commit();
}
}
@ -299,8 +350,8 @@ class Database
*/
public function cancelTransaction()
{
if ($this->pdo->inTransaction()) {
$this->pdo->rollback();
if ($this->getConnection()->inTransaction()) {
$this->getConnection()->rollback();
}
}
@ -308,11 +359,11 @@ class Database
* Get a table instance
*
* @access public
* @return Picodb\Table
* @param string $table_name
* @return Table
*/
public function table($table_name)
{
require_once __DIR__.'/Table.php';
return new Table($this, $table_name);
}
@ -320,12 +371,11 @@ class Database
* Get a hashtable instance
*
* @access public
* @return Picodb\Hashtable
* @param string $table_name
* @return Hashtable
*/
public function hashtable($table_name)
{
require_once __DIR__.'/Table.php';
require_once __DIR__.'/Hashtable.php';
return new Hashtable($this, $table_name);
}
@ -333,11 +383,10 @@ class Database
* Get a schema instance
*
* @access public
* @return Picodb\Schema
* @return Schema
*/
public function schema()
{
require_once __DIR__.'/Schema.php';
return new Schema($this);
}
}

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;
use PDO;
use LogicException;
use PDOException;
class Mysql extends PDO
/**
* Mysql Driver
*
* @author Frederic Guillot
*/
class Mysql extends Base
{
private $schema_table = 'schema_version';
/**
* List of required settings options
*
* @access protected
* @var array
*/
protected $requiredAtttributes = array(
'hostname',
'username',
'password',
'database',
);
public function __construct(array $settings)
/**
* Table to store the schema version
*
* @access private
* @var array
*/
private $schemaTable = 'schema_version';
/**
* Create a new PDO connection
*
* @access public
* @param array $settings
*/
public function createConnection(array $settings)
{
$required_atttributes = array(
'hostname',
'username',
'password',
'database',
'charset',
);
foreach ($required_atttributes as $attribute) {
if (! isset($settings[$attribute])) {
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
}
}
$dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$settings['charset'];
$charset = empty($settings['charset']) ? 'utf8' : $settings['charset'];
$dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$charset;
if (! empty($settings['port'])) {
$dsn .= ';port='.$settings['port'];
@ -35,59 +52,159 @@ class Mysql extends PDO
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES',
);
parent::__construct($dsn, $settings['username'], $settings['password'], $options);
$this->pdo = new PDO($dsn, $settings['username'], $settings['password'], $options);
if (isset($settings['schema_table'])) {
$this->schema_table = $settings['schema_table'];
$this->schemaTable = $settings['schema_table'];
}
}
/**
* Enable foreign keys
*
* @access public
*/
public function enableForeignKeys()
{
$this->pdo->exec('SET FOREIGN_KEY_CHECKS=1');
}
/**
* Disable foreign keys
*
* @access public
*/
public function disableForeignKeys()
{
$this->pdo->exec('SET FOREIGN_KEY_CHECKS=0');
}
/**
* Return true if the error code is a duplicate key
*
* @access public
* @param integer $code
* @return boolean
*/
public function isDuplicateKeyError($code)
{
return $code == 23000;
}
/**
* Escape identifier
*
* @access public
* @param string $identifier
* @return string
*/
public function escape($identifier)
{
return '`'.$identifier.'`';
}
/**
* Get non standard operator
*
* @access public
* @param string $operator
* @return string
*/
public function getOperator($operator)
{
if ($operator === 'LIKE') {
return 'LIKE BINARY';
}
else if ($operator === 'ILIKE') {
return 'LIKE';
}
return '';
}
/**
* Get last inserted id
*
* @access public
* @return integer
*/
public function getLastId()
{
return $this->pdo->lastInsertId();
}
/**
* Get current schema version
*
* @access public
* @return integer
*/
public function getSchemaVersion()
{
$this->exec("CREATE TABLE IF NOT EXISTS `".$this->schema_table."` (`version` INT DEFAULT '0')");
$this->pdo->exec("CREATE TABLE IF NOT EXISTS `".$this->schemaTable."` (`version` INT DEFAULT '0')");
$rq = $this->prepare('SELECT `version` FROM `'.$this->schema_table.'`');
$rq = $this->pdo->prepare('SELECT `version` FROM `'.$this->schemaTable.'`');
$rq->execute();
$result = $rq->fetch(PDO::FETCH_ASSOC);
$result = $rq->fetchColumn();
if (isset($result['version'])) {
return (int) $result['version'];
if ($result !== false) {
return (int) $result;
}
else {
$this->exec('INSERT INTO `'.$this->schema_table.'` VALUES(0)');
$this->pdo->exec('INSERT INTO `'.$this->schemaTable.'` VALUES(0)');
}
return 0;
}
/**
* Set current schema version
*
* @access public
* @param integer $version
*/
public function setSchemaVersion($version)
{
$rq = $this->prepare('UPDATE `'.$this->schema_table.'` SET `version`=?');
$rq = $this->pdo->prepare('UPDATE `'.$this->schemaTable.'` SET `version`=?');
$rq->execute(array($version));
}
public function getLastId()
/**
* Upsert for a key/value variable
*
* @access public
* @param string $table
* @param string $keyColumn
* @param string $valueColumn
* @param array $dictionary
* @return bool False on failure
*/
public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
{
return $this->lastInsertId();
}
try {
public function escapeIdentifier($value)
{
return '`'.$value.'`';
}
$sql = sprintf(
'REPLACE INTO %s (%s, %s) VALUES %s',
$this->escape($table),
$this->escape($keyColumn),
$this->escape($valueColumn),
implode(', ', array_fill(0, count($dictionary), '(?, ?)'))
);
public function operatorLikeCaseSensitive()
{
return 'LIKE BINARY';
}
$values = array();
public function operatorLikeNotCaseSensitive()
{
return 'LIKE';
}
foreach ($dictionary as $key => $value) {
$values[] = $key;
$values[] = $value;
}
public function getDuplicateKeyErrorCode()
{
return array(23000);
$rq = $this->pdo->prepare($sql);
$rq->execute($values);
return true;
}
catch (PDOException $e) {
return false;
}
}
}
}

View File

@ -3,88 +3,170 @@
namespace PicoDb\Driver;
use PDO;
use LogicException;
use PDOException;
class Postgres extends PDO
/**
* Postgres Driver
*
* @author Frederic Guillot
*/
class Postgres extends Base
{
private $schema_table = 'schema_version';
/**
* List of required settings options
*
* @access protected
* @var array
*/
protected $requiredAtttributes = array(
'hostname',
'username',
'password',
'database',
);
public function __construct(array $settings)
/**
* Table to store the schema version
*
* @access private
* @var array
*/
private $schemaTable = 'schema_version';
/**
* Create a new PDO connection
*
* @access public
* @param array $settings
*/
public function createConnection(array $settings)
{
$required_atttributes = array(
'hostname',
'username',
'password',
'database',
);
foreach ($required_atttributes as $attribute) {
if (! isset($settings[$attribute])) {
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
}
}
$dsn = 'pgsql:host='.$settings['hostname'].';dbname='.$settings['database'];
if (! empty($settings['port'])) {
$dsn .= ';port='.$settings['port'];
}
parent::__construct($dsn, $settings['username'], $settings['password']);
$this->pdo = new PDO($dsn, $settings['username'], $settings['password']);
if (isset($settings['schema_table'])) {
$this->schema_table = $settings['schema_table'];
$this->schemaTable = $settings['schema_table'];
}
}
/**
* Enable foreign keys
*
* @access public
*/
public function enableForeignKeys()
{
}
/**
* Disable foreign keys
*
* @access public
*/
public function disableForeignKeys()
{
}
/**
* Return true if the error code is a duplicate key
*
* @access public
* @param integer $code
* @return boolean
*/
public function isDuplicateKeyError($code)
{
return $code == 23505 || $code == 23503;
}
/**
* Escape identifier
*
* @access public
* @param string $identifier
* @return string
*/
public function escape($identifier)
{
return '"'.$identifier.'"';
}
/**
* Get non standard operator
*
* @access public
* @param string $operator
* @return string
*/
public function getOperator($operator)
{
if ($operator === 'LIKE') {
return 'LIKE';
}
else if ($operator === 'ILIKE') {
return 'ILIKE';
}
return '';
}
/**
* Get last inserted id
*
* @access public
* @return integer
*/
public function getLastId()
{
try {
$rq = $this->pdo->prepare('SELECT LASTVAL()');
$rq->execute();
return $rq->fetchColumn();
}
catch (PDOException $e) {
return 0;
}
}
/**
* Get current schema version
*
* @access public
* @return integer
*/
public function getSchemaVersion()
{
$this->exec("CREATE TABLE IF NOT EXISTS ".$this->schema_table." (version SMALLINT DEFAULT 0)");
$this->pdo->exec("CREATE TABLE IF NOT EXISTS ".$this->schemaTable." (version INTEGER DEFAULT 0)");
$rq = $this->prepare('SELECT version FROM '.$this->schema_table.'');
$rq = $this->pdo->prepare('SELECT "version" FROM "'.$this->schemaTable.'"');
$rq->execute();
$result = $rq->fetch(PDO::FETCH_ASSOC);
$result = $rq->fetchColumn();
if (isset($result['version'])) {
return (int) $result['version'];
if ($result !== false) {
return (int) $result;
}
else {
$this->exec('INSERT INTO '.$this->schema_table.' VALUES(0)');
$this->pdo->exec('INSERT INTO '.$this->schemaTable.' VALUES(0)');
}
return 0;
}
/**
* Set current schema version
*
* @access public
* @param integer $version
*/
public function setSchemaVersion($version)
{
$rq = $this->prepare('UPDATE '.$this->schema_table.' SET version=?');
$rq = $this->pdo->prepare('UPDATE '.$this->schemaTable.' SET version=?');
$rq->execute(array($version));
}
public function getLastId()
{
$rq = $this->prepare('SELECT LASTVAL()');
$rq->execute();
return $rq->fetchColumn();
}
public function escapeIdentifier($value)
{
return '"'.$value.'"';
}
public function operatorLikeCaseSensitive()
{
return 'LIKE';
}
public function operatorLikeNotCaseSensitive()
{
return 'ILIKE';
}
public function getDuplicateKeyErrorCode()
{
return array(23505, 23503);
}
}
}

View File

@ -3,67 +3,166 @@
namespace PicoDb\Driver;
use PDO;
use LogicException;
use PDOException;
class Sqlite extends PDO
/**
* Sqlite Driver
*
* @author Frederic Guillot
*/
class Sqlite extends Base
{
public function __construct(array $settings)
{
$required_atttributes = array(
'filename',
);
/**
* List of required settings options
*
* @access protected
* @var array
*/
protected $requiredAtttributes = array('filename');
foreach ($required_atttributes as $attribute) {
if (! isset($settings[$attribute])) {
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
}
/**
* Create a new PDO connection
*
* @access public
* @param array $settings
*/
public function createConnection(array $settings)
{
$this->pdo = new PDO('sqlite:'.$settings['filename']);
$this->enableForeignKeys();
}
/**
* Enable foreign keys
*
* @access public
*/
public function enableForeignKeys()
{
$this->pdo->exec('PRAGMA foreign_keys = ON');
}
/**
* Disable foreign keys
*
* @access public
*/
public function disableForeignKeys()
{
$this->pdo->exec('PRAGMA foreign_keys = OFF');
}
/**
* Return true if the error code is a duplicate key
*
* @access public
* @param integer $code
* @return boolean
*/
public function isDuplicateKeyError($code)
{
return $code == 23000;
}
/**
* Escape identifier
*
* @access public
* @param string $identifier
* @return string
*/
public function escape($identifier)
{
return '"'.$identifier.'"';
}
/**
* Get non standard operator
*
* @access public
* @param string $operator
* @return string
*/
public function getOperator($operator)
{
if ($operator === 'LIKE' || $operator === 'ILIKE') {
return 'LIKE';
}
parent::__construct('sqlite:'.$settings['filename']);
$this->exec('PRAGMA foreign_keys = ON');
}
public function getSchemaVersion()
{
$rq = $this->prepare('PRAGMA user_version');
$rq->execute();
$result = $rq->fetch(PDO::FETCH_ASSOC);
if (isset($result['user_version'])) {
return (int) $result['user_version'];
}
return 0;
}
public function setSchemaVersion($version)
{
$this->exec('PRAGMA user_version='.$version);
return '';
}
/**
* Get last inserted id
*
* @access public
* @return integer
*/
public function getLastId()
{
return $this->lastInsertId();
return $this->pdo->lastInsertId();
}
public function escapeIdentifier($value)
/**
* Get current schema version
*
* @access public
* @return integer
*/
public function getSchemaVersion()
{
return '"'.$value.'"';
$rq = $this->pdo->prepare('PRAGMA user_version');
$rq->execute();
return (int) $rq->fetchColumn();
}
public function operatorLikeCaseSensitive()
/**
* Set current schema version
*
* @access public
* @param integer $version
*/
public function setSchemaVersion($version)
{
return 'LIKE';
$this->pdo->exec('PRAGMA user_version='.$version);
}
public function operatorLikeNotCaseSensitive()
/**
* Upsert for a key/value variable
*
* @access public
* @param string $table
* @param string $keyColumn
* @param string $valueColumn
* @param array $dictionary
* @return bool False on failure
*/
public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
{
return 'LIKE';
}
try {
$this->pdo->beginTransaction();
public function getDuplicateKeyErrorCode()
{
return array(23000);
foreach ($dictionary as $key => $value) {
$sql = sprintf(
'INSERT OR REPLACE INTO %s (%s, %s) VALUES (?, ?)',
$this->escape($table),
$this->escape($keyColumn),
$this->escape($valueColumn)
);
$rq = $this->pdo->prepare($sql);
$rq->execute(array($key, $value));
}
$this->pdo->commit();
return true;
}
catch (PDOException $e) {
$this->pdo->rollback();
return false;
}
}
}
}

View File

@ -4,21 +4,40 @@ namespace PicoDb;
use PDO;
/**
* Hashtable (key/value)
*
* @author Frederic Guillot
* @author Mathias Kresin
*/
class Hashtable extends Table
{
private $column_key = 'key';
private $column_value = 'value';
/**
* Column for the key
*
* @access private
* @var string
*/
private $keyColumn = 'key';
/**
* Column for the value
*
* @access private
* @var string
*/
private $valueColumn = 'value';
/**
* Set the key column
*
* @access public
* @param string $column
* @return \PicoDb\Table
* @param string $column
* @return Table
*/
public function columnKey($column)
{
$this->column_key = $column;
$this->keyColumn = $column;
return $this;
}
@ -26,12 +45,12 @@ class Hashtable extends Table
* Set the value column
*
* @access public
* @param string $column
* @return \PicoDb\Table
* @param string $column
* @return Table
*/
public function columnValue($column)
{
$this->column_value = $column;
$this->valueColumn = $column;
return $this;
}
@ -39,64 +58,12 @@ class Hashtable extends Table
* Insert or update
*
* @access public
* @param array $data
* @param array $hashmap
* @return boolean
*/
public function put(array $data)
public function put(array $hashmap)
{
switch ($this->db->getConnection()->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql':
$sql = sprintf(
'REPLACE INTO %s (%s) VALUES %s',
$this->db->escapeIdentifier($this->table_name),
"$this->column_key, $this->column_value",
implode(', ', array_fill(0, count($data), '(?, ?)'))
);
foreach ($data as $key => $value) {
$this->values[] = $key;
$this->values[] = $value;
}
$this->db->execute($sql, $this->values);
break;
case 'sqlite': // multi insert (see mysql) requires sqlite library > 3.7.11 (bundled with PHP 5.5.11+)
// all or nothing
$this->db->startTransaction();
foreach($data as $key => $value) {
$sql = sprintf(
'INSERT OR REPLACE INTO %s (%s) VALUES (?, ?)',
$this->db->escapeIdentifier($this->table_name),
$this->db->escapeIdentifier($this->column_key).', '.$this->db->escapeIdentifier($this->column_value)
);
$this->db->execute($sql, array($key, $value));
}
break;
default: // no upsert available, need to make a select/update/insert limbo
// all or nothing
$this->db->startTransaction();
foreach($data as $key => $value) {
// check if the key exists
$this->eq($this->column_key, $key);
if ($this->count() === 1) {
// update the record
$this->update(array($this->column_key => $key, $this->column_value => $value));
}
else {
// insert the record
$this->insert(array($this->column_key => $key, $this->column_value => $value));
}
}
}
$this->db->closeTransaction();
return true;
return $this->db->getDriver()->upsert($this->getName(), $this->keyColumn, $this->valueColumn, $hashmap);
}
/**
@ -111,13 +78,13 @@ class Hashtable extends Table
// setup where condition
if (func_num_args() > 0) {
$this->in($this->column_key, func_get_args());
$this->in($this->keyColumn, func_get_args());
}
// setup to select columns in case that there are more than two
$this->columns($this->column_key, $this->column_value);
$this->columns($this->keyColumn, $this->valueColumn);
$rq = $this->db->execute($this->buildSelectQuery(), $this->values);
$rq = $this->db->execute($this->buildSelectQuery(), $this->condition->getValues());
$rows = $rq->fetchAll(PDO::FETCH_NUM);
foreach ($rows as $row) {
@ -137,8 +104,8 @@ class Hashtable extends Table
*/
public function getAll($key, $value)
{
$this->column_key = $key;
$this->column_value = $value;
$this->keyColumn = $key;
$this->valueColumn = $value;
return $this->get();
}
}

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

View File

@ -3,45 +3,167 @@
namespace PicoDb;
use PDO;
use Closure;
/**
* Table
*
* @author Frederic Guillot
*
* @method Table addCondition($sql)
* @method Table beginOr()
* @method Table closeOr()
* @method Table eq($column, $value)
* @method Table neq($column, $value)
* @method Table in($column, array $values)
* @method Table notin($column, array $values)
* @method Table like($column, $value)
* @method Table ilike($column, $value)
* @method Table gt($column, $value)
* @method Table lt($column, $value)
* @method Table gte($column, $value)
* @method Table lte($column, $value)
* @method Table isNull($column)
* @method Table notNull($column)
*/
class Table
{
/**
* Sorting direction
*
* @access public
* @var string
*/
const SORT_ASC = 'ASC';
const SORT_DESC = 'DESC';
protected $db;
protected $table_name = '';
protected $values = array();
/**
* Condition instance
*
* @access public
* @var Condition
*/
public $condition;
/**
* Database instance
*
* @access protected
* @var Database
*/
protected $db;
/**
* Table name
*
* @access protected
* @var string
*/
protected $name = '';
/**
* Columns list for SELECT query
*
* @access private
* @var array
*/
private $columns = array();
private $sql_limit = '';
private $sql_offset = '';
private $sql_order = '';
/**
* Columns to sum during update
*
* @access private
* @var array
*/
private $sumColumns = array();
/**
* SQL limit
*
* @access private
* @var string
*/
private $sqlLimit = '';
/**
* SQL offset
*
* @access private
* @var string
*/
private $sqlOffset = '';
/**
* SQL order
*
* @access private
* @var string
*/
private $sqlOrder = '';
/**
* SQL custom SELECT value
*
* @access private
* @var string
*/
private $sqlSelect = '';
/**
* SQL joins
*
* @access private
* @var array
*/
private $joins = array();
private $condition = '';
private $conditions = array();
private $or_conditions = array();
private $is_or_condition = false;
/**
* Use DISTINCT or not?
*
* @access private
* @var boolean
*/
private $distinct = false;
private $group_by = array();
private $filter_callback = null;
/**
* Group by those columns
*
* @access private
* @var array
*/
private $groupBy = array();
/**
* Callback for result filtering
*
* @access private
* @var Closure
*/
private $callback = null;
/**
* Constructor
*
* @access public
* @param \PicoDb\Database $db
* @param string $table_name
* @param Database $db
* @param string $name
*/
public function __construct(Database $db, $table_name)
public function __construct(Database $db, $name)
{
$this->db = $db;
$this->table_name = $table_name;
$this->name = $name;
$this->condition = new Condition($db);
}
/**
* Return the table name
*
* @access public
* @return string
*/
public function getName()
{
return $this->name;
}
/**
@ -53,12 +175,7 @@ class Table
*/
public function save(array $data)
{
if (! empty($this->conditions)) {
return $this->update($data);
}
else {
return $this->insert($data);
}
return $this->condition->hasCondition() ? $this->update($data) : $this->insert($data);
}
/**
@ -70,25 +187,34 @@ class Table
* @param array $data
* @return boolean
*/
public function update(array $data)
public function update(array $data = array())
{
$columns = array();
$values = array();
// Split columns and values
foreach ($data as $column => $value) {
$columns[] = $this->db->escapeIdentifier($column).'=?';
$values[] = $value;
}
foreach ($this->values as $value) {
// Sum columns
foreach ($this->sumColumns as $column => $value) {
$columns[] = $this->db->escapeIdentifier($column).'='.$this->db->escapeIdentifier($column).' + ?';
$values[] = $value;
}
// Append condition values
foreach ($this->condition->getValues() as $value) {
$values[] = $value;
}
// Build SQL query
$sql = sprintf(
'UPDATE %s SET %s %s',
$this->db->escapeIdentifier($this->table_name),
$this->db->escapeIdentifier($this->name),
implode(', ', $columns),
$this->buildCondition()
$this->condition->build()
);
return $this->db->execute($sql, $values) !== false;
@ -111,7 +237,7 @@ class Table
$sql = sprintf(
'INSERT INTO %s (%s) VALUES (%s)',
$this->db->escapeIdentifier($this->table_name),
$this->db->escapeIdentifier($this->name),
implode(', ', $columns),
implode(', ', array_fill(0, count($data), '?'))
);
@ -129,27 +255,14 @@ class Table
{
$sql = sprintf(
'DELETE FROM %s %s',
$this->db->escapeIdentifier($this->table_name),
$this->buildCondition()
$this->db->escapeIdentifier($this->name),
$this->condition->build()
);
$result = $this->db->execute($sql, $this->values);
$result = $this->db->execute($sql, $this->condition->getValues());
return $result->rowCount() > 0;
}
/**
* Add callback to alter the resultset
*
* @access public
* @param array|callable $callback
* @return \PicoDb\Table
*/
public function filter($callback)
{
$this->filter_callback = $callback;
return $this;
}
/**
* Fetch all rows
*
@ -158,11 +271,11 @@ class Table
*/
public function findAll()
{
$rq = $this->db->execute($this->buildSelectQuery(), $this->values);
$rq = $this->db->execute($this->buildSelectQuery(), $this->condition->getValues());
$results = $rq->fetchAll(PDO::FETCH_ASSOC);
if (is_callable($this->filter_callback) && ! empty($results)) {
return call_user_func($this->filter_callback, $results);
if (is_callable($this->callback) && ! empty($results)) {
return call_user_func($this->callback, $results);
}
return $results;
@ -173,12 +286,12 @@ class Table
*
* @access public
* @param string $column
* @return boolean
* @return mixed
*/
public function findAllByColumn($column)
{
$this->columns = array($column);
$rq = $this->db->execute($this->buildSelectQuery(), $this->values);
$rq = $this->db->execute($this->buildSelectQuery(), $this->condition->getValues());
return $rq->fetchAll(PDO::FETCH_COLUMN, 0);
}
@ -187,8 +300,7 @@ class Table
* Fetch one row
*
* @access public
* @param array $data
* @return boolean
* @return array|null
*/
public function findOne()
{
@ -210,35 +322,40 @@ class Table
$this->limit(1);
$this->columns = array($column);
$rq = $this->db->execute($this->buildSelectQuery(), $this->values);
return $rq->fetchColumn();
return $this->db->execute($this->buildSelectQuery(), $this->condition->getValues())->fetchColumn();
}
/**
* Build a select query
* Build a subquery with an alias
*
* @access public
* @return string
* @param string $sql
* @param string $alias
* @return Table
*/
public function buildSelectQuery()
public function subquery($sql, $alias)
{
foreach ($this->columns as $key => $value) {
$this->columns[$key] = $this->db->escapeIdentifier($value, $this->table_name);
}
$this->columns[] = '('.$sql.') AS '.$this->db->escapeIdentifier($alias);
return $this;
}
return sprintf(
'SELECT %s %s FROM %s %s %s %s %s %s %s',
$this->distinct ? 'DISTINCT' : '',
empty($this->columns) ? '*' : implode(', ', $this->columns),
$this->db->escapeIdentifier($this->table_name),
implode(' ', $this->joins),
$this->buildCondition(),
empty($this->group_by) ? '' : 'GROUP BY '.implode(', ', $this->group_by),
$this->sql_order,
$this->sql_limit,
$this->sql_offset
/**
* Exists
*
* @access public
* @return integer
*/
public function exists()
{
$sql = sprintf(
'SELECT 1 FROM %s '.implode(' ', $this->joins).$this->condition->build(),
$this->db->escapeIdentifier($this->name)
);
$rq = $this->db->execute($sql, $this->condition->getValues());
$result = $rq->fetchColumn();
return $result ? true : false;
}
/**
@ -250,11 +367,11 @@ class Table
public function count()
{
$sql = sprintf(
'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->buildCondition().$this->sql_order.$this->sql_limit.$this->sql_offset,
$this->db->escapeIdentifier($this->table_name)
'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->condition->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
$this->db->escapeIdentifier($this->name)
);
$rq = $this->db->execute($sql, $this->values);
$rq = $this->db->execute($sql, $this->condition->getValues());
$result = $rq->fetchColumn();
return $result ? (int) $result : 0;
@ -270,12 +387,12 @@ class Table
public function sum($column)
{
$sql = sprintf(
'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->buildCondition().$this->sql_order.$this->sql_limit.$this->sql_offset,
'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->condition->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
$this->db->escapeIdentifier($column),
$this->db->escapeIdentifier($this->table_name)
$this->db->escapeIdentifier($this->name)
);
$rq = $this->db->execute($sql, $this->values);
$rq = $this->db->execute($sql, $this->condition->getValues());
$result = $rq->fetchColumn();
return $result ? (float) $result : 0;
@ -290,7 +407,7 @@ class Table
* @param string $local_column Local column
* @param string $local_table Local table
* @param string $alias Join table alias
* @return \PicoDb\Table
* @return Table
*/
public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '')
{
@ -298,7 +415,7 @@ class Table
'LEFT JOIN %s ON %s=%s',
$this->db->escapeIdentifier($table),
$this->db->escapeIdentifier($alias ?: $table).'.'.$this->db->escapeIdentifier($foreign_column),
$this->db->escapeIdentifier($local_table ?: $this->table_name).'.'.$this->db->escapeIdentifier($local_column)
$this->db->escapeIdentifier($local_table ?: $this->name).'.'.$this->db->escapeIdentifier($local_column)
);
return $this;
@ -313,7 +430,7 @@ class Table
* @param string $column1
* @param string $table2
* @param string $column2
* @return \PicoDb\Table
* @return Table
*/
public function left($table1, $alias1, $column1, $table2, $column2)
{
@ -328,100 +445,24 @@ class Table
return $this;
}
/**
* Add custom condition
*
* @access private
* @return Table
*/
private function condition($condition)
{
$this->condition = $condition;
return $this;
}
/**
* Build condition
*
* @access private
* @return string
*/
private function buildCondition()
{
if (! empty($this->condition)) {
return 'WHERE '.$this->condition;
}
return empty($this->conditions) ? '' : ' WHERE '.implode(' AND ', $this->conditions);
}
/**
* Add new condition
*
* @access public
* @param string $sql
* @return Table
*/
public function addCondition($sql)
{
if ($this->is_or_condition) {
$this->or_conditions[] = $sql;
}
else {
$this->conditions[] = $sql;
}
return $this;
}
/**
* Start OR condition
*
* @access public
* @return \PicoDb\Table
*/
public function beginOr()
{
$this->is_or_condition = true;
$this->or_conditions = array();
return $this;
}
/**
* Close OR condition
*
* @access public
* @return \PicoDb\Table
*/
public function closeOr()
{
$this->is_or_condition = false;
if (! empty($this->or_conditions)) {
$this->conditions[] = '('.implode(' OR ', $this->or_conditions).')';
}
return $this;
}
/**
* Order by
*
* @access public
* @param string $column Column name
* @param string $order Direction ASC or DESC
* @return \PicoDb\Table
* @return Table
*/
public function orderBy($column, $order = self::SORT_ASC)
{
$order = strtoupper($order);
$order = $order === self::SORT_ASC || $order === self::SORT_DESC ? $order : self::SORT_ASC;
if ($this->sql_order === '') {
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order;
if ($this->sqlOrder === '') {
$this->sqlOrder = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order;
}
else {
$this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.$order;
$this->sqlOrder .= ', '.$this->db->escapeIdentifier($column).' '.$order;
}
return $this;
@ -432,17 +473,11 @@ class Table
*
* @access public
* @param string $column
* @return \PicoDb\Table
* @return Table
*/
public function asc($column)
{
if ($this->sql_order === '') {
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.self::SORT_ASC;
}
else {
$this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.self::SORT_ASC;
}
$this->orderBy($column, self::SORT_ASC);
return $this;
}
@ -451,17 +486,11 @@ class Table
*
* @access public
* @param string $column
* @return \PicoDb\Table
* @return Table
*/
public function desc($column)
{
if ($this->sql_order === '') {
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.self::SORT_DESC;
}
else {
$this->sql_order .= ', '.$this->db->escapeIdentifier($column).' '.self::SORT_DESC;
}
$this->orderBy($column, self::SORT_DESC);
return $this;
}
@ -470,12 +499,12 @@ class Table
*
* @access public
* @param integer $value
* @return \PicoDb\Table
* @return Table
*/
public function limit($value)
{
if (! is_null($value)) {
$this->sql_limit = ' LIMIT '.(int) $value;
$this->sqlLimit = ' LIMIT '.(int) $value;
}
return $this;
@ -486,12 +515,12 @@ class Table
*
* @access public
* @param integer $value
* @return \PicoDb\Table
* @return Table
*/
public function offset($value)
{
if (! is_null($value)) {
$this->sql_offset = ' OFFSET '.(int) $value;
$this->sqlOffset = ' OFFSET '.(int) $value;
}
return $this;
@ -501,11 +530,24 @@ class Table
* Group by
*
* @access public
* @return \PicoDb\Table
* @return Table
*/
public function groupBy()
{
$this->group_by = func_get_args();
$this->groupBy = func_get_args();
return $this;
}
/**
* Custom select
*
* @access public
* @param string $select
* @return Table
*/
public function select($select)
{
$this->sqlSelect = $select;
return $this;
}
@ -513,7 +555,7 @@ class Table
* Define the columns for the select
*
* @access public
* @return \PicoDb\Table
* @return Table
*/
public function columns()
{
@ -521,11 +563,25 @@ class Table
return $this;
}
/**
* Sum column
*
* @access public
* @param string $column
* @param mixed $value
* @return Table
*/
public function sumColumn($column, $value)
{
$this->sumColumns[$column] = $value;
return $this;
}
/**
* Distinct
*
* @access public
* @return \PicoDb\Table
* @return Table
*/
public function distinct()
{
@ -534,119 +590,58 @@ class Table
return $this;
}
/**
* Add callback to alter the resultset
*
* @access public
* @param Closure|array $callback
* @return Table
*/
public function callback($callback)
{
$this->callback = $callback;
return $this;
}
/**
* Build a select query
*
* @access public
* @return string
*/
public function buildSelectQuery()
{
if (empty($this->sqlSelect)) {
$this->columns = $this->db->escapeIdentifierList($this->columns);
$this->sqlSelect = ($this->distinct ? 'DISTINCT ' : '').(empty($this->columns) ? '*' : implode(', ', $this->columns));
}
$this->groupBy = $this->db->escapeIdentifierList($this->groupBy);
return trim(sprintf(
'SELECT %s FROM %s %s %s %s %s %s %s',
$this->sqlSelect,
$this->db->escapeIdentifier($this->name),
implode(' ', $this->joins),
$this->condition->build(),
empty($this->groupBy) ? '' : 'GROUP BY '.implode(', ', $this->groupBy),
$this->sqlOrder,
$this->sqlLimit,
$this->sqlOffset
));
}
/**
* Magic method for sql conditions
*
* @access public
* @param string $name
* @param array $arguments
* @return \PicoDb\Table
* @return Table
*/
public function __call($name, array $arguments)
{
$column = $arguments[0];
$sql = '';
switch (strtolower($name)) {
case 'in':
if (isset($arguments[1]) && is_array($arguments[1]) && ! empty($arguments[1])) {
$sql = sprintf(
'%s IN (%s)',
$this->db->escapeIdentifier($column),
implode(', ', array_fill(0, count($arguments[1]), '?'))
);
}
break;
case 'notin':
if (isset($arguments[1]) && is_array($arguments[1]) && ! empty($arguments[1])) {
$sql = sprintf(
'%s NOT IN (%s)',
$this->db->escapeIdentifier($column),
implode(', ', array_fill(0, count($arguments[1]), '?'))
);
}
break;
case 'like':
$sql = sprintf(
'%s %s ?',
$this->db->escapeIdentifier($column),
$this->db->getConnection()->operatorLikeCaseSensitive()
);
break;
case 'ilike':
$sql = sprintf(
'%s %s ?',
$this->db->escapeIdentifier($column),
$this->db->getConnection()->operatorLikeNotCaseSensitive()
);
break;
case 'eq':
case 'equal':
case 'equals':
$sql = sprintf('%s = ?', $this->db->escapeIdentifier($column));
break;
case 'neq':
case 'notequal':
case 'notequals':
$sql = sprintf('%s != ?', $this->db->escapeIdentifier($column));
break;
case 'gt':
case 'greaterthan':
$sql = sprintf('%s > ?', $this->db->escapeIdentifier($column));
break;
case 'lt':
case 'lowerthan':
$sql = sprintf('%s < ?', $this->db->escapeIdentifier($column));
break;
case 'gte':
case 'greaterthanorequals':
$sql = sprintf('%s >= ?', $this->db->escapeIdentifier($column));
break;
case 'lte':
case 'lowerthanorequals':
$sql = sprintf('%s <= ?', $this->db->escapeIdentifier($column));
break;
case 'isnull':
$sql = sprintf('%s IS NULL', $this->db->escapeIdentifier($column));
break;
case 'notnull':
$sql = sprintf('%s IS NOT NULL', $this->db->escapeIdentifier($column));
break;
}
if ($sql !== '') {
$this->addCondition($sql);
if (isset($arguments[1])) {
if (is_array($arguments[1])) {
foreach ($arguments[1] as $value) {
$this->values[] = $value;
}
}
else {
$this->values[] = $arguments[1];
}
}
}
call_user_func_array(array($this->condition, $name), $arguments);
return $this;
}
}

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')
);
}
}