Curl HTTP Client 2.0

It’s been a while since I last updated my Curl HTTP Client class. That’s the class that we’ve been using for years now, for all kinds of site scrapping, bulk domain registration without API, … and even today we use it as a part of core in our brand new Payment system.

Since I had some spare time this weekend, I finally managed to merge some of updates we’ve created during all these years and put a class to github so I can do frequent updates regularly. Don’t worry, class has retained most of it’s previous functionality explained here so update to latest version shouldn’t cause any problem.

Here’s the GitHub page: https://github.com/dinke/curl_http_client. Feel free to use it in your own projects and send in your comments.

Validating an integer with PHP

When I started writing this post I wasn’t sure should I put this into programming category or fun … I mean, validating if passed variable is integer aka whole number, how hard can it be? It appears not really easy in PHP :)

First off some background, I needed to validate an array with db key values (like array(1,2,6,7,…n)), so I thought of using simple function like array_filter with callback, something like:

array_filter($foo_array, 'is_int');

where is_int is callback, calling built in is_int function for each array value in order to filter out non int values.

The problem is (yes after 10 years dealing with PHP I am aware of that), PHP doesn’t treat int and string numbers the same, so string ’42’ wouldn’t be recognized as integer.

is_int('42'); //false
is_int(42); //true

To make things more “interesting” for programmers, if you have integer form data like age, year or whatever, they will be sent trough $_POST/$_GET/$_REQUEST arrays as ‘string numbers’ (so string ’42’ not int 42).

There is nice function to deal with such things and it’s name is is_numeric … but it only checks if string is actually number, so float values will be evaluated to true as well. ctype_digit on the other hand is opposite of is_int, it will only return true if test variable is string number, so ’42’ would evaluate to true, but 42 to false.

ctype_digit('42'); //true
ctype_digit(42);//false

And to make things even worse, PHP is silently converting really big integers to float (those bigger than PHP_INT_MAX const) so guess what would you get for number like is_int(23234234234235334234234)? Yep, false :)

$var = PHP_INT_MAX;
var_dump($var++); //true
var_dump($var); //false it's float now!

Yeah I know, you could cast var to int and do is_int … or cast var to string and do ctype_digit … and other dirty hacks… but what if someone smart from PHP team had decided to let say add 2nd argument to is_int check so you can check for type in some kind of ‘non strict’ mode, so string ’42’ is actually evaluated as integer? Something like this in C I guess:

function is_int(int var, int strict = 1)
{
   //if strict is false evaluate string integers like '42' to true!
}

All in all (at least to me), the easiest way to validate whether a variable is whole number (aka string or integer string) is with regular expression. Something like this:

/**
 * Test if number is integer, including string integers
 * @param mixed var
 * @return boolean
 */
function isWholeNumber($var)
{
	if(preg_match('/^\d+$/', $var))
	{
		return true;
	}
	else
	{
		return false;
	}
}

Someone from PHP dev team should really consider fixing this.

Debugging with Xdebug and Eclipse on Mac

In my previous posts I explained how to install MacPorts and setup typical Apache/PHP setup with it. Now we’re going to start with something more serious, something that sooner or later every PHP developer will have to deal with. Debugging! And yes when I say debugging I didn’t mean echoing or using var_dump ;)

First off some background info. As far as I know, the very first PHP IDE which actually had debugger built in was Zend Studio. I’ve been using ZDE 5.2.x for very long time, but since Zend stopped supporting it, it was very difficult to continue using it. On Snow Leopard it required some nasty Java hacks to make it working, and any Java update would break it. It was obvious that time of ZDE 5.x was over so I had to pick something new. My choice was Eclipse (I didn’t see any point in paying Zend extra fee for IDE based on Eclipse) so I downloaded Eclipse Hellios release from Zend PDT pages. Unfortunately it was really slow and buggy so after a while I’ve decided to download “classic” Eclipse and get PHP Dev SDK installed manually. Somehow it worked much better for me and since there is new Eclipse Indigo 3.7.1 release right now, I’ve decided to wrote detailed explanation how to install it. In case you already have Eclipse installed and you’re happy with it, just skim to Xdebug setup part.

Setting up Eclipse
————————-
So, let’s download Eclipse 3.7.1 classic and unpack it to some dir. I prefer to use my home dir (which is /Users/dinke/eclipse) but you can pick any, just copy files there and run Eclipse application from that folder. On first run it will ask you about workspace (which is place where Eclipse keeps some internal files), I used /Users/dinke/workspace but you can use any location that suit you. Eclipse will start with Welcome window that we are going to close for now (you can always get it reopened by choosing Help->Welcome)

Eclipse Startup

Now it’s time to download and install PHP Development Tools SDK. So open Help -> Install New Software. Pick Indigo from “work with” drop-down and when list bellow gets updated, go to Programming Languages and check box next to PHP Development Tools SDK as in image bellow.

PDT Startup

Now click next, accept terms and Finish, and PHP tools will be installed. After installation is done (it may take some time), you’ll be offered to restart Eclipse, so do it immediately. After Eclipse has been restarted we are going to switch to PHP perspective. So click to “Open Perspective” icon in upper right corner of Eclipse and pick “Other”. There you’ll be offered options similar as in image bellow:

PHP Perspective

Pick PHP click OK and we’re done with Eclipse setup. Congratulations! Now you can tweak Eclipse to your own preferences (Eclipse->Preferences) or import/start new project and start playing with code.

