Losing the ".json" in the API Explorer documentation - php

First let me say that the new API Explorer in Restler is great. Very happy about its addition. Now, in typical fashion, let me complain about something that isn't working for me ...
The fact that Restler can return results in multiple formats is a very nice feature but I'm currently not using it (choosing to only use JSON as my return format). In the API Explorer I'd like all references to .json to not show up as this just complicates the look of the service architecture.
Here's a quick example:
class Users {
/**
* Preferences
*
* Preferences returns a dictionary of name-value pairs that provide input to applications that want to make user-specific decisions
*
* #url GET /{user_id}/preferences
**/
function preferences ($user_id , $which = 'all') {
return "$which preferences for {$user_id}";
}
/**
* GET Sensors
*
* Get a list of all sensors associated with a user.
*
* #url GET /{user_id}/sensor
**/
function sensor ($user_id) {
return "sensor";
}
/**
* GET Sensors by Type
*
* #param $user_id The user who's sensors you are interested in
* #param $type The type of sensor you want listed.
*
* #url GET /{user_id}/sensor/{type}
**/
function sensor_by_type ($user_id, $type) {
return "specific sensor";
}
/**
* ADD a Sensor
*
* #param $user_id The user who you'll be adding the sensor to
*
* #url POST /sensor
**/
function postSensor() {
return "post sensor";
}
}
In this example the API Explorer looks like this:
The basic problem I'd like to remove is remove all ".json" references as the calling structure without the optional .json works perfectly fine.
Also, for those that DO want the .json showing up there's a secondary problem of WHERE does this post-item modifier show up? In the example above you have .json attaching to the "users" element in the GET's and to the "sensor" element in the PUT. This has nothing to do with the HTTP operation but rather it seems to choose the element which immediately precedes the first variable which may not be intuitive to the user and actually isn't a requirement in Restler (at least its my impression that you can attache .json anywhere in the chain and get the desired effect).

We are using safer defaults that will work for everyone, but made it completely configurable.
if you prefer .json to be added at the end, add the following to index.php (gateway)
use Luracast\Restler\Resources;
Resources::$placeFormatExtensionBeforeDynamicParts = false;
If you prefer not to add .json extension, add the following to index.php
use Luracast\Restler\Resources;
Resources::$useFormatAsExtension = false;

Related

Open 1 page with different content [duplicate]

