From the Terminal

Using Meta instead of Ctrl for Cut, Copy, Paste shortcuts in Firefox for Linux

Set these in about:config

accessibility.typeaheadfind isn't blocked by Meta + C / Meta+V so we need to turn it off.

ui.key.generalAccessKey:   224
ui.key.accelKey: 91
accessibility.typeaheadfind: false
accessibility.typeaheadfind.autostart: false

A few useful life cycle tracking variables every ORM should have

Right now I'm in the unusual position of being responsible for maintaining two different ORMs libraries. Occasionally I want to implement a feature that exists in one of them in the other. One such feature recently came up and I think this is something that all ORMs would benefit from whether or not they are custom written or maintained by the community. In fact after looking into things it looks like some of these already exist in one way or another in some of the most popular ORMs available.

Let's take a look at some of them.

  • isNew - False by default. Set to true only when an object that isPhantom is saved
  • isUpdated - Set to true when an object that already existed in the data store is saved.
  • isPhantom - True if this object was instantiated as a brand new object and isn't yet saved.
  • isDirty - False by default. Set to true only when an object has had any field change from it's state when it was instantiated.

Some of these properties might already exist in one form or another in your frameworks. They are simple to implement by simply adding them to the functions in the Model class at different points in the life cycle of the object. Most of them are extremely simple but as you'll see with isDirty consideration might get extremely complex in a hurry.

isNew

The point of isNew is to be able to tell that this object was created during this script execution but was previously saved. Before save it's obvious that an object is new by it's missing primary key. However after saving all evidence of this object being new has been erased. This property lets you know that this object was previously created by you so that if it's part of a large array you can iterate over the items and still know which objects are the "new" ones.

Let's take a look at how this might be useful.

<?php
$cars = Car::GetAll();

$car = new Car();
$car->save();
$cars[] = $car;

foreach($cars as $car) {
    if($car->isNew) {
        // do something special to the objects we created above even if we saved them since then
    }
}

 

isUpdated

This one is pretty simple. This let's us know something that was already in the data store, has been updated by us since initialization.

isPhantom

Let's us know if we need to Update or Insert this model when saving the object. If Insert this gets set to false right after.

 

Implementation Details

Let's table isDirty for now and come back to it. For now let's take a look at some code. Here we do a pretty basic boiler plate for the variables we want and how they are manipulated later on.

__construct()

<?php
public function __construct(..., $isDirty = false, $isPhantom = null /* you can
    implement these differently but the hydrator needs some way to set these
    for an instantiated object */)
    {
        $this->_isPhantom = false; // this should be set by your model hydrator at time of hydration
        $this->_isDirty = $this->_isPhantom || $isDirty;
        $this->_isNew = false;
        $this->_isUpdated = false;
    }

save()

public function save() {
        if ($this->isDirty) {
            // create new or update existing
            if ($this->_isPhantom) {
                // do insert
                $this->_isPhantom = false;
                $this->_isNew = true;
            } else {
                // do update
                $this->_isUpdated = true;
            }

            $this->_isDirty = false; // only if you don't use a function to detect this
        }
}

So here we can see how it all comes together from initialization to storage. But what about isDirty? What's so complex about it? Well let's take a look.

 

isDirty

There's multiple ways to implement isDirty and both have positive aspects as well as drawbacks. If your ORM uses direct PHP object properties that map to fields only one is open to you anyway. Let's take a look at what I mean.

If you use an ORM where the fields are not object properties and are in-fact "not real" that means that you can actually implement this as a simple boolean.

For example:

<?php
public function __construct(...) {
    $this->_isDirty = false;
}

public function __set($field,$value) {
    $this->_isDirty = true;
    ...
}

public function save() {
    ... 
    $this->_isDirty = false;
}

The best thing about this method is how light is it on the run time. You simple need one boolean worth of memory space for each object. Unfortunately this method also can't tell when the field was set back to it's original value or if a field was edited but the value remained the same as it was before. So while you may save some memory up front in your run time you might end up wasting a lot more memory resources in your database updating records that have no new data or simply slowing down batch processes sending writes to the database that it will end up wasting time. Furthermore this only works with dynamically allocated "magic" properties.

