Creating dirs with correct chmod in PHP

One trick I've been dragging with me on all this years of PHP programing is a little snippet to correctly create directories with the chmod I want.

By simply calling mkdir('my_dir', 0777) I used to often end up with directories that I can't write to nor delete, even if I was correctly setting the chmod.

The trick was to reset the mask (using umask(0)) before the mkdir()call and then reapplying the old mask after.

$tmpUmask = umask(0);
mkdir('my_dir', 0777);
umask($tmpUmask);

I must admit that I've never really understand why it was working better than simply calling mkdir() but hey, it's been years that I'm using that now and I never run into access rights issues since.

Calling a parent method with arguments in PHP

For testing purpose I just needed to overwrite an existing class to add some more logic to some methods before returning a call to its parent method.

I needed to do that in a test case to save in an inner variable the result so I can clean up my mess after each test ran.

The key is calling call_user_func_array with the right syntax. It seems that some version of PHP prior to 5.3 choke (like in segmentation fault) if an alternate syntax is given.

Here is what worked for me :

class TestDocument extends Document {
    function foo() {
        return $this->fooResult= call_user_func_array(array('parent', 'foo'), func_get_args());
    }
}

SWFUpload and cakePHP

One thing that always sent me an awful hours of debugging is the fact that the Flash player identify itself as a whole different browser that the one it is integrated into.

I've ran into this issue multiple times when dealing with SWFUpload and today was one more. As I always spent quite a lot of time debugging issues arising from this, I guess I should post about it here.

The Flash player uses a different userAgent that your browser

Most of the time, this is not a problem. But when dealing with restricted areas of a website built on top of cakePHP, it can become one.

The thing is that as the userAgentstring used by the Flash player is not the same as the one used by your browser. So, when you make a request to your site, cake will see that as whole new request and you won't be able to access your current session variables.

As you can't overwrite the userAgenthash sent, you need to send the sessionIdalong with your flash request. That way, you'll be able to call a

$this->Session->id($sessionId);
$this->Session->start();

in your controller beforeFilter(or component initialize)

In cake 1.2, you'll also have to call $this->Session->destroy() before to delete the Flash session created.

The Flash player uses a different cookie pool that your browser

This used to be an issue in 1.2 but not longer is.

Cake stores in a cookie the name of the session you're supposed to use. Flash having its own cookie pool, it will save its own cookie (usually somewhere you don't even know) and will always check for the session specified inside.

This took me quite a long time to found out why the flash request where reading an other request that the one I was setting.

In 1.2, you needed to delete the cookie created by cake whenever you made a flash request to avoid conflicts

setcookie(Configure::read('Session.cookie'), '', time() - 42000, $this->Session->path);

cakePHP used to overwrite the userAgent string in session

In cake 1.2, when you manually change the current session, cake would update the inner userAgenthash saved to your current userAgent hash.

This meant that whenever you were moving from the Flash session to the correct session, you had to overwrite the userAgenthash to the correct one.

$this->Session->write('Config.userAgent', $userAgent);

This is no longer the case in 1.3, changing the current session does not alter it anymore.

Doesn't all that stuff alter security ?

Well, most of the answers I found online were among the lines of "Just disable the check of the userAgent", so I think my method is a little bit more secure.

Anyway, passing a sessionIdas a parameter seems a little risky, even to me. I guess there should be a way of encrypting it to not pass it as clear text

UPDATE !

I had to fiddle with this script some more. It appears that, as we are changing the sessionId, we need to do that BEFORE we access the session. It means that any call to Session::read() or Session::check(), or almost any other Session method BEFORE setting the id will block this trick from working. So, make sure that this code is executed before anything else, put it in a component that will be loaded first.

It also appears that if you follow my advice, you'll only have to call id($sessionId), and none of the hocum pocum about destroy, writeand userAgenthashes I talked about...

I just lost some hours finding this out. I add a call to Session in my I18ncomponent that was rendering this whole fix useless. It was driving me crazy...

Forcing joins in a cakePHP find

Today I had to setup a complex find relation. Here is the simplifed version of what I had :

TABLE timestamps
int id
datetime date
string type
int user_id

The type field only had two types of values : STARTand END. As you can guess, this was used to log the time users where using an application. Every time a user started using the app, a START record was created, and when he loggued out, an ENDrecord was created. So basically, the records where working as pairs.

I wanted to get a list of all records that could be easily displayed. I wanted to bind the timestamp model to itself, so that when querying all the start records, I'll automatically have the end ones as related models.

Here's how I did that :

$this->find('all', array(
  'conditions' => array(
    'Timestamp.type' => 'START'
  ),
  'joins' => array(
    array(
      'table' => 'timestamp',
      'alias' => 'EndTimestamp',
      'type' => 'LEFT',
      'conditions' => array(
        'EndTimestamp.type' => 'END',
        'EndTimestamp.user_id = Timestamp.user_id',
        'EndTimestamp.date > Timestamp.date',
      )
    )
  ),
  'order' => array(
    'Timestamp.date' => 'ASC'
  ),
  'group' => 'Timestamp.date'
));

It will fetch all the start timestamp (fields) in chronological order (order). We will also define a custom join relation (joins). We set the table name and the alias we need, and set it as a JOIN LEFT.

Then we add the conditions : we want only the ENDrecords, that belongs to the same user, and that occurs after the STARTrecords. We also add a groupkey to make sure not to get twice the same result (or it will corrupt our results)

Note that the joins syntax needs to be wrapped in an unkeyed array. This is because you may need to add several joins.

I had never heard of this joins key before today, but it is quite handy, I guess I'll use it again.

Allow click through an HTML element

When one HTML element is over another one (like when positioning an element using position:absolute), you usually can't click through the top element to access the bottom element.

That's used as a common technique to prevent the right click on images by some sites (like Flickr). They just add an empty transparent div over their images to prevent the lambda user from right clicking and saving the image.

Sometimes, when integrating complex designs, you need those additional layers, but you also want the user to be able to click through them, as if they weren't there.

Just use the pointer-events:none css property to allow click events to go through the element.

This is only supported by Firefox 3.6+, Chrome and Safari for now.