nabeel shahzad

Archive for the ‘General’ Category

ezDB and PHP 5.3

with 4 comments

As-per Justin’s request, I’ve renamed by fork of ezSQL to ezDB. I’ve updated the github links, it’s now:

http://github.com/nshahzad/ezdb/

This will include changes to the class names, to keep it all even (ezSQL to ezDB). I’m working on APC caching right now, since that’s what I’m using on current project.

PHP 5.3 was also released today! This is an exciting release – with the addition of namespaces and __callStatic(), the static DB class will be much easier to work with (instead of replicating every function). I will be finishing up the 5.2 release first, and then subsequent releases and features, I think I will be posting to the PHP 5.3 release only, unless there’s demand to back-port it all.

I’ll also be implementing some features from CakePHP’s ORM, such as “findBy{ColumnName}({tablename})”, and other simple lookups. I’ve been using Cake alot too, and it’s a great framework.
Cheers!

Written by Nabeel

June 30th, 2009 at 5:21 pm

Posted in General,php,Projects

ezSQL updates

without comments

Well, got github working great. Did an update today, including:

  • $allowed_columns parameter for quick_insert() and quick_update(). So you can pass in $_POST or $_GET, as well as a list of valid indices to use
  • (optional) Exception handling with try/catch (more info)
  • Set return type (object, associative array, numeric array) globally through $default_type

Also, I’m updating the wiki slowly, transferring all the useful information onto there, since it’s easy to keep everything together.

Thanks!

Written by Nabeel

June 29th, 2009 at 1:25 pm

Posted in General

ezSQL on github

without comments

Learning how to use github, so I created a repo for ezSQL.

http://github.com/nshahzad/ezdb

I plan on creating another version (a fork I guess?) for PHP 5.3 when it hopefully launches next week. It’ll have proper namespace support. Haven’t gotten to memcache just yet (hey, been busy! :) , but at least there’s an easier route of distribution now.

Written by Nabeel

June 28th, 2009 at 4:41 pm

Posted in General

PHP Benchmarking

with one comment

I came across a few sites which have some valuable information on the “cost” of certain functions or practices in PHP. It comes in handy to keep in mind and avoid doing, and also to remember that there are better ways of doing things. If your application is coming down to relying on micro-optimization, your application itself needs to be looked at. But there’s no harm in knowing what methods take more time than others. Or of course, you can use ‘em to impress a prospective employer ;)

There are also some lectures and talks on optimizing:

Some good stuff there to look over and to keep in mind, especially obvious things to avoid, such as including the count() inside every for() loop. Actually any of the stuff on http://talks.php.net are a good read, and good to see where PHP has evolved from and where it’s evolving to, and to understand how to leverage the language.

To benchmark your scripts, you can use the PEAR Benchmark package; I have PEAR in my path, so for me it was as simple as including the same code that was in the examples.

Written by Nabeel

March 18th, 2009 at 9:14 am

Posted in General

Tagged with

Nginx + PHP Query Strings

with 4 comments

Nginx (Engine-X) is the sweetest web server I’ve used. It’s running my VPS now, CPU usage has barely budged, even with a decent number of visitors. I ran into a problem, which I was searching for a solution before I cracked at it myself. The solution ended up being incredibly simple, but still may help someone else who’s searchin’ for it.

With nginx, PHP is passed of to FastCGI PHP process to parse and execute. It relies on a location rule to find out what are PHP files. But it would fall apart using URLs like:

Java
1
index.php/some/query/string/parameters


Apache doesn’t have any trouble, with it, but nginx, if you looked at the error log, it’ll be about the directory not existing. (Hmm, parsing that in PHP sounds like a good idea for another post… noted).

So if we check out the current rewrite rules:

Java
1
2
3
4
5
6
location ~ \.php$ {
        include /etc/nginx/conf/fastcgi_params;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /path/to/public/$fastcgi_script_name;
}

The fix is pretty simple:

Java
1
2
3
4
5
6
location ~ \.php(.*)$ {
        include /etc/nginx/conf/fastcgi_params;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /path/to/public/$fastcgi_script_name;
}

Adding in the (.*) after the .php allows the PHP files to be picked up, and those additional query string parameters will get passed along into FastCGI.

Simple fix!

Written by Nabeel

February 24th, 2009 at 6:30 pm

Posted in General

Tagged with , , ,

ezSQL Database Library (Improved!)

with 14 comments

Note: I’ve made a dedicated page for ezSQL-related items, you can view it here

