Archive for the ‘PHP’ Category

RabbitEdit

Saturday, October 4th, 2008

RabbitEdit is a nifty tool that allows easier Rabbit development and saves you quite some time and effort.
Its purpose is to automatically do particular common things for you, like writing pieces of code, creating parent directories for your new source files, and add the newly created files (and directories) into the SVN repository.
It can be found in rabbit/etc/rabbitedit/ and its installation is as simple as:

# make install 

You can uninstall it similarly, running:

# make uninstall 

Its configuration file can be found at /etc/rabbitedit/rabbitedit.conf and follows the common configuration rules used by the most programs.
You can see how it works, by issuing:

$ rabbitedit --help

A simple example of its usage, could be the following:

$ rabbitedit --parents --svn libs/customer/settings.php

Let’s assume you are in the root directory of your rabbit project. By running the above, RabbitEdit creates libs/customer/ if it does not exist (this is indicated by the “–parents” option), adds that directory into the SVN repository (indicated by “–svn”) as well as libs/customer/settings.php, and if settings.php did not exist before, it creates it and writes a standard piece of code in it (having the information that the file is a lib, and is about the settings of customers). If the file existed before, it leaves it as it was. Then, it opens the file with the source code editor of your choice.

RabbitEdit is written in Python, and is designed with the hope it fastens up the development of Rabbit projects and gives developers the chance to avoid drudgery, and focus on really important things, like actually coding.

“echo” and “md5sum”

Saturday, October 4th, 2008

A problem I faced when I attempted to manually set a password for my user in Water, was that I could not login. I created the md5 sum of my password, running:

$ echo mypassword|md5sum

and pasted it into the MySQL field of phpMyAdmin. After many login failures and enough searching about what is to blame, Kostis90gr found out that “echo”, is outputting a newline at the end of the string, so the md5 sum produced from the command above, was different than the one produced from PHP’s md5() function, simply because they were given different input. So, the right thing to do, is to pass “echo” the “-n” option, like:

$ echo -n mypassword|md5sum

which prevents “echo” from outputting a newline at the end of the string.
I hope this post is helpful, because my unawareness of this “echo”’s little “particularity”, caused me quite enough frustration.

Overloading in PHP must use public methods

Friday, July 25th, 2008

The magically overloading methods in PHP, such as __get, and __set must be public according to The PHP Manual.

Aleksis recently pointed that out to me by making his new methods public, which gave me the initiative to look it up at the manual.

I thought this script to convert them might end up being useful to others, too:

for i in `find -iname "*.php"
               |xargs egrep -i "(private|protected) function __(get|set)"
               |awk -F: '{print $1}'`
do 
sed 's/\(private\|protected\) function __\(get\|set\)/public function __\2/' $i >$i.processed;
mv $i.processed $i
done

Which “false” is it?

Tuesday, May 27th, 2008

dionyziz@turing ~ $ cat test.php
<?php
    class false {
    }
 
    $c = New false();
    echo get_class( $c );
?>
 
dionyziz@turing ~ $ php test.php
false
dionyziz@turing ~ $

Change collation on all columns of a database

Sunday, May 25th, 2008

It was recently required for me to change the collation of each and every column of every table in a database from ‘latin1′ to ‘utf8′. Although the table collations were correct, the column collations were incorrect. It’s a cumbersome process to perform manually, and there’s apparently no real automated way to do it without a script. Although collation information is only meta-data, not actual data, I found this problem interesting.

Changing one column collation information is easy enough to do with one MySQL query:

ALTER TABLE `moods` 
CHANGE `mood_label` `mood_label` text CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Changing all the columns is more difficult. Here’s a small script that I came up with to do it recently:

dionyziz@orion:~$ mysqldump -u root --password=1234 \ 
--no-data --no-create-db --compact ccbeta \
|egrep 'CREATE TABLE|latin1' \
|sed 's/CREATE TABLE `\(.*\)` (/;ALTER TABLE `\1`/' \
|sed 's/character set latin1/CHARACTER SET utf8 COLLATE utf8_unicode_ci/' \
|sed 's/  `\(.*\)`/ CHANGE `\1` `\1`/'>columns
dionyziz@orion:~$ php -r 'file_put_contents( "columns", 
    preg_replace( "#^;|ALTER TABLE `.*`(\\s*;|$)#", "", 
    preg_replace( "#,(\\s*);#", ";\\1", 
    file_get_contents( "columns" ) ) ) );'
