Using sqlite efficiently with php - php

I'm porting over a small php app to use sqlite, and this is the first time that I use sqlite with php and its been a while since i last coded something with php(it was php4 by the time).
The trouble i'm having right now is that apparently with sqlite you need to call the sqlite3 object several times since it apparently doesnt establish a permanent connection with the database, so my code right now is filled with
$db = new SQLite3('test.db');
in every single function in the code. is this efficient? is there a better and cleaner way of doing this?
the other issue that i'm having is if i use sqlite inside say include/functions.php it tries to look for test.db inside this include/ dir when it should be using the app root. how could i fix this efficiently?

You only have to establish a connection again if your $db variable goes out of scope or is otherwise destroyed. If you create the variable inside a function, it will go out of scope as soon as the function exits, forcing you to recreate the variable/connection in the next function.
As such, in some form or another, you will need to create the variable in a scope that is accessible everywhere a database connection is required. The easiest but ugliest is to create it in the global scope. A better way would be to create a static class that holds the connection and can return the connection handler on request. Another way would be to use dependency injection, basically meaning you pass the connection handle into every function or object where it is needed.
The right answer depends on your existing architecture.

Code
<?php
class SQL {
private $db;
function __construct() {
try {
/*** connect to SQLite database ***/
$db = new PDO("sqlite::memory:");
$db->exec("CREATE TABLE blog (Id INTEGER PRIMARY KEY, text TEXT)");
$this->db = $db;
/*** a little message to say we did it ***/
echo 'database created in memory';
} catch(PDOException $e) {
echo $e->getMessage();
}
}
function add($text) {
$this->db->exec("INSERT INTO blog(text) VALUES ('$text')");
}
function get() {
$res = array();
$result = $this->db->query('SELECT text FROM blog');
foreach ($result as $row) {
$res[] = $row['text'];
}
return $res;
}
}
?>
<?php
$sql = new SQL();
$sql->add('hello');
print_r($sql->get());
?>
<?php
$sql = new SQL();
$sql->add('hello');
$sql->add('world');
print_r($sql->get());
?>
Output:
php sql.inc
database created in memoryArray
(
[0] => hello
)
database created in memoryArray
(
[0] => hello
[1] => world
)
As you can see no sprinkling off $db =.
P.S: You should use PDO because it is superior to just calling new SQLite3

Related

PHP OOP Help - Work In Progress