I’ve been using ezSQL for a long time, as my “database driver”. It’s an awesome little class, that you can easily use to get database results, and return in different formats. I’ve made a bunch of changes and updates to it, so I thought I’d put them out there and share them.

Along the way, I’ve made some updates and changes to it, to suite my requirements, including format PHP5 support. I’ve updated the error handling/logging, so now you can call:

Java
1
2
$sql->error(); // Get the error string
$sql->errno(); // Get the error number

Respectively after queries to return the “real” status of a query. the debug() function has been added to, so at call time, you can pass to bool whether to display the result on the screen, or pass it back as a string

I’ve also added several utility functions:

Java
1
2
3
4
5
6
7
$sql->quick_select();
$sql->quick_update();
$sql->quick_insert();
// quick_select() example:
$columns = array('column1', 'column2');
$sql->quick_select('table_name', $columns, 'LIMIT 10');

To make it easier to do simple SELECT’s and UPDATEs, and INSERTs.

Another thing it was missing with MySQLi support, so that has been added (though I have not had the time to add support for statements (though that can be accessed rather easily). I had added MSSQL support, but the file has gone AWOL (Icreated and used it for a specific project a while ago). I will update when I get a hold of it.

And another thing I added was a static interface class, which I use all the time on PHP5 projects:

Java
1
2
3
4
5
6
DB::init('mysql');
DB::connect('username', 'password', 'database_name', 'localhost');
// Now anywhere in your script:
$row = DB::get_row('...');
// Or
$results = DB::get_results('...', ARRAY_A);

Makes it much easier to access the database function, without having to do $db = DB::getInstance(), in every single function to get a singleton object of the database. Since that’s for PHP5 only, the whole library has been updated for PHP 5 support, with public/private functions and constructors/destructors as well.

I also condensed it to just one include() now, just keep all the files in the same place (include ‘…/DB.class.php’). From that you can either use the static class, or declare a new ezSQL object.

I also added in phpDoc blocks on all the functions, since IntelliSense is awesome and really helpful.

I hope these improvements make it easier for everyone. Of course, the original credit goes to Justin Vincent. There are some docs and examples here as well.

You can download it from here

Written by Nabeel

February 3rd, 2009 at 11:52 am

Posted in Fun,General,php,Projects

Reducing HTTP Requests

with one comment

In doing some optimizing for phpVMS, I noticed I have a lot of HTTP requests:

requests_before_2

As you can see, there are 27 HTTP requests, 15 of them JavaScript files. This means, the browser has to reach out 15 times to grab individual JS files. This seems like a straight forward problem, if we can just condense those 15 files into one, then we can almost half the number of total requests.

So we can do this manually – copy and paste each file, into one master file. The problem is, when you update one file, you have to go and search around in your other files, and figure out exactly where to update, or just redo the entire condensing process. It’s a pain. but these seems like a good place to write some code to do it.

As part of Codon now, I’ve included a class called CodonCondenser. It’s just a basic class that will condense requested files into one file. I set out some requirements:

  1. It’s a class, so we can reuse it
  2. Use it for any file types
  3. Have a cached file of our condensed batch, so we don’t need to dynamically condense it every time, which could be expense.
  4. Be able to add/remove files at will, and it will handle that

Since we have some basic requirements, let’s plan how it’ll work:

  1. Set our options, the path to the files, the URL to the files since our function will return the URL to the generated file, set the file type, and a cache
  2. Pass an array, with the list of our files
  3. Generate a MD5 hash of the array we’ve passed, this will be our file name. If the array changes, as in we add files, or remove files, the hash will change, so we satisfy requirement #4… OR… use a filename that we generate, or pass in.
  4. Check this file name, see if it exists; if it does, see if the file age is older than we want (satisfying #3). If it’s not too old, then use this file
  5. If it’s too old, or doesn’t exist, then generate a new condensed file and return the URL to it.

So let’s start:

PHP
1
2
3
4
5
6
7
8
class CodonCondenser
{
    public $path;
    public $url;
    public $timeout = 24;
    public $file_ext = '';
    public $filename;
}

Our basic class, with the settings. Next is our function to set these options:

PHP
1
2
3
4
5
6
7
public function SetOptions($path, $url, $file_ext, $timeout=24)
{
    $this->path = $path;
    $this->url = $url;
    $this->timeout = $timeout;
    $this->file_ext = $file_ext;
}

We pass the $path, which is the absolute path to the files, and $url, which is the path to where the file is will be publicly accessible.

$file_ext is the extension of the condensed file (js, css, htm, etc), and $timeout is the time that the condensed file is considered ‘fresh’. Passing this as blank ($timeout=’’), will disable the time check.

Next is our function to check the cached version. We’ll return true or false if the file passed is valid:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected function getCachedFile()
{
    if(!file_exists($this->path.'/'.$this->filename))
    {
        return false;
    }
    # Check if the version that exists
    # is older than the timeout we have alloted
    # Value of "" skips the time check
    if($this->timeout == '')
    {
        if ((time() - @filemtime($file)) > ($this->timeout*3600))
        {
            # It is older, so delete it
            @unlink($this->path.'/'.$file);
            return false;
        }
    }
    # The cache file is ok
    return true;
}

So what we are checking, if the file doesn’t exist, then return false. Next, if the timeout value is blank, just skip the check, and only base it on whether the condensed file exists or not (as we saw the option up above). Otherwise, we check the modification time of the file, and if the current time – the time file created (in seconds), is greater than our timeout value, then return false. If we have 0 (zero) as a timeout value, it will generate a new file every time.

Note that the function is protected, so we can’t access it directly, and instead, go through the main function:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
public function GetCondensedFile($files, $filename='')
{
    if($filename != '')
    {
        $this->filename = $filename;
    }
    else
    {
        $this->filename = md5(implode('',$files));
        $this->filename .= '.'.$this->file_ext;
    }
}

First, here we are checking if we passed an optional filename. I use the filename option, depending on whether it’s being used in the admin area, or in the front-end client area. I’ll explain this later.

Otherwise, we will build a filename, we check for the cached file, using the function above:

PHP
1
2
3
4
5
6
# Check if we've already made this condensed cache file
# If we have, then just give the URL of that file
if($this->getCachedFile() == true)
{
    return $this->url.'/'.$this->filename;
}

If the getCachedFile() returns true, that mean’s that the cached file is okay, and we exit and just return the full URL path to the condensed file. If it’s not condensed, then we build our condensed file:

PHP
1
2
3
4
5
6
7
8
9
$fp = fopen($this->path.'/'.$this->filename, 'w');
foreach($files as $file)
{
    fwrite($fp, file_get_contents($this->path.'/'.$file));
}
fclose($fp);
return $this->url.'/'.$this->filename;

This is pretty straightforward, we just open the condensed file, and write every file into it, then return the full URL path to the file. So this is how we’ll use it:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$condenser->SetOptions('/var/www/lib/js', 'http://mysite.com/lib/js', 'js');
$files = array( 'jquery.min.js', 'jquery-ui.js',
                'jquery.dimensions.pack.js',
                'jquery.form.js', 'jqModal.js',
                'jquery.bigiframe.js',
                'jquery.sparklines.js',
                'jquery.autocomplete.js',
                'jquery.tablesorter.pack.js',
                'jquery.tablesorter.pager.js',
                'jquery.metadata.js', 'jquery.impromptu.js',
                'jquery.listen-min.js', 'nicEdit.js');
$url = $condenser->GetCondensedFile($files);

And then to link the Javascript into our page:

JavaScript
1
<script type="text/javascript" src="<?php echo $url?>"></script>

Our result in the end:

requests_after_2

14 less requests! Almost half the number of requests. Woohoo! While it’s a basic class, and we can certainly expand it by adding features such as compression (such as implementing JSMin-PHP into), to minify and pack the JS. Be careful if your JS files are already packed if you re-pack them, it could cause errors. I use files that have already been packed, and merge them together this way.

You can also use this for HTML files, CSS, any other text files which are all brought in together in separate requests.

Click to download the full script.

Written by Nabeel

January 8th, 2009 at 3:25 pm

Posted in General

Airplane Movie Quotes

with one comment

I just did a quick thing for the phpVMS admin panel, displaying random quotes from the movie Airplane! in the footer. Just call the function randquote(), and it’ll return the string, so you can do whatever you want with it.

And don’t call me Shirly…

Click to download it (zip file)

Written by Nabeel

January 2nd, 2009 at 12:26 pm

Posted in Fun,General,phpVMS

Tagged with , ,

MySQL “Improved”

without comments

Found an old post I wrote, about the new “MySQLi” functions in PHP 5+. PHP6 is almost on the horizon, and I personally haven’t seen too much wide adoption of this new MySQL functionality.

The i in “MySQLi” stands for i”mproved”. While most of the functions between MySQL and MySQLi will work exactly the same (just by replacing the mysql_ with mysqli such as mysqli_query()), there are several cool things that come with this new "improved" MySQL library. One thing that’s available is to use the database connector itself as a "class", making it easy to create your own database library that is customized to your application. This makes it real easy to separate the logic and extend your database code, for those of us who are OO nuts ;)

Another advantage is that you can also use an SSL connection easily with the database, without jumping through hoops by just providing the path to the certificate. But the best improvement is the ability to use prepared statements. This makes queries (more) secure by telling MySQL exactly which types of values to expect, and also stores a template of those values in memory. The result is security, and since it’s cached, it ends up being much faster on repeat queries and inserts.

I will use the procedural version of the code in these examples, to keep it a bit easier, and I’m also leaving out error checking, for the sake of example.

PHP
1
2
3
4
5
6
7
8
//First we create our connection
$db_link = mysqli_connect('localhost', 'username', 'password', 'db name');
//Now we want to prepare our statement
// So we first have to initialize it.
//Now we prepare the actual statement. The ? replace the variables
// We're going to "insert" those later on
mysqli_stmt_prepare($db_link, 'SELECT * FROM data WHERE stringvalue=? AND numbervalue=?');

So we’re prepared the statement, putting in question marks where there are going to be values placed in. We are “binding” a value into there, so we have to use a function mysqli_stmt_bind_param().  If we look at the parameters for this function, it is:

PHP
1
mysqli_stmt_bind_param(statement, types, variables);

The types, are defined as:

  • i – integer (any whole number)
  • d – double (number, with decimals)
  • s – string
  • b – blob

Since we have to variables in the query above (string value and number value), our "types" are going to be "si". The first is a string (s) and the second is a number (i).

PHP
1
mysql_stmt_bind_param($statement, 'si', $string_value, $number_value);

You might be wondering “What’s the point?”. It helps a great deal with security, when it comes to inserting data:

PHP
1
2
3
4
5
6
7
8
9
10
$statement = mysqli_prepare($link, 'INSERT INTO table (name, idnumber, address) VALUES (?, ?, ?)');
mysqli_stmt_bind_param($statement, 'sis', $name, $idnumber, $address);
$name = 'First Last';
$idnumber = 123456789;
$address = 'I live here';
mysqli_stmt_execute($statement);
mysqli_stmt_close($statement);

As you can see, MySQL is now expecting certain types for the data which is being inserted, and treats it as such – strings as strings, integers as integers, and so on, and will automatically take the best precautions to ensure the data is valid.

For more info, check out the Zend information on MySQLi.

Written by Nabeel

January 1st, 2009 at 9:00 pm

Posted in General

Profiling PHP Code

with 2 comments

After or during writing a project, it’s important to visit your code with a profiler – see how long your code takes to run, and identify potential bottlenecks, and places where you can improve your code.

The profiler I use is Xdebug, a slick PHP debugger, and profiling tool. I use XAMPP for my testing (no running services, turn on Apache and MySQL when I need it), on my machine, and it comes with the xdebug extension by default. I leave xdebug enabled, as it gives great debugging information, including a caller/stack trace. You also need a program called WinCacheGrind, which will allow you to view the profile traces.

To enable Xdebug in your XAMPP config, go to: C:\xampp\apache\bin (I installed it in C:\xampp), and open up your php.ini file, and go down to the “extensions”, and uncomment this line:

[code]
;extension=php_xdebug.dll
[/code]

So it reads:

[code]
extension=php_xdebug.dll
[/code]

And then add this at the bottom of your .ini file (Note: your php.ini file may already have this, just needs to be uncommented)

[code]
[XDebug]
;; Only Zend OR (!) XDebug
zend_extension_ts="C:\xampp\php\ext\php_xdebug.dll"
xdebug.remote_enable=true
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.profiler_enable=1
xdebug.profiler_output_dir="C:\xampp\tmp"
[/code]

This will enable the profiler, as well as the debugging. Restart Apache in the XAMPP control panel, and run a single PHP page (ie, I goto http://localhost/phpvms). Just run it once, and goto your C:\xampp\tmp directory. There will be a file like “cachegrind.out.<number>”. Open this file using WinCacheGrind, and you’ll see something similar to:

cache

I’ve expanded it out, but you can see the time it takes to run each function, load any include()’s, etc. This is a great way to peek into your scripts and see what’s going on in there, and where you can concentrate your optimization efforts. I’ll share some of the things I did to optimize in the future.

Written by Nabeel

December 18th, 2008 at 11:16 am

Posted in General