dionyziz@orion:~$ mysql -u root --password=1234 ccbeta <columns

Let’s go through it step-by-step.

mysqldump -u root --password=1234 --no-data --no-create-db --compact ccbeta

This creates a list of CREATE TABLE statements for all our tables. That’s good because it’ll allow us to determine whether the collation of a column is incorrect. Here’s an example CREATE TABLE statement:

CREATE TABLE `albums` (
  `album_id` int(11) NOT NULL auto_increment,
  `album_userid` int(11) NOT NULL default '0',
  `album_created` datetime NOT NULL default '0000-00-00 00:00:00',
  `album_name` text character set latin1 NOT NULL,
  `album_description` text character set latin1 NOT NULL,
  PRIMARY KEY  (`album_id`),
  KEY `album_userid` (`album_userid` )
) ENGINE=MyISAM AUTO_INCREMENT=55 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

In this example, the `album_name` and `album_description` columns are wrong and need their collations changed.

egrep 'CREATE TABLE|latin1'

This simple line limits our results to only lines that contain “CREATE TABLE” or “latin1″. That’s useful since it’ll only show the table names followed by a list of all incorrectly collated columns, if any. The result would be something like this:

CREATE TABLE `relations` (
CREATE TABLE `searches` (
 `search_query` text character set latin1 NOT NULL,
CREATE TABLE `shoutbox` (
 `shout_text` text character set latin1 NOT NULL,
 `shout_delreason` text character set latin1 NOT NULL,

(with more entries potentially)

Good. Now all we need to do is modify these lines to make them ALTER TABLE lines:

sed 's/CREATE TABLE `\(.*\)` (/;ALTER TABLE `\1`/'

Ah, the magic of regular expressions. This removes the final “(” of every CREATE TABLE line, as we don’t need it and also changes the word “CREATE” into “ALTER”. It also adds a semicolon in front of the ALTER TABLE statement (to terminate the previous statement).

sed 's/character set latin1/CHARACTER SET utf8 COLLATE utf8_unicode_ci/'

Straightforward enough, this replaces the existing character set instruction from latin1 to utf8, and adds the correct collation as well.

sed 's/  `\(.*\)`/ CHANGE `\1` `\1`/'

Finally, this adds the word “CHANGE” in front of every column line and repeats the column name (as we want to tell MySQL which column to change (first repetition) and to which to change it (second repetition)). The result is:

;ALTER TABLE `relations`
;ALTER TABLE `searches`
 CHANGE `search_query` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
;ALTER TABLE `shoutbox`
 CHANGE `shout_text` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
 CHANGE `shout_delreason` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,

Pretty close to what we actually want. You’ll notice three problems:

  • There are empty ALTER statements
  • There’s an extra comma at the end of every column (providing all your tables have a primary key, as they should)
  • There’s a redundant semicolon at the beginning

These problems cannot easily be fixed by sed because sed performs a line-to-line processing. A sed expert might have been able to provide us with a better solution, but I’ll prefer to use the PREG feature of PHP. To use PHP, first let’s save our current result into a file:

>columns

Time to run our PHP code on the target file:

php -r 'file_put_contents( "columns", 
    preg_replace( "#^;|ALTER TABLE `.*`(\\s*;|$)#", "", 
    preg_replace( "#,(\\s*);#", ";\\1", 
    file_get_contents( "columns" ) ) ) );'

Let’s analyze it in short.

file_get_contents( "columns" );

This, simply enough, reads the “columns” file into memory. Now we’ll perform two regular expression replacements:

First, we’ll match the following regular expression:

#,(\s*);# 

(notice that the # are separators that wrap the regular expression for clarity — they aren’t part of the actual regular expression)

Anything matching this will be replaced by ;\1. This means that a comma followed by any whitespace (including a new line) followed by a semicolon will be replaced by only a semicolon (and the same whitespace). This simply removes the redundant comma at the end of every ALTER statement.

Second, we’ll match the following:

#^;|ALTER TABLE `.*`(\\s*;|$)# 

Anything matching will be removed. You’ll notice that this regular expression matches basically two things (separated by the first alternation (pipe) character).

The first part is:

#^;# 

It’ll remove the first line if it only contains a single semicolon (which it does in our example).

The second part is:

#ALTER TABLE `.*`(\\s*;|$)# 

This will look for empty ALTER TABLE statements (an ALTER TABLE statement followed only by whitespace and a semicolon or an end-of-file) and remove them.

Finally, we’ll write the result back to the file we read from:

file_put_contents( "columns", ... );

Now if we cat that file we’ll see that it contains all ALTER statements in the form we want them:

ALTER TABLE `searches`
 CHANGE `search_query` `search_query` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;
ALTER TABLE `shoutbox`
 CHANGE `shout_text` `shout_text` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
 CHANGE `shout_delreason` `shout_delreason` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;

Excellent. Finally, let’s execute it:

mysql -u root --password=1234 ccbeta <columns

You can also add ‘time’ in front of it to measure how long it’ll take. We can now validate that the collations were changed successfully by, again, performing our initial dump and grepping for ‘latin1′ to confirm that there are none.

Increasing method visibility through extension

Sunday, March 16th, 2008

Although it’s nothing superspecial, I wanted to illustrate how it is possible to modify the visibility of a parent class method when extending it, to convert it from ‘protected’ to ‘public’. Assume we have a base class which exposes a protected function called ‘Test’. We want to extend the class and make that function public in our extension. Here’s how:

<?php
    class Fred {
        protected function Test() {
            ?>Boo!<?php
        }
    }
 
    class Barney extends Fred {
        public function Test() {
            parent::Test();
        }
    }
 
    $barney = New Barney();
    $barney->Test();
?>

Notice that $barney->Test() is a public method call. It’s also possible to disallow this kind of behavior from the parent’s perspective by declaring a method as final. This way it can no longer be overridden (including its visibility):

<?php
    class Fred {
        final protected function Test() {
            ?>Boo!<?php
        }
    }
?>

Mysql fetch array problem

Sunday, June 24th, 2007

Currently I’m working on converting my practice project, a simple uploader program in PHP, into OOP.
Let’s take a look at the construct of the User class. For every instance of the class that is created, there is a check on whether the parameter provided is an id or a username, simply by checking if it’s a string or an integer. If it’s an integer it supposes that the variable given is a user id, and so continues by filling up the other properties.

class User {
 
private $mId;
private $mName;
......
 
function __construct( $construct ) {
 
  if ( is_int( $construct ) ) {  // mySQL query by user id
 
    $this->mId = $construct;
    .......
		
  else {                            // mySQL query by username
 
    $this->mName = $construct;
     .......

In order to display the username which is a private property ($mName) I used a simple fuction

public function GetName() {
 
return $this->mName;
}

So in my index.php I used to following code to display the user who owned a specific folder:

$owner = New User( $folder_owner['id'] );

$folder_owner['id'] was part of an mySQL fetched array which carried the owner’s id.

However, when I called to my previous method to display the username:

echo $owner->GetName();

the result was echoing not the username as expected but instead the user’s id.
foo.txt, created on 12-06-2007 by 2

The class’s methods seem correct, so what was it that went wrong back there?
Let’s look again the creation of my new User object.
$owner = New User( $folder_owner['id'] );

As I said before, $folder_owner['id'] came from a mysql_fetch_array function. When this function is called, the array created from each row is always an array of strings. So even if the cell that contains the requested id in my db contains an integer, the result will always be extracted as a string. As my constructor received a string and not an integer, $this->mName was set with the variable provided, which in our case was in fact the id and not the username, but still had the form of a string. So, calling at the method GetName() would always return the id.

The error was pointed out by my friend and tutor Dionyziz, who also suggested me the simple solution of converting $folder_owner['id'] to an integer before using it as a parameter for my method.

$owner = New User( ( integer ) $folder_owner['id'] );

What will definitely not be forgotten:

  • mysql_fetch_array() always returns an array of strings.

special thanks to Dionyziz for helping me out :)

More info:

mysql_fetch_array()