Is it possible to make sessions per Browser tabs?
As example a user opened 2 tabs in his browser:
Tab 1 and Tab 2
In Tab 1 he has a session:
$_SESSION['xxx'] = 'lorem';
And in Tab 2 the session is:
$_SESSION['xxx'] = 'ipsum';
Now on refresh i need to get the current session in the active tab. For example if the user refreshes Tab 2 i need to get the $_SESSION['xxx'] for tab 2 on load which is 'ipsum'. But $_SESSION['xxx'] shouldn't change on Tab 1.
Is there any option to save sessions per tab. If not what are other options to handle this issue?
Thanks for any help!
PHP stores session IDs in cookies and cookies are per client (browser), not tab. So there is no simple and easy way to do this. There are ways of doing this by creating your own session handlers, but they are more hacks than solutions and as such come with their own risks and complexity. For whatever reason you may need this, I am quite sure that there is a better architectural solution than session splitting.
I've been scouring the web for answers to this problem and have not yet found a satisfying solution. I finally pulled together something in JavaScript that sort of works.
//generate a random ID, doesn't really matter how
if(!sessionStorage.tab) {
var max = 99999999;
var min = 10000000;
sessionStorage.tab = Math.floor(Math.random() * (max - min + 1) + min);
}
//set tab_id cookie before leaving page
window.addEventListener('beforeunload', function() {
document.cookie = 'tab_id=' + sessionStorage.tab;
});
HTML5 sessionStorage is not shared between tabs, so we can store a unique tab ID there. Listening for the beforeunload event on the window tells us that we're leaving (and loading some other page). By setting a cookie before we leave, we include our value in the new request without any extra URL manipulation. To distinguish between tabs you just have to check $_COOKIE['tab_id'] on the server and store sessions values appropriately.
Do note that Firefox behaves strangely, in that triggering window.open() will create a window that shares sessionStorage with its parent, giving you two tabs with the same ID. Manually opening a blank tab and then navigating to the target URL will give you separate storage. Chrome works for me in all of my tests so far.
I realize this is probably not the right answer, or even a "good" answer, but it is an answer.
Here's my solution; we're using this to allow multiple app views open per client.
POST to get data:
'doStuff=getData&model=GTD&sn=6789&type=random&date=18-Dec-2018'
the controller then gets the data from the tables, build the object for the device, stores it to the session object and stores it in the variable as such. It generates a per tab guid so that the user can compare the same instrument with a different view in the UI.
$_session[$model][$sn][$type][$guid][$key];
the guid of course is also sent back in the data object so that the tab knows how to recall that data later on.
When the user wants to print the results to a file (pdf, etc) it sends a post with the relevant data in a POST.
'doStuff=saveFile&model=GTD&sn=6789&type=random&calDate=18-Dec-2018&guid=randomKey'
The controller then will pass that to the storage to retrieve.
Session class file example:
<?php
class Session {
public $fieldKey1;
public $fieldKey2;
public function GetStorage($model, $sn, $type, $guid, $key) {
return $_SESSION[$model][$sn][$type][$guid][$key];
}
}
?>
the controller file:
<?php
require_once('session.php');
global $session; //from session class file
switch($_POST['doStuff']) {
case 'saveFile':
$session->GetStorage($_POST['model'], $_POST['sn'], $_POST['type'], $_POST['guid'], $key);
break;
}
?>
This allows the user to have several views of the same data, without overwriting the data-set from each tab. If you don't need as much data granularity per tab, you can of course simplify the number of keys for your $_SESSION variable.
I've been taking a shot to make a web app with this feature.
It have not been matured and have constraints and flows, like having to transmit the session id in the url if your javascript make a post to an external php code depending on it, but it's functional and suits my needs (for now).
I am thinking of a more secure solution, so feel free to adapt it to your needs and give me your suggestions.
<?php
/**
* Split $_SESSION by browser Tab emulator.
* methods exemples are used whith :
* $session = new SessionSplit();
* as SessionSplit may reload the page, it has to be used on top of the code.
*
*/
class SessionSplit{
public $id;
private $gprefix="session_slice_";
private $prefix="";
private $witness="";
function SessionSplit($witness='witness'){
if(session_status()===PHP_SESSION_NONE){
session_start();
}
$this->witness=$witness;
if($this->get_id()){
$this->prefix=$this->gprefix.$this->id;
//set a witness to 'register' the session id
$this->set($this->witness,'true');
}else{
// force the session id in the url to not interfere with form validation
$actual_link = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$new_link = $actual_link.(strpos($actual_link,'?')===false?'?':'&').
'session_id='.$this->id;
header('Location: '.$new_link);
}
}
private function get_id(){
if(isset($_GET['session_id'])){
$this->id=$_GET['session_id'];
return true;
}else{
$this->new_id();
return false;
}
}
private function new_id(){
$id=0;
while(isset($_SESSION[$this->gprefix.$id.'.'.$this->witness])){$id++;}
$this->id=$id;
}
// ----------- publics
public function clearAll(){
foreach($_SESSION as $key=>$value){
if(strpos($key,$this->prefix.'.')===0){
unset($_SESSION[$key]);
}
}
}
/**
* $is_user=$session->has('user');
* equivalent to
* $is_user=isset($_SESSION['user']);
* #param {string} $local_id
* #return {boolean}
*/
public function has($local_id){
return isset($_SESSION[$this->prefix.'.'.$local_id]);
}
/**
*
* $session->clear('user');
* equivalent to
* unset($_SESSION['user']);
* #param {string} $local_id
*/
public function clear($local_id){
unset($_SESSION[$this->prefix.'.'.$local_id]);
}
/**
* $user=$session->get('user');
* equivalent to
* $user=$_SESSION['user'];
* #param {string} $local_id
* #return {mixed}
*/
public function get($local_id){
if (isset($_SESSION[$this->prefix.'.'.$local_id])) {
return $_SESSION[$this->prefix.'.'.$local_id];
}else return null;
}
/**
* $session->set('user',$user);
* equivalent to
* $_SESSION['user']=$user;
* #param {string} $local_id
* #param {mixed} $value
*/
public function set($local_id,$value){
$_SESSION[$this->prefix.'.'.$local_id]=$value;
}
};
?>

