Site Sponsors:
Quick PHP Persistence  

Easy Enough?


One of the reasons why SQL databases are such a performance bottleneck in modern enterprise systems is that people store absolutely everything in them. Indeed, over the decades I encounter more and more blank stares when I respond to queries by telling friends that - even given the millions of search-able recipes, quotations, events, and graphics shared throughout my popular sites, that we have never felt the need to use SQL. -Binary searches, text indexing, and data storage routines are easy enough to write?

Requirement Triage


Indeed, it is due to the many fallacies one often discovers in the "one technology fits all" mantra that savvy technical folks refuse to do things like log HTML hits to an enterprise resource. -Without clear business justification, when it comes time to store billions of simple n-keyed records a day - into a bottleneck-sensitive database - perhaps folks are doing so because they do not realize that there is an easy alternative? (After all, to make any database work, someone had to write things like indexing and binary searches (and a whole lot more - wink - wink), at some point?)

When it comes to demonstrating customized, responsive solutions to domain-specific challenges, such was the case when I was recently showing off a performance-boosting notion designed to use PHP CRUD. When discussing how we can quickly & easily save data to an indexed file, the "I'm from Missouri" (i.e. 'show me') -urge came up with a succinct demonstration:


class user_info {

var $data = array();

function __construct() {
$this->data[0] = -1;
$this->data[1] = '';
$this->data[2] = '';
$this->data[3] = '';
$this->data[4] = false;

}

public function getPayload() {
return $this->data[4];
}

public function setPayload($str) {
$this->data[4] = $str;
}

public function getAlias() {
return $this->data[3];
}

public function setAlias($str) {
$this->data[3] = $str;
}

public function getPassword() {
return $this->data[2];
}

public function setPassword($str) {
$this->data[2] = $str;
}

public function getEmailt() {
return $this->data[1];
}

public function setEmail($email) {
$this->data[1] = $email;
}

public function getAccount() {
return $this->data[0];
}

public function setAccount($ss) {
$this->data[0] = $ss;
}

public function isNull() {
return ($this->data[0] === -1);
}

public function toString() {
$result = implode('|', $this->data);
return $result;
}

public function fromString($str) {
$this->data = explode('|', $str);
return true; // gigo
}

}


Simple String


The crux of the above example is a demonstration of how we can quickly read and write class information to a string. No XML. No JSON. Just a nice, efficient, cross platform representation of an easily extensible data format.

Exploding Arrays


By using arrays, encapsulation, and PHP's implode() and explode() feature, a classy lifestyle can get instances stored relatively quickly. -Best of all, when using an encapsulated array(), we are free to rapidly extend the number of fields / columns we keep without change requests, hours of meetings, or ever contacting your friendly neighborhood DBA ;-)

After storing data, comes the searching. While I consider my indexing technologies to be intellectual property, at the end of the day such things are easy enough to write for oneself.

Class Egocentricity


For what it is worth, working within the confines of typeless languages (such as PHP) can also bring a few conveniences to mind. Indeed, while an accomplished object-oriented designer might be interested in designing an instance out of a controller, for maintenance reasons some odd 'typeless-convenience' patterns may surface:


class user_info_crud extends user_info {
var $info;
var $db;

public function __construct() {
user_info::__construct();
$this->info = new IndexedDataInfo();
$this->info->data_min = 1024;
$df = home('front_end_data/user_info.crud');
$this->info->setFileNames($df);
$this->db = new IndexedData();
}

public function create() {
$res = $this->db->append($this->info, '');
if ($res === false)
return false;
$this->setAccount($res);
return $this->update();
}

public function read($ss) {
$str = $this->db->read($this->info, $ss);
if ($str === false)
return false;
return $this->fromString($str);
}

public function update() {
if ($this->isNull() == false) {
$str = $this->toString();
return $this->db->update($this->info, $this->getAccount(), $str);
}
return false;
}

public function delete() {
if ($this->isNull() == false)
return $this->db->update($this->info, $this->getAccount(), '');
return true;
}

public function tally() {
return $this->db->tally($this->info);
}

public function destroyDatabase() {
return $this->info->delete();
}
}


The above demonstrates how we might use PHP CRUD to maintain a list of user accounts. By using the logical, zero-based record-offset as the record identifier / account number, users have that identifier, as well as the email address (etc.) to manage their front-end web-access account. --In addition to leveraging file-level POSIX securities (usr/group, etc), the security conscious can even use PHP CRUD's base64 encoding as another line of defense.

Proper Packaging


But the real point is this: In as much as PHP is - for the moment - extremely typeless - rather than having your CRUD operations return - and manage - objects - it is often easier in PHP to use the above design pattern. While it always seems laughably obscene to derive a controller from an entity (such as we do above), doing so can often make OIOO (One-In One-Out - pronounced "ohio") operations far, far easier to deal with.

Indeed, in the final analysis, the ability to wrap a controller around an object is often what templates (also referred to as parametrized types) are all about.