I am right now trying to write a PHP class that executes database information. Yet I feel like I am doing something majorly wrong! It seems like what you would have to type in to get it to work is too much. Here is a example of what it looks like:
<?php
class database {
public $query_type = "";
public $database_name = "";
public $database_items_query = array();
public $database_where = "";
public function __construct($query_type, $database_name, $database_items_query, $database_where) {
$this->query_type = $query_type;
$this->query_name = $query_name;
$this->query_items_query = $query_items_query;
$this->query_where = $query_where;
}
public function database_query($query_type, $database_name, $database_items_query, $database_where) {
if ($query_type == "select") {
return $sqlquery = "SELECT ($database_items_query) FROM $database_name WHERE $database_where";
}
elseif ($query_type == "update") {
return $sqlquery = "UPDATE $databasename ";
}
}
}
$username = new database("");
?>
So for the beginning you would have to type in just this to get it to work the first function?
$username = new database("select","users","username","id");
So basically with what I have so far, what am I doing wrong? Sorry if this does not make sense :(
Ok I can see what you are trying to do, A constructor is normally used to "Create" a object. When the object is created then you have the opportunity to store values inside it so you dont have to
re-enter them, You only have to call a constructor once in a objects lifetime, and you can reuse all the methods multiple times.
soo... if you change your method declaration for database_query to something along these lines:
public function database_query($database_items_query = $this->database_items_query, $database_where = $this->query_where) {
$query_type = $this->query_type;
$database_name = $this->database_name;
Then you would be using the class variables as default values so your code would be something along the lines of
$columns = array("Name","Description");
$db = new database("SELECT","my_db",$columns,"1=1");
$db->database_query(); // returns name and description from all rows
$db->database_query(array("*") ); // returns all columns and rows
$db->database_query(array("*"),"id=7"); // returns all columns where id = 7
$other_db = new database("SELECT","my_other_db",$columns,"1=1");
$db->database_query(array("id") ); // calling database_query on first object instance
$other_db->database_query(array("*") ); // calling database_query on second object instance
When using objects/classes you have to remember that you can declare variables which belong to a given instance of the object so you can re
Dunno about you but when I was your age I was writing a fair bit of C, and to get introduced to OO I was described classes as being structs with function pointers inside.
If trying to learn OO I would reccomend taking a look at java since there is a lot of first principals OO stuff out there, then once comfortable with that style of code
taking what you have learned across to the language which you know a bit better (in your case PHP.)

Design pattern for PHP classes sharing data from SQL queries

I'm a beginner to OOP. The following is the jist of my code, which I am trying to find a proper design pattern for:
class data {
public $location = array();
public $restaurant = array();
}
$data = new data;
$query = mysqli_query($mysqli, "SELECT * FROM restaurants");
//actually a big long query, simplified for illustrative purposes here
$i = 0;
while ($row = mysqli_fetch_array($query)) {
$data->location[] = $i.')'.$row['location']."<br>";
$data->restaurant[] = $row['restaurant']."<br>";
$i++;
}
I'd like to access the data class from another PHP page. (To print out information in HTML, hence the tags). I prefer not to run the query twice. I understand that classes are created and destroyed in a single PHP page load. I'd appreciated design pattern guidance for managing HTTP application state and minimizing computer resources in such a situation.
If you store the data object in a $_SESSION variable, you will have access to it from other pages and upon refresh. As mentioned in other post and comments, you want to separate out the HTML from data processing.
class data {
public $location = array();
public $restaurant = array();
}
// start your session
session_start();
$data = new data;
$query = mysqli_query($mysqli, "SELECT * FROM restaurants");
//actually a big long query, simplified for illustrative purposes here
$i = 0;
while ($row = mysqli_fetch_array($query)) {
$data->location[] = $i.')'.$row['location'];
$data->restaurant[] = $row['restaurant'];
$i++;
}
// HTML (separate from data processing)
foreach ($data->location as $location) {
echo $location . '<br />';
}
// save your session
$_SESSION['data'] = $data;
When you wish to reference the data object from another page
// start your session
session_start();
// get data object
$data = $_SESSION['data'];
// do something with data
foreach($data->location as $location) {
echo $location . '<br />';
}
SELECT data in database is rather inexpensive, in general speaking. You didn't need to worry about running the query twice. MySQL will do the caching part.
From your codes, you mixed up database data with HTML. I suggest to separate it.
// fetch data part
while ($row = mysqli_fetch_array($query)) {
$data->location[] = $row['location'];
$data->restaurant[] = $row['restaurant'];
}
// print HTML part
$i = 0;
foreach($data->location as $loc) {
echo $i . ')' . $loc . '<br />';
}
First you say this:
I'm a beginner to OOP.
Then you say this:
I prefer not to run the query twice. I understand that classes are
created and destroyed in a single PHP page load.
You are overthinking this. PHP is a scripting language based on a user request to that script. Meaning, it will always reload—and rerun—the code on each load of the PHP page. So there is no way around that.
And when I say you are overthinking this, PHP is basically a part of a L.A.M.P. stack (Linux, Apache, MySQL & PHP) so the burden of query speed rests on the MySQL server which will cache the request anyway.
Meaning while you are thinking of PHP efficiency, the inherent architecture of PHP insists that queries be run on each load. And with that in mind the burden of managing the queries falls on MySQL and on the efficiency of the server & the design of the data structures in the database.
So if you are worried about you code eating up resources, think about improving MySQL efficiency in some way. But each layer of a L.A.M.P. stack has its purpose. And the PHP layer’s purpose is to just reload & rerun scripts in each request.
You are probably are looking for the Repository pattern.
The general idea is to have a class that can retrieve data objects for you.
Example:
$db = new Db(); // your db instance; you can use PDO for this.
$repo = new RestaurantRepository($db); // create a repo instance
$restaurants = $repo->getRestaurants(); // retrieve and array of restaurants instances
Implentation:
class RestaurantRepository {
public function __construct($db) {
$this->db = $db;
}
public function getRestaurants() {
// do query and return an array of instances
}
}
Code is untested and may have typos but it's a starter.
Saving the query results to a $_SESSION variable in the form of an array results in not having to re-run the query on another page. Additionally, it manages state correctly as I can unset($_SESSION['name']) if the query is re-run with different parameters.
I can also save the output of class data to a session variable. It seems to me that this design pattern makes more sense than running a new query for page refreshes.

duplicate return value php

$amnt = "1.00";
$from = "USD";
$to = "GBP";
/* Set up new DB object with the from/to currency */
$DBob = new database($from, $to);
$locFrom = $DBob->readLocation($from);
$locTo = $DBob->readLocation($to);
echo $locFrom . $locTo;
The echo return for both objects return the same value, for the $to variable... rather than returning two seperate queries in SQL. The queries are the same however one uses the $from currency code and the other uses the $to currency code
sql code:
public function readLocation($toFrom)
{
//establish a connection to the mysql database
$dbcon = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME) OR DIE ('Could not connect to Mysql database: '. mysqli_connect_error());
if ($toFrom == 'from')
{
//$substring1 = substr($this->fromcurr,0,2);//
$query2 = ("SELECT location AS loc FROM currency WHERE countrycode ="."'$this->fromcurr'");
}
elseif ($toFrom == 'to');
{
//$substring1 = substr($this->tocurr,0,2);//
$query2 = ("SELECT location AS loc FROM currency WHERE countrycode ="."'$this->tocurr'");
}
$result = mysqli_query($dbcon,$query2);
$resultR = mysqli_fetch_array($result);
mysqli_close($dbcon);
$queryResult = $resultR['loc'];
return $queryResult;
}
You have messed up the code of the function so it does not work. I highlight some points how you can improve it which will help you to not only remove the errors but to prevent similar errors in the future by changing how you write your code.
In this discussion, I leave the topic of proper value encoding aside which can result into flaws and defects (SQL inection). Just saying, you should add those on your own.
The main business of the function is to build a conditional SQL query and execute it. So first of all move the query building out of the function into a function of it's own:
private function buildReadLocationSQL($countrycode)
{
$SQLPattern = "SELECT location AS loc FROM currency WHERE countrycode = '%s'";
return sprintf($SQLPattern, $countrycode);
}
Then you need to find out what $countrycode should be. You are using two string values here to describe what you need. Why not name the functions instead?
public function readLocationFrom()
{
...
}
public function readLocationTo()
{
...
}
Having two functions here it's clear what they do and you can not make any mistakes when you invoke (call) them.
The missing part is to encapsulate the database connection and query. This makes sense anyway because you only want to connect to the database once your application runs and not on every query. Connecting to a database is expensive. Also this helps to extend your class later on by making the code more modular.
So let's change the database class a bit to make it store the connection (you don't need to explicitly close it either, PHP will do this for you when the script ends) and to offer a function to query the database:
class Database
{
private $connection;
private function connect()
{
$connection = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if ($connection === FALSE)
{
throw new RuntimeException(sprintf('Could not connect to Mysql database: %s', mysqli_connect_error()));
}
$this->connection = $connection;
}
private function query($sql)
{
if (!$this->connection) $this->connect(); // lazy connect to the DB
$result = mysqli_query($this->connection, $sql);
if (!$result)
{
throw new RuntimeException(sprintf('Could not query the Mysql database: %s', mysqli_error($this->connection)));
}
return $result;
}
...
}
These two new private functions take care about the database specific connection and query jobs. As you add more and more functions to your database class over time, you can easily re-use them. Also those two are private (as the $connection member), because you want to hide the details from the rest of your application.
This now needs to be brought together with the readLocationFrom and readLocationTo functions to get it to work. As both functions do similar things, we create another private function that encapsulates both and which is similar to your original function logic.
However, this time, we take care that passed arguments contain only values that make sense (pre-condition checks). Also the function is private, so that it can not be called by other code which reduces the possibility of calling it wrong from outside of the class (it can't be called from the outside):
private function readLocationConditional($what)
{
switch($what)
{
case 'from':
$countrycode = $this->fromcurr;
break;
case 'to':
$countrycode = $this->tocurr;
break;
default:
throw new InvalidArgumentException(sprintf('Invalid value for $what ("%s").', $what));
}
$sql = $this->buildReadLocationSQL($countrycode);
$result = $this->query($sql);
$array = mysqli_fetch_array($result);
$field = 'loc';
if (!isset($array[$field]))
{
throw new UnexpectedValueException(sprintf('Row does not contain %s (contains %s)', $field, implode(',', array_keys($array))));
}
$location = $array[$field];
return $location;
}
So let's take a closer look what's done differently to the original function: The input values are validated and the conditional variable is assigned to $countrycode. This helps to keep things apart. In case some "undefined" value for $what is given, an exception is thrown. This will immediately notify you if you used this function wrongly.
Then the new buildReadLocationSQL function is used to create the SQL, the new query function is used to run the query. At the end, the data returned from the database is validated again and an exception is thrown if the needed field is missing. This will immediately notify you if something is wrong with the returned data.
This is now easy to bring together with the two new public functions which are actual wrappers for the new private function readLocationConditional. So let's put this all together:
public function readLocationFrom()
{
return $this->readLocationConditional('from');
}
public function readLocationTo()
{
return $this->readLocationConditional('to');
}
So now, you've rewritten the database class to make it more flexible and better functioning. Put it together and that's it.
The first key point here is that you check error conditions properly. I've used exceptions instead of die because I think it's more developer friendly. You can do this with die statements as well or with error values etc.. But Exceptions do not only tell you a message, but also where something happened, can be caught (you can't catch die) and can have a different type, so they carry much more helpful information if something goes wrong.
The second key point is that you simplify your code. The more simple your code flow is, the easier it is to code. Putting things that cover the same into functions of it's own is very productive. You can write them as you go on while you keep things separated. And another tip: The less parameters a function have (ideally: zero which is not always possible), the less you can do wrong and the less input values you need to verify.
Just to say: You originally did this somehow as well, but not completely. You had only one place where you checked for an error (the connection to the mysql database), but not for the query itself, not for the return value from the query and not for the function parameter. Putting everything into small functions of it's own, it's easier to see what to check for and handle the details.
Last but not least, the new usage, I made the names more speaking (nstd o abbr evrthng):
$amount = "1.00";
$currencyFrom = "USD";
$currencyTo = "GBP";
$db = new Database($currencyFrom, $currencyTo);
echo $db->readLocationFrom(), $db->readLocationTo();
Hope this helps. And don't fear to write some lines of code more as long as it helps to prevent errors (it's your code, use it as a tool for you) and with modularity in mind. In the end you will see that this is less code and less hassles. Looking for the actual part you made an error in is also a good place to start.
Discussion of errors; I've seen the following:
elseif ($toFrom == 'to');
^
If-blocks can be daunting, restructuring your code helps here. Reduce the use of if and especially elseif. Moving parts into functions of it's own helps to reduce complexity. Less complexity, less errors (we are humans).
$query2 = ("...");
^ ^
This parenthesis is superfluous, might be a sign for just copied over code from a previous function call. Take more care.
Just putting the finger on these errors doesn't show the whole picture and just fixing those won't get you a step further. So it's better to recognize the errors and to understand why they have been made and then to think about how to prevent them.
It looks like whatever this code is supposed to do (which isn't entirely clear), it's not being used correctly. Take a look at some important parts:
$from = "USD";
$to = "GBP";
later...
$locFrom = $DBob->readLocation($from);
$locTo = $DBob->readLocation($to);
and inside that function...
if($toFrom == 'from') {
//...
}
else if($toFrom == 'to'); {
//...
}
That conditional is never satisfied. The $toFrom function parameter (which is not a particularly intuitive variable name) is being set to "USD" and "GBP" with the two calls to the function. It's never equal to "from" or "to" as the conditional assumes.
So $query2 never even gets set to anything. This would lead me to believe that the behavior of this line is an error or undefined:
$result = mysqli_query($dbcon,$query2);
Better variable/function names may help lead you to the logical use of whatever this code is supposed to be doing. But as it stands right now, the function is assuming values that the calling code isn't supplying. If the function is going to assume those values, the very first thing it should do is check its input:
if (($toFrom != 'to') &&
($toFrom != 'from')) {
// Incorrect argument(s) supplied.
// Throw an error and do not proceed with the function
}

FlashBuilder 4.5 + PHP + mssql list output

I've created a web service with the following code:
class WebUser {
public $USERID;
}
class UseridService
{
public $username = "my_user";
public $password = "my_pw";
public $server = "my_remote_server";
public $databasename = "my_database";
public $tablename = "my_table";
function __construct ()
{
$this->con = mssql_connect($this->server, $this->username, $this->password) or die('Connection failed!');
mssql_select_db($this->databasename);
}
public function getUserid ()
{
$sql = "Select top 10 USERID FROM my_table";
$result = mssql_query($sql);
$rows = array();
while ($row = mssql_fetch_assoc($result))
{
$storage = new WebUser();
$storage->USERID = $row['USERID'];
$rows[] = $row;
}
mssql_close($this->con);
return $rows;
}
}
For now in flash builder 4.5, I want to output the top 10 userids into a List component in my canvas. I can assure everyone that the PHP webservice code I wrote works and returns an array of WebUser() objects with just the USERID string inside.
There is a lot of documentation online for MySQL and how they simply "drag and drop" the webservice into a list and it "magically" works. Despite trying to follow their conventions using MSSQL instead, I simply cannot get it working.
I was hoping if anyone can offer a piece of advice on what to do? Even if it's not an answer itself, does anyone know any online documentation that works specifically with Flashbuilder/PHP/MSSQL?
Go to the source! Adobe Documentation on setting this up:
http://www.adobe.com/devnet/flash-builder/articles/flashbuilder-php-part1.html
They walk you through hooking up a service in Flashbuilder, connecting your data objects, and displaying them.
I know what you're asking asks for MSSQL, but the database you use on the back-end doesn't matter. What matters is serializing your object from the server-side to the client-side (i.e. PHP to AS3) which means matching the objects on both ends or finding a way to convert them (i.e. JSON-encoded objects in a REST-based web service can be de-serialized quite smoothly using the as3core library as an example).

Efficient way to look up value based on a key in php [duplicate]

This question already has answers here:
How to find array / dictionary value using key?
(2 answers)
Closed 1 year ago.
With a list of around 100,000 key/value pairs (both string, mostly around 5-20 characters each) I am looking for a way to efficiently find the value for a given key.
This needs to be done in a php website. I am familiar with hash tables in java (which is probally what I would do if working in java) but am new to php.
I am looking for tips on how I should store this list (in a text file or in a database?) and search this list.
The list would have to be updated occasionally but I am mostly interested in look up time.
You could do it as a straight PHP array, but Sqlite is going to be your best bet for speed and convenience if it is available.
PHP array
Just store everything in a php file like this:
<?php
return array(
'key1'=>'value1',
'key2'=>'value2',
// snip
'key100000'=>'value100000',
);
Then you can access it like this:
<?php
$s = microtime(true); // gets the start time for benchmarking
$data = require('data.php');
echo $data['key2'];
var_dump(microtime(true)-$s); // dumps the execution time
Not the most efficient thing in the world, but it's going to work. It takes 0.1 seconds on my machine.
Sqlite
PHP should come with sqlite enabled, which will work great for this kind of thing.
This script will create a database for you from start to finish with similar characteristics to the dataset you describe in the question:
<?php
// this will *create* data.sqlite if it does not exist. Make sure "/data"
// is writable and *not* publicly accessible.
// the ATTR_ERRMODE bit at the end is useful as it forces PDO to throw an
// exception when you make a mistake, rather than internally storing an
// error code and waiting for you to retrieve it.
$pdo = new PDO('sqlite:'.dirname(__FILE__).'/data/data.sqlite', null, null, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
// create the table if you need to
$pdo->exec("CREATE TABLE stuff(id TEXT PRIMARY KEY, value TEXT)");
// insert the data
$stmt = $pdo->prepare('INSERT INTO stuff(id, value) VALUES(:id, :value)');
$id = null;
$value = null;
// this binds the variables by reference so you can re-use the prepared statement
$stmt->bindParam(':id', $id);
$stmt->bindParam(':value', $value);
// insert some data (in this case it's just dummy data)
for ($i=0; $i<100000; $i++) {
$id = $i;
$value = 'value'.$i;
$stmt->execute();
}
And then to use the values:
<?php
$s = microtime(true);
$pdo = new PDO('sqlite:'.dirname(__FILE__).'/data/data.sqlite', null, null, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
$stmt = $pdo->prepare("SELECT * FROM stuff WHERE id=:id");
$stmt->bindValue(':id', 5);
$stmt->execute();
$value = $stmt->fetchColumn(1);
var_dump($value);
// the number of seconds it took to do the lookup
var_dump(microtime(true)-$s);
This one is waaaay faster. 0.0009 seconds on my machine.
MySQL
You could also use MySQL for this instead of Sqlite, but if it's just one table with the characteristics you describe, it's probably going to be overkill. The above Sqlite example will work fine using MySQL if you have a MySQL server available to you. Just change the line that instantiates PDO to this:
$pdo = new PDO('mysql:host=your.host;dbname=your_db', 'user', 'password', array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
The queries in the sqlite example should all work fine with MySQL, but please note that I haven't tested this.
Let's get a bit crazy: Filesystem madness
Not that the Sqlite solution is slow (0.0009 seconds!), but this about four times faster on my machine. Also, Sqlite may not be available, setting up MySQL might be out of the question, etc.
In this case, you can also use the file system:
<?php
$s = microtime(true); // more hack benchmarking
class FileCache
{
protected $basePath;
public function __construct($basePath)
{
$this->basePath = $basePath;
}
public function add($key, $value)
{
$path = $this->getPath($key);
file_put_contents($path, $value);
}
public function get($key)
{
$path = $this->getPath($key);
return file_get_contents($path);
}
public function getPath($key)
{
$split = 3;
$key = md5($key);
if (!is_writable($this->basePath)) {
throw new Exception("Base path '{$this->basePath}' was not writable");
}
$path = array();
for ($i=0; $i<$split; $i++) {
$path[] = $key[$i];
}
$dir = $this->basePath.'/'.implode('/', $path);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
return $dir.'/'.substr($key, $split);
}
}
$fc = new FileCache('/tmp/foo');
/*
// use this crap for generating a test example. it's slow to create though.
for ($i=0;$i<100000;$i++) {
$fc->add('key'.$i, 'value'.$i);
}
//*/
echo $fc->get('key1', 'value1');
var_dump(microtime(true)-$s);
This one takes 0.0002 seconds for a lookup on my machine. This also has the benefit of being reasonably constant regardless of the cache size.
It depends on how frequent you would access your array, think it this way how many users can access it at same time.There are many advantages towards storing it in database and here you have two options MySQL and SQLite.
SQLite works more like text file with SQL support, you can save few milliseconds during queries as it located within reach of your application, the main disadvantage of it that it can add only one record at a time (same as text file).
I would recommend SQLite for arrays with static content like GEO IP data, translations etc.
MySQL is more powerful solution but require authentication and located on separate machine.
PHP arrays will do everything you need. But shouldn't that much data be stored in a database?
http://php.net/array

Categories