PHP - Session per Tab

Is it possible to make sessions per Browser tabs?
As example a user opened 2 tabs in his browser:
Tab 1 and Tab 2
In Tab 1 he has a session:
$_SESSION['xxx'] = 'lorem';
And in Tab 2 the session is:
$_SESSION['xxx'] = 'ipsum';
Now on refresh i need to get the current session in the active tab. For example if the user refreshes Tab 2 i need to get the $_SESSION['xxx'] for tab 2 on load which is 'ipsum'. But $_SESSION['xxx'] shouldn't change on Tab 1.
Is there any option to save sessions per tab. If not what are other options to handle this issue?
Thanks for any help!
PHP stores session IDs in cookies and cookies are per client (browser), not tab. So there is no simple and easy way to do this. There are ways of doing this by creating your own session handlers, but they are more hacks than solutions and as such come with their own risks and complexity. For whatever reason you may need this, I am quite sure that there is a better architectural solution than session splitting.
I've been scouring the web for answers to this problem and have not yet found a satisfying solution. I finally pulled together something in JavaScript that sort of works.
//generate a random ID, doesn't really matter how
if(!sessionStorage.tab) {
var max = 99999999;
var min = 10000000;
sessionStorage.tab = Math.floor(Math.random() * (max - min + 1) + min);
}
//set tab_id cookie before leaving page
window.addEventListener('beforeunload', function() {
document.cookie = 'tab_id=' + sessionStorage.tab;
});
HTML5 sessionStorage is not shared between tabs, so we can store a unique tab ID there. Listening for the beforeunload event on the window tells us that we're leaving (and loading some other page). By setting a cookie before we leave, we include our value in the new request without any extra URL manipulation. To distinguish between tabs you just have to check $_COOKIE['tab_id'] on the server and store sessions values appropriately.
Do note that Firefox behaves strangely, in that triggering window.open() will create a window that shares sessionStorage with its parent, giving you two tabs with the same ID. Manually opening a blank tab and then navigating to the target URL will give you separate storage. Chrome works for me in all of my tests so far.
I realize this is probably not the right answer, or even a "good" answer, but it is an answer.
Here's my solution; we're using this to allow multiple app views open per client.
POST to get data:
'doStuff=getData&model=GTD&sn=6789&type=random&date=18-Dec-2018'
the controller then gets the data from the tables, build the object for the device, stores it to the session object and stores it in the variable as such. It generates a per tab guid so that the user can compare the same instrument with a different view in the UI.
$_session[$model][$sn][$type][$guid][$key];
the guid of course is also sent back in the data object so that the tab knows how to recall that data later on.
When the user wants to print the results to a file (pdf, etc) it sends a post with the relevant data in a POST.
'doStuff=saveFile&model=GTD&sn=6789&type=random&calDate=18-Dec-2018&guid=randomKey'
The controller then will pass that to the storage to retrieve.
Session class file example:
<?php
class Session {
public $fieldKey1;
public $fieldKey2;
public function GetStorage($model, $sn, $type, $guid, $key) {
return $_SESSION[$model][$sn][$type][$guid][$key];
}
}
?>
the controller file:
<?php
require_once('session.php');
global $session; //from session class file
switch($_POST['doStuff']) {
case 'saveFile':
$session->GetStorage($_POST['model'], $_POST['sn'], $_POST['type'], $_POST['guid'], $key);
break;
}
?>
This allows the user to have several views of the same data, without overwriting the data-set from each tab. If you don't need as much data granularity per tab, you can of course simplify the number of keys for your $_SESSION variable.
I've been taking a shot to make a web app with this feature.
It have not been matured and have constraints and flows, like having to transmit the session id in the url if your javascript make a post to an external php code depending on it, but it's functional and suits my needs (for now).
I am thinking of a more secure solution, so feel free to adapt it to your needs and give me your suggestions.
<?php
/**
* Split $_SESSION by browser Tab emulator.
* methods exemples are used whith :
* $session = new SessionSplit();
* as SessionSplit may reload the page, it has to be used on top of the code.
*
*/
class SessionSplit{
public $id;
private $gprefix="session_slice_";
private $prefix="";
private $witness="";
function SessionSplit($witness='witness'){
if(session_status()===PHP_SESSION_NONE){
session_start();
}
$this->witness=$witness;
if($this->get_id()){
$this->prefix=$this->gprefix.$this->id;
//set a witness to 'register' the session id
$this->set($this->witness,'true');
}else{
// force the session id in the url to not interfere with form validation
$actual_link = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$new_link = $actual_link.(strpos($actual_link,'?')===false?'?':'&').
'session_id='.$this->id;
header('Location: '.$new_link);
}
}
private function get_id(){
if(isset($_GET['session_id'])){
$this->id=$_GET['session_id'];
return true;
}else{
$this->new_id();
return false;
}
}
private function new_id(){
$id=0;
while(isset($_SESSION[$this->gprefix.$id.'.'.$this->witness])){$id++;}
$this->id=$id;
}
// ----------- publics
public function clearAll(){
foreach($_SESSION as $key=>$value){
if(strpos($key,$this->prefix.'.')===0){
unset($_SESSION[$key]);
}
}
}
/**
* $is_user=$session->has('user');
* equivalent to
* $is_user=isset($_SESSION['user']);
* #param {string} $local_id
* #return {boolean}
*/
public function has($local_id){
return isset($_SESSION[$this->prefix.'.'.$local_id]);
}
/**
*
* $session->clear('user');
* equivalent to
* unset($_SESSION['user']);
* #param {string} $local_id
*/
public function clear($local_id){
unset($_SESSION[$this->prefix.'.'.$local_id]);
}
/**
* $user=$session->get('user');
* equivalent to
* $user=$_SESSION['user'];
* #param {string} $local_id
* #return {mixed}
*/
public function get($local_id){
if (isset($_SESSION[$this->prefix.'.'.$local_id])) {
return $_SESSION[$this->prefix.'.'.$local_id];
}else return null;
}
/**
* $session->set('user',$user);
* equivalent to
* $_SESSION['user']=$user;
* #param {string} $local_id
* #param {mixed} $value
*/
public function set($local_id,$value){
$_SESSION[$this->prefix.'.'.$local_id]=$value;
}
};
?>