Better still, if push comes to shove (as it often may), we can always retrieve or otherwise manage an array of user_info.

Enjoy,


-Rn



[ view entry ] ( 1699 views )   |  permalink  |  related link
PHP CRUD - New Project 
Decided to share this one:

https://sourceforge.net/projects/phpcrud/

Today many say that "CRUD" stands for "Create Retrieve Update and Delete." While fine for an SQL mindset, many old-timers got used to the acronym standing for "Create READ Update Delete" way back in the 1970's. Indeed, the term dates back even farther than ISAM. ...

By way of a handy completeness check-moniker however (the intended use?), the pattern works much the same for both camps. --The only difference is a record-oriented, rather than a QUERY oriented, mentality; Enjoying an expected result set of 1:1, -v- 1:*... or the difference between knowing what to expect, and perhaps having WAY too many (*) data coming back to process / secure effectively.

When designing software, be sure to know the difference. Why? Because while the classic 1:1 usage is easy and succinct, supporting the modern variant will imply many more activities. Discussions over things such as pre-queries, limiting queries, session limits, timeouts, roles, SQL injection attacks, view creation (etc.) invariably have to be considered. Activities that will consume not only design & implementation time, but data-processing resources, as well.

(Note: If you have followed a permalink to this article, then you will not have noticed a subsequent example. -Here is an interesting example.)

Enjoy,

-Rn

(*) Data is plural - Sounds odd, don't it? :)


[ view entry ] ( 4142 views )   |  permalink  |  related link
It's about time ..  
If you ever need a simple way to manage a sortable, logical, human-editable day & time representation, then I cobbled yet another one together from the com.soft9000.DateInfo and com.soft9000.TimeInfo classes this morning:


package com.soft9000.dates;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
* TimeString24: A human-editable, sortable, 24-hour-clock-based time-format reader / writer.
*
* <p>The serialized date delimiter is "/", the time delimiter is ":".</p>
* <p>The ultimate representation is logic-sortable. Human-editable. 24-hour clock.</p>
* <p>Finally, because it is often desirable to put "garbage" into processed date fields,
* the use of &amp;Garbage-In Garbage-Out&amp; (Gigo) setters is supported
* for all individual fields. Note that a -1 in both the month and
* day areas is the isNull() condition.</p>
*
* (BC times, if used, COULD be <b>negative</b>?)
*
* @author profnagy
*/
public class TimeString24 {

private int Year = 0; // Gigo setter.
private int Month = -1; // 1 - 12. Gigo setter
private int Day = -1; // 1 - 31 (Gigo setter, user-specified days-in-month elsewhere are not strictly enforced)
private int Hour = 0; // 0 - 23. Gigo setter (only)
private int Minute = 0; // 0 - 59. Gigo setter (only)
private int Second = 0; // 0 - 59. Gigo setter (only)

/**
* isNull() construction.
*
*/
public TimeString24() {
super();
}

/**
* Create a time string from the classic Date type.
*
* @param date A java.util.Date
*/
public TimeString24(Date date) {
super();
assign(GetInfo(date));
}


/**
* Check to see if null construction was used.
*
* @return True of null construction values are used. False otherwise.
*/
public boolean isNull() {
return (Day == -1 && Month == -1);
}

/**
* Invalidate this object by putting null construction values back in-place.
*/
public void asNull() {
Day = Month = -1;
}

/**
* Assign the values for a date.
* @param Year The year
* @param iMonthNum The month
* @param Day The day
* @return True if all went well.
*/
public final boolean setDate(int iYear, int iMonthNum, int iDay) {
if (iMonthNum < 1 || iMonthNum > 12) {
return false;
}
if (iDay < 1 || iDay > 31) {
return false;
}
this.Year = iYear;
this.Month = iMonthNum;
this.Day = iDay;
return true;
}


/**
* Assign the values for a time.
* @param Hour The hour (24 hour clock)
* @param Minute The standard minute
* @param Second The standard second
* @return True if all went well.
*/
public final boolean setTime(int iHour, int iMinute, int iSecond) {
if (iHour < 0 || iHour > 23) {
return false;
}
if (iMinute < 0 || iMinute > 59) {
return false;
}
if (iSecond < 0 || iSecond > 59) {
return false;
}
this.Year = iHour;
this.Month = iMinute;
this.Day = iSecond;
return true;
}

/**
* Assign the values form a TimeString24.
*
* @param ref a TimeString24
* @return True if all went well.
*/
public final boolean assign(TimeString24 ref) {
if (ref != null) {
this.Second = ref.Second;
this.Minute = ref.Minute;
this.Hour = ref.Hour;
this.Day = ref.Day;
this.Month = ref.Month;
this.Year = ref.Year;
return true;
}
return false;
}

/**
* Pad-out an integer representation to the desired, sortable, size.
*
* @param iVal The integer
* @param minSize The file size (can be greater, will not be less)
* @return A string-parsable integer, as prefixed by '0' up-to the desired
* size.
*/
public static String Pad(int iVal, int minSize) {
if (minSize <= 0) {
minSize = 1;
}
boolean bNeg = false;
if (iVal == -1) {
bNeg = true;
minSize--; // negative sign will use a space -
iVal = java.lang.Math.abs(iVal);
}

StringBuilder sb = new StringBuilder();
sb.append(iVal);
if (sb.length() < minSize) {
int count = minSize - sb.length();
sb = new StringBuilder();
for (int ss = 0; ss < count; ss++) {
sb.append('0');
}
sb.append(iVal);
}
if (bNeg) {
String str = sb.toString();
sb = new StringBuilder();
sb.append("-");
sb.append(str);
}
return sb.toString();
}

/**
* Get the default "Date Only" string.
*
* @return YYY/MM/DD format
*/
public String adDateOnly() {
return (Pad(this.Year, 4) + "/" + Pad(this.Month, 2) + "/" + Pad(this.Day, 2));
}

/**
* Create a TimeString24() for the present second.
*
* @return An up-to-the-second TimeString24()
*/
public static TimeString24 GetToday() {
return GetInfo(GetDate());
}

/**
* Get today's Date
*
* @return Today's java.util.Date.
*/
public static Date GetDate() {
Calendar cal = new GregorianCalendar();
Date date = cal.getTime();
cal = null;
return date;
}

/**
* Create a TimeString24 from the classic Date type..
*
* @param date A classic Java Date type.
* @return Always returns a TimeString24.
*/
public static TimeString24 GetInfo(Date date) {
Calendar cal = new GregorianCalendar();
cal.setTime(date);
TimeString24 info = new TimeString24();
info.Year = cal.get(Calendar.YEAR);
info.Month = cal.get(Calendar.MONTH) + 1;
info.Day = cal.get(Calendar.DAY_OF_MONTH);
info.Hour = cal.get(Calendar.HOUR_OF_DAY);
info.Minute = cal.get(Calendar.MINUTE);
info.Second = cal.get(Calendar.SECOND);
cal = null;
return info;
}

/**
* Create a TimeString24 from the default toString() representation.
*
* @param sLine The toString() representation. The format is "2012/02/05
* 09:01:10"
* @return <font color='red'>NULL on error</font>.
*/
public static TimeString24 FromString(String sLine) {
if (sLine == null) {
return null;
}
TimeString24 result = new TimeString24();
if (result.fromString(sLine) == true) {
return result;
} else {
return null;
}
}

/**
* Format "2012/02/05 09:01:10"
*
* @return The parsable (fromString()) format.
*
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(Pad(this.Year, 4));
sb.append("/");
sb.append(Pad(this.Month, 2));
sb.append("/");
sb.append(Pad(this.Day, 2));
sb.append(" ");
sb.append(Pad(this.Hour, 2));
sb.append(":");
sb.append(Pad(this.Minute, 2));
sb.append(":");
sb.append(Pad(this.Second, 2));
return sb.toString();
}

/**
* Format "2012/02/05 09:01:10"
*
* @param sLine The classic "ENGLISH STRING" to parse
* @return True on success
*/
public boolean fromString(String sLine) {
String str = sLine.replaceAll("/", "\t");
str = str.replaceAll(":", "\t");
str = str.replaceAll(" ", "\t");
String[] array = str.split("\t");

try {
if (array.length == 6) {
this.Year = Integer.parseInt(array[0]);
this.Month = Integer.parseInt(array[1]);
this.Day = Integer.parseInt(array[2]);
this.Hour = Integer.parseInt(array[3]);
this.Minute = Integer.parseInt(array[4]);
this.Second = Integer.parseInt(array[5]);
return true;
}

} catch (Exception ex) {
}
assign(new TimeString24()); // null
return false;
}

/**
* @return the Day
*/
public int getDay() {
return Day;
}

/**
* @return the Hour
*/
public int getHour() {
return Hour;
}

/**
* @return the Minute
*/
public int getMinute() {
return Minute;
}

/**
* @return the Month
*/
public int getMonth() {
return Month;
}

/**
* @return the Second
*/
public int getSecond() {
return Second;
}

/**
* @return the Year
*/
public int getYear() {
return Year;
}

/**
* @param Day the Day to set. Gigo
*/
public void setDay(int Day) {
this.Day = Day;
}

/**
* @param Hour the Hour to set. Gigo
*/
public void setHour(int Hour) {
this.Hour = Hour;
}

/**
* @param Minute the Minute to set. Gigo
*/
public void setMinute(int Minute) {
this.Minute = Minute;
}

/**
* @param Month the Month to set. Gigo
*/
public void setMonth(int Month) {
this.Month = Month;
}

/**
* @param Second the Second to set. Gigo
*/
public void setSecond(int Second) {
this.Second = Second;
}

/**
* @param Year the Year to set. Gigo
*/
public void setYear(int Year) {
this.Year = Year;
}
}


'Gotta love these quiet mornings!

-Rn


[ view entry ] ( 1320 views )   |  permalink  |  related link

<<First <Back | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Next> Last>>