Your other option is to use a method to test each field for a change and return true or false. Okay but how? By keeping a copy of the "original" field values of course. And this is where this method starts to show it's down sides. You actually end up approximately doubling the amount of memory you need to make this happen. Plus that itself has complexity. For example do you change your original variables after a save? Unfortunately the answer is yes. Simple because you want isDirty to go back to false after running a save. Some things are free though. Any object that is a phantom is dirty by definition.

public function isDirty()
{
		if ($this->isPhantom) {
			return true;
		}

		$fields = static::getFields();
		foreach ($fields as $field) {
			if ($this->isFieldDirty($field)) {
				return true;
			}
		}
		return false;
}

public function isFieldDirty($field)
{
		if (isset($this->_originalValues[$field])) {
			if ($this->_originalValues[$field] !== $this->$field) {
				return true;
			} else {
				return false;
			}
		}
		return false;
}

Of course you'll need to set_originalValues in your object during hydration. New objects won't start tracking this until immediately after save. It's worth noting that Laravel's Eloquent implements isDirty in a very similar way.

 

Closing Thoughts

Most ORMs implement these one way or another out of necessity however it would be nice if ORMS could standardize on these and perhaps start using the same terminology surrounding them. Is dirty is optional for many ORMs when it's beneficial to force a save() even if nothing has been changed simply to updated the modified at timestamp for the record in the data store. Still a well designed ORM should let you do both depending on your needs.

Ignoring SAPI with Symfony VarDumper

<?php
$varCloner = new \Symfony\Component\VarDumper\Cloner\VarCloner();
$cliDumper = new \Symfony\Component\VarDumper\Dumper\CliDumper();
echo $cliDumper->dump($varCloner->cloneVar($query), true);
		

Quick way to output full text instead of HTML when SAPI is not CLI

The case for system wide dependency injection in PHP

So first let me clarify what I mean when I say "system wide dependency injection".

The typical PHP Composer project offers "project-wide" dependency injection. The autoloader is made for your project and you simply require that one file. However, with other languages it's typical for dependencies to be in a shared folder for the entire system.

In composer we could use a "global" directory in global mode as well like this.

The problem is it's only global "for you". So if you decided to run composer global for another user all the packages you have installed would be different. So it's not global. Not even system-wide really since the home directory might not allow read from other users.

So neither of these modes in composer are really system wide.

In other languages system wide dependencies are stored like so.

For example here's Python.

Perl too.

And Ruby.

Okay so what about PHP?

To fill this hole some Linux developers who choose to include PHP code directly in the system have started to build packages for their distro's package managers.

Here we have the install script for symfony-console in portage. You can see the files are actually stored in /usr/share/php. Unfortunately these files don't have automatically generated autoloaders by composer making integrations very messy.

Portage users aren't the only ones.

Clearly some users choose to install PHP code for use system wide.

 

/usr/share/ or /usr/lib

Let's check the Linux Foundation's documentation on the topic.

Even though PHP code isn't architecture dependent, in my opinion, it's more of a library for programming so to that end I think it makes more sense to use /usr/lib or in my case /usr/lib64 just like other scripting languages. It would also avoid the messy businesses of integrating with none composer package managers.

 

I decided to go ahead and experiment with this idea.

I forked composer and made a composer system command. Just like composer global sets the folder to ~/.config/composer/, with the system command it instead sets the folder to /usr/lib64/php or /usr/lib/php if they exist in that order. You must create /usr/lib{64}/php yourself.

The fork of composer is available here: https://github.com/hparadiz/composer

I then loaded in some really awesome PHP libraries.

With this newfound power to no longer have to bundle libs with my scripts I made a database backup utility for this blog in 50 lines of code with SSH tunneling that drops sql.gz to my local home folder.

Feel free to fork it and make your own.

I had the script output to a Dropbox folder since I have it running already. Which is awesome since that's three locations and one of them is managed for me. I'll be hacking at this some more but for now I like this starting place.

I like that this script is fully portable and editable wherever it is. If I compile it to a phar it would be difficult to make changes or loop it for multiple databases without setting up an entire build configuration despite that logic being relatively simple even for a novice.

For me, at least, the reasons to allow composer to manage dependencies at a system wide level instead of having other package managers do it is self evident.