API Explorer not using URL parameters

I have created a RESTful API that has an optional parameter passed in as a URL variable. It seems to work correctly when executing from the browser directly but when trying it in the API Explorer it lists the parameter but ignores it when executing it. I have no idea where to start to look in solving this. Any help would be greatly appreciated.
The class definition is as follows:
class actions {
/**
* LIST available Actions
*
* List all the actions that a user (or app) can choose from. The response list
* will include [name],[slug/id], and [description] attributes. If you want a more complete set of
* meta attributes for the actions then you can specify "meta=all" in the request url. For full spec of
* response please review LG_actions_list.json.
*
* #url GET /available
*
* #param $meta {#from url} Optional parameter to control the amount of meta-data passed back. Values are "none","normal", and "all"
**/
public function available ($meta="normal")
{
return "list actions (meta level set to $meta)";
}
}
In this case I can type "all" as the value of $meta in the API explorer but the response is still "list actions (meta level set to normal)".
UPDATE:
In order to clarify this behaviour I am adding the API Explorers output and the output I get when I call the service directly:
In comparison, when actually using the API I get the correct results. Typing this into Chrome:
http://[domain]/api/actions/available?meta=foobar
I get the desired output of:
"list actions (meta level set to foobar)"
There are few problems with what you are doing with the above code
Optional Parameters are better left to query string
When you add a manual route using #url no auto routes will be added for that method
Do the following to make it work.
Changed the method name to get to map it to root of the class
Turned off smart auto routing
Now in the explorer there will be two operations listed
actions.json
actions.json/{meta}
.
class actions {
/**
* LIST available Actions
*
* List all the actions that a user (or app) can choose from. The response list
* will include [name],[slug/id], and [description] attributes. If you want a more complete set of
* meta attributes for the actions then you can specify "meta=all" in the request url. For full spec of
* response please review LG_actions_list.json.
*
* #smart-auto-routing false
* #param $meta Optional parameter to control the amount of meta-data passed back. Values are "none","normal", and "all"
**/
public function get ($meta="normal")
{
return "list actions (meta level set to $meta)";
}
}