Xdebug
————
Now we’re going to deal with Xdebug part. First we have to install it, and we’re going to use MacPorts. If you don’t have MacPorts installed just look at my previous post about it.

So, start terminal and type this:

sudo port install php5-xdebug

Restart Apache (sudo /opt/local/apache2/bin/apachectl restart) and make sure that you have proper Xdebug section on phpinfo page (see bellow):

phpinfo

Now we need to add some configuration options to php.ini file, so let’s add this to the bottom of php.ini file (in my case located at /opt/local/etc/php5/php.ini):

[xdebug]
zend_extension="/opt/local/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so"
xdebug.profiler_output_dir = "/tmp/xdebug/"
xdebug.profiler_enable = On
xdebug.remote_enable=On
xdebug.remote_host="localhost"
xdebug.remote_port=9000
xdebug.remote_handler="dbgp"
xdebug.idekey=ECLIPSE_DBGP 

and restart Apache again.

Now let’s configure Eclipse debugging options. Open Eclipse Preferences and then PHP->Debug and instead of Zend pick XDebug from PHP Debugger drop down.

eclipse_debug

Click Configure link next to dropdown, select Xdebug and click configure button again and make sure that in Accept remote session (JIT) is set to any (see image bellow).

eclipse_xdebug_settings

And this is it! Hooray!

Now let’s see how it’s actually working, we’re going to use so called “remote” debugging which allow us to actually debug directly from browser (there’s even firefox ext for that), however I prefer to insert my URL’s directly to configuration options. So select Run->Debug As from the menu and pick PHP Web Page. You will be asked to insert URL and then Debugging perspective will be opened along with editor. There you have ability to step into code, setup break points and watch variables on the right.

debugger

Happy Debugging :)

Useful Links:
Eclipse/Xdebug Remote debugging on Windows
Xdebug

MySQL: Deleting with Left Join

Today I had to deal with one huge table and cleanup all data where foreign key doesn’t have it’s primary key match in original table, just to remind myself how sub-queries in MySQL are terrible slower than joins.

I have some script which generates domains from typos, so I have one table with original domains (master_domains) and other one (result_domains) with generated typo domains. Basically something like this:

mysql> describe master_domains;
+--------+------------------+------+-----+---------+----------------+
| Field  | Type             | Null | Key | Default | Extra          |
+--------+------------------+------+-----+---------+----------------+
| id     | int(10) unsigned | NO   | PRI | NULL    | auto_increment | 
| domain | varchar(255)     | NO   | UNI | NULL    |                | 
+--------+------------------+------+-----+---------+----------------+
2 rows in set (0.07 sec)

mysql> describe result_domains;
+-----------+------------------+------+-----+---------+----------------+
| Field     | Type             | Null | Key | Default | Extra          |
+-----------+------------------+------+-----+---------+----------------+
| id        | int(10) unsigned | NO   | PRI | NULL    | auto_increment | 
| domain    | varchar(255)     | NO   | UNI | NULL    |                | 
| master_id | int(10) unsigned | YES  | MUL | NULL    |                | 
+-----------+------------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

Table result_domains has master_id which is foreign key reference to primary key (id) in master_domains table. Since I also have other scripts generating domains without typos (which store result_domains.master_id field as NULL), today I simple wanted to get rid of those masters without proper master_id reference in result table or in other words those master domains where result_domains.master_id is NOT NULL.

With sub-queries you could write query easily with something like this:

delete from master_domains where id not in 
(select master_id from result_domains_frontend)

It is good habit to always run select query before deleting big number of rows (just to make sure your query is written correctly) so I tried select query first:

select * from master_domains where id not in 
(select master_id from result_domains_frontend) limit 10

However, it took several minutes to run without any output so eventually I’ve decided to stop it. I know that sub-queries are much slower than joins, so decided to do try removal operation with left join.

Left joins are actually perfect weapon to find rows that exist in one (left) and doesn’t exist in other (right) table. They also have one big advantage over sub-queries – they are performing much faster, plus they are backward compatible with old prehistoric MySQL 5.x versions. However delete syntax is little bit tricky so after few trial and errors eventually I came out with this query:

delete master_domains.* from master_domains 
left join result_domains_frontend 
on master_domains.id=result_domains_frontend.master_id 
where result_domains_frontend.master_id is null ;

And voila after a while it came up with result:

mysql> delete master_domains.* from master_domains 
left join result_domains_frontend 
on master_domains.id=result_domains_frontend.master_id 
where result_domains_frontend.master_id is null ;
Query OK, 270558 rows affected (46.58 sec)
mysql> 

MySQL: Moving table from one db to another

To move one table from one db to another, you can create new table (create table foo_new like foo) in db where you want to move table and then copy data with insert into/select query. However there is much easier way which is especially handy when you deal with big tables.

As you probably aready know, there is easy way to rename MySQL table just by issuing rename clause in alter statement:

ALTER TABLE foo RENAME TO new_foo;

You can also use RENAME TABLE syntax like this:

RENAME TABLE foo TO new_foo;

Now, when you need to move table from one db to another, all you have to do is to specify it’s current db and new db name as table prefix. For example if you want to move table foo from current db to new db you can issue queries like these:

ALTER TABLE currentdb.foo RENAME TO newdb.foo;

or

RENAME TABLE currentdb.foo TO newdb.foo;

Btw there is important difference between ALTER and RENAME statements in a way that with RENAME you can rename more than one tables at once. This comes handy if you want for example to swap names of two tables:

RENAME TABLE table1 TO temp, table2 TO table1, temp TO table2;

Next Page »