Drupal 6 Views 2: Setting Date Arguments

Passing uid as an argument works fine with this code:
$bouts = views_get_view_result('Results', 'page_1', array($user->uid));
The key line in views_get_view_result that sets arguments is:
$view->set_arguments($args);
But what about passing date ranges?
Also, if something is specified as a filter on a view, is there a way to prorammatically alter it?
views_get_view_result:
/**
* Investigate the result of a view.
* from Drupal.org.
*
* #param string $viewname
* The name of the view to retrieve the data from.
* #param string $display_id
* The display id. On the edit page for the view in question, you'll find
* a list of displays at the left side of the control area. "Defaults"
* will be at the top of that list. Hover your cursor over the name of the
* display you want to use. A URL will appear in the status bar of your
* browser. This is usually at the bottom of the window, in the chrome.
* Everything after #views-tab- is the display ID, e.g. page_1.
* #param array $args
* Array of arguments. (no keys, just args)
* #return
* array
* An array containing an object for each view item.
* string
* If the view is not found a message is returned.
*/
function views_get_view_result($viewname, $display_id = NULL, $args = NULL) {
$view = views_get_view($viewname);
if (is_object($view)) {
if (is_array($args)) {
$view->set_arguments($args);
}
if (is_string($display_id)) {
$view->set_display($display_id);
}
else {
$view->init_display();
}
$view->pre_execute();
$view->execute();
/* print "<pre> $viewname: $display_id";
print_r(get_class_methods($view)); */
return $view->result;
}
else {
return t('View %viewname not found.', array('%viewname' => $viewname));
}
}
As for passing data ranges and given the posted function definition, you could pass date ranges to that only if the view would accept them as arguments. I'm not 100% sure, but afaik date ranges can only be defined as filters, not as arguments, which leads to your second Question:
Programmatically altering the views filter settings is possible, but a bit messy, given the rather complicated view object/array mashup structure. In your posted function above, the first line is
$view = views_get_view($viewname);
After that, $view contains the whole view object. The filter settings are defined per display, so assuming you have a view with only a default display, you will find the filter settings under
$view->display['default']->display_options['filters']
(Note the object/array notation mix - the display is a contained object of type views_display)
The 'filters' array contains one entry per filter, with varying elements depending on the filter type. For your purpose, I would suggest to create a dummy view with just the filter you are interested in, with preconfigured/hardcoded values. Using a debugger (or var_dump/print_r) you can then take a look at the filter array after view creation. From what you find there, you should be able to deduce how to inject your custom date range.
Disclaimer: Poking around in the view like this is a bit annoying and not to effective, but it works. As of yet, I have not found a concise documentation of Views2 that would explain the innards in a straight forward way, as I find the official API documentation a bit lacking concerning the usage from code. (Of course this could well be just me being to stupid ;)
If you're using views 2, you can use the GUI to add a date argument. Then in the url, you can put :
www.yousite.com/yourview/startDate--finishDate
For the startDate/finishDate, the format is YYYY-MM-DD-HH.
GL!

Flat file databases [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
What are the best practices around creating flat file database structures in PHP?
A lot of more matured PHP flat file frameworks out there which I attempt to implement SQL-like query syntax which is over the top for my purposes in most cases. (I would just use a database at that point).
Are there any elegant tricks out there to get good performance and features with a small code overhead?
Well, what is the nature of the flat databases. Are they large or small. Is it simple arrays with arrays in them? if its something simple say userprofiles built as such:
$user = array("name" => "bob",
"age" => 20,
"websites" => array("example.com","bob.example.com","bob2.example.com"),
"and_one" => "more");
and to save or update the db record for that user.
$dir = "../userdata/"; //make sure to put it bellow what the server can reach.
file_put_contents($dir.$user['name'],serialize($user));
and to load the record for the user
function &get_user($name){
return unserialize(file_get_contents("../userdata/".$name));
}
but again this implementation will vary on the application and nature of the database you need.
You might consider SQLite. It's almost as simple as flat files, but you do get a SQL engine for querying. It works well with PHP too.
In my opinion, using a "Flat File Database" in the sense you're meaning (and the answer you've accepted) isn't necessarily the best way to go about things. First of all, using serialize() and unserialize() can cause MAJOR headaches if someone gets in and edits the file (they can, in fact, put arbitrary code in your "database" to be run each time.)
Personally, I'd say - why not look to the future? There have been so many times that I've had issues because I've been creating my own "proprietary" files, and the project has exploded to a point where it needs a database, and I'm thinking "you know, I wish I'd written this for a database to start with" - because the refactoring of the code takes way too much time and effort.
From this I've learnt that future proofing my application so that when it gets bigger I don't have to go and spend days refactoring is the way to go forward. How do I do this?
SQLite. It works as a database, uses SQL, and is pretty easy to change over to MySQL (especially if you're using abstracted classes for database manipulation like I do!)
In fact, especially with the "accepted answer"'s method, it can drastically cut the memory usage of your app (you don't have to load all the "RECORDS" into PHP)
One framework I'm considering would be for a blogging platform. Since just about any possible view of data you would want would be sorted by date, I was thinking about this structure:
One directory per content node:
./content/YYYYMMDDHHMMSS/
Subdirectories of each node including
/tags
/authors
/comments
As well as simple text files in the node directory for pre- and post-rendered content and the like.
This would allow a simple PHP glob() call (and probably a reversal of the result array) to query on just about anything within the content structure:
glob("content/*/tags/funny");
Would return paths including all articles tagged "funny".
Here's the code we use for Lilina:
<?php
/**
* Handler for persistent data files
*
* #author Ryan McCue <cubegames#gmail.com>
* #package Lilina
* #version 1.0
* #license http://opensource.org/licenses/gpl-license.php GNU Public License
*/
/**
* Handler for persistent data files
*
* #package Lilina
*/
class DataHandler {
/**
* Directory to store data.
*
* #since 1.0
*
* #var string
*/
protected $directory;
/**
* Constructor, duh.
*
* #since 1.0
* #uses $directory Holds the data directory, which the constructor sets.
*
* #param string $directory
*/
public function __construct($directory = null) {
if ($directory === null)
$directory = get_data_dir();
if (substr($directory, -1) != '/')
$directory .= '/';
$this->directory = (string) $directory;
}
/**
* Prepares filename and content for saving
*
* #since 1.0
* #uses $directory
* #uses put()
*
* #param string $filename Filename to save to
* #param string $content Content to save to cache
*/
public function save($filename, $content) {
$file = $this->directory . $filename;
if(!$this->put($file, $content)) {
trigger_error(get_class($this) . " error: Couldn't write to $file", E_USER_WARNING);
return false;
}
return true;
}
/**
* Saves data to file
*
* #since 1.0
* #uses $directory
*
* #param string $file Filename to save to
* #param string $data Data to save into $file
*/
protected function put($file, $data, $mode = false) {
if(file_exists($file) && file_get_contents($file) === $data) {
touch($file);
return true;
}
if(!$fp = #fopen($file, 'wb')) {
return false;
}
fwrite($fp, $data);
fclose($fp);
$this->chmod($file, $mode);
return true;
}
/**
* Change the file permissions
*
* #since 1.0
*
* #param string $file Absolute path to file
* #param integer $mode Octal mode
*/
protected function chmod($file, $mode = false){
if(!$mode)
$mode = 0644;
return #chmod($file, $mode);
}
/**
* Returns the content of the cached file if it is still valid
*
* #since 1.0
* #uses $directory
* #uses check() Check if cache file is still valid
*
* #param string $id Unique ID for content type, used to distinguish between different caches
* #return null|string Content of the cached file if valid, otherwise null
*/
public function load($filename) {
return $this->get($this->directory . $filename);
}
/**
* Returns the content of the file
*
* #since 1.0
* #uses $directory
* #uses check() Check if file is valid
*
* #param string $id Filename to load data from
* #return bool|string Content of the file if valid, otherwise null
*/
protected function get($filename) {
if(!$this->check($filename))
return null;
return file_get_contents($filename);
}
/**
* Check a file for validity
*
* Basically just a fancy alias for file_exists(), made primarily to be
* overriden.
*
* #since 1.0
* #uses $directory
*
* #param string $id Unique ID for content type, used to distinguish between different caches
* #return bool False if the cache doesn't exist or is invalid, otherwise true
*/
protected function check($filename){
return file_exists($filename);
}
/**
* Delete a file
*
* #param string $filename Unique ID
*/
public function delete($filename) {
return unlink($this->directory . $filename);
}
}
?>
It stores each entry as a separate file, which we found is efficient enough for use (no unneeded data is loaded and it's faster to save).
IMHO, you have two... er, three options if you want to avoid homebrewing something:
SQLite
If you're familiar with PDO, you can install a PDO driver that supports SQLite. Never used it, but I have used PDO a ton with MySQL. I'm going to give this a shot on a current project.
XML
Done this many times for relatively small amounts of data. XMLReader is a lightweight, read-forward, cursor-style class. SimpleXML makes it simple to read an XML document into an object that you can access just like any other class instance.
JSON (update)
Good option for smallish amounts of data, just read/write file and json_decode/json_encode. Not sure if PHP offers a structure to navigate a JSON tree without loading it all in memory though.
If you're going to use a flat file to persist data, use XML to structure the data. PHP has a built-in XML parser.
If you want a human-readable result, you can also use this type of file :
ofaurax|27|male|something|
another|24|unknown||
...
This way, you have only one file, you can debug it (and manually fix) easily, you can add fields later (at the end of each line) and the PHP code is simple (for each line, split according to |).
However, the drawbacks is that you should parse the entire file to search something (if you have millions of entry, it's not fine) and you should handle the separator in data (for example if the nick is WaR|ordz).
I have written two simple functions designed to store data in a file. You can judge for yourself if it's useful in this case.
The point is to save a php variable (if it's either an array a string or an object) to a file.
<?php
function varname(&$var) {
$oldvalue=$var;
$var='AAAAB3NzaC1yc2EAAAABIwAAAQEAqytmUAQKMOj24lAjqKJC2Gyqhbhb+DmB9eDDb8+QcFI+QOySUpYDn884rgKB6EAtoFyOZVMA6HlNj0VxMKAGE+sLTJ40rLTcieGRCeHJ/TI37e66OrjxgB+7tngKdvoG5EF9hnoGc4eTMpVUDdpAK3ykqR1FIclgk0whV7cEn/6K4697zgwwb5R2yva/zuTX+xKRqcZvyaF3Ur0Q8T+gvrAX8ktmpE18MjnA5JuGuZFZGFzQbvzCVdN52nu8i003GEFmzp0Ny57pWClKkAy3Q5P5AR2BCUwk8V0iEX3iu7J+b9pv4LRZBQkDujaAtSiAaeG2cjfzL9xIgWPf+J05IQ==';
foreach($GLOBALS as $var_name => $value) {
if ($value === 'AAAAB3NzaC1yc2EAAAABIwAAAQEAqytmUAQKMOj24lAjqKJC2Gyqhbhb+DmB9eDDb8+QcFI+QOySUpYDn884rgKB6EAtoFyOZVMA6HlNj0VxMKAGE+sLTJ40rLTcieGRCeHJ/TI37e66OrjxgB+7tngKdvoG5EF9hnoGc4eTMpVUDdpAK3ykqR1FIclgk0whV7cEn/6K4697zgwwb5R2yva/zuTX+xKRqcZvyaF3Ur0Q8T+gvrAX8ktmpE18MjnA5JuGuZFZGFzQbvzCVdN52nu8i003GEFmzp0Ny57pWClKkAy3Q5P5AR2BCUwk8V0iEX3iu7J+b9pv4LRZBQkDujaAtSiAaeG2cjfzL9xIgWPf+J05IQ==')
{
$var=$oldvalue;
return $var_name;
}
}
$var=$oldvalue;
return false;
}
function putphp(&$var, $file=false)
{
$varname=varname($var);
if(!$file)
{
$file=$varname.'.php';
}
$pathinfo=pathinfo($file);
if(file_exists($file))
{
if(is_dir($file))
{
$file=$pathinfo['dirname'].'/'.$pathinfo['basename'].'/'.$varname.'.php';
}
}
file_put_contents($file,'<?php'."\n\$".$varname.'='.var_export($var, true).";\n");
return true;
}
This one is inspiring as a practical solution:
https://github.com/mhgolkar/FlatFire
It uses multiple strategies to handling data...
[Copied from Readme File]
Free or Structured or Mixed
- STRUCTURED
Regular (table, row, column) format.
[DATABASE]
/ \
TX TableY
\_____________________________
|ROW_0 Colum_0 Colum_1 Colum_2|
|ROW_1 Colum_0 Colum_1 Colum_2|
|_____________________________|
- FREE
More creative data storing. You can store data in any structure you want for each (free) element, its similar to storing an array with a unique "Id".
[DATABASE]
/ \
EX ElementY (ID)
\________________
|Field_0 Value_0 |
|Field_1 Value_1 |
|Field_2 Value_2 |
|________________|
recall [ID]: get_free("ElementY") --> array([Field_0]=>Value_0,[Field_1]=>Value_1...
- MIXD (Mixed)
Mixed databases can store both free elements and tables.If you add a table to a free db or a free element to a structured db, flat fire will automatically convert FREE or SRCT to MIXD database.
[DATABASE]
/ \
EX TY
Just pointing out a potential problem with a flat file database with this type of system:
data|some text|more data
row 2 data|bla hbalh|more data
...etc
The problem is that the cell data contains a "|" or a "\n" then the data will be lost. Sometimes it would be easier to split by combinations of letters that most people wouldn't use.
For example:
Column splitter: #$% (Shift+345)
Row splitter: ^&* (Shift+678)
Text file: test data#$%blah blah#$%^&*new row#$%new row data 2
Then use: explode("#$%", $data); use foreach, the explode again to separate columns
Or anything along these lines. Also, I might add that flat file databases are good for systems with small amounts of data (ie. less than 20 rows), but become huge memory hogs for larger databases.

Categories