Really simple CSS compression function in PHP

Thanks to the simple syntax of CSS, we can easily compress the CSS code with a PHP function of only 3 lines of codes.

Since the compression will remove all the code indention and comments so it should be apply to production codes only.

Here is the CSS compression that I used in my projects.


function CSS_Compress(&$css){
    //new lines, multiple spaces/tabs/newlines
    $css = preg_replace('/[\r\n\t\s]+/s', ' ', $css);
    //remove comments
    $css = preg_replace('#/\*.*?\*/#', '', $css);
    //remove extra single spaces
    $css = preg_replace('/[\s]*([\{\},;:])[\s]*/', '\1', $css);
}

Fixing the drive letter for RAMDisk without saving and loading drive image – A solution for Dataram RAMDisk

[Note: 我們另外為此文章撰寫了中文版本,請看這篇。]

Using RAMDisk

One of the most popular RAMDisk solution is Dataram RAMDisk. It is FREE to setup a RAMDisk of max. 4GB size, running in both 32-bit & 64bit Windows systems (Usually other RAMDisk solutions would charge quite much for a 64bit version.) with promising performance. If you are using a 32bit Windows, Dataram RAMDisk  can access the RAM outside the address range(the so-called 4GB RAM limit in 32bit Windows) so your RAM wouldn’t be wasted. I have been using Dataram RAMDisk since I upgraded to 64bit Windows 7, and I’m totally satisfied with its performance and stability.

The problem

My only complain about Dataram RAMDisk is that the drive letter of the RAMDisk is dynamically assigned on each time it runs. So today I have “K:” as my RAMDisk and have my browser cache folder configured to “K:”. On the next morning if I have plugged in an USB drive (or assigned another network drive) before the RAMDisk being created, my USB / network drive will occupy the drive letter “K:” so the RAMDisk will become “L:”. This will be a big disaster because all the cache will be written into the USB / network drive instead of the RAMDisk. So I want the RAMDisk keeps a consistent drive letter all the time.

Official solution

In the user manual, Dataram provides a solution through saving the RAMDrive into a drive image file once and loading it back on every time the computer starts. I personally don’t like it because :

  1. It creates a large disk image file (same size as the RAMDisk) on the harddisk, and
  2. It takes quite much overhead to load the disk image file on every booting.

My solution : A DOS Batch script

Instead, I write my own solution using just a 3-line (without comments) DOS batch script. Thanks to the feature that Dataram RAMDisk allows users to specify a Drive Label and keep it consistent all the time. So my trick is: When the system starts, the script will find the dynamic RAMDisk drive letter (e.g. “K:”) according to the disk label, then create a consistent drive letter (e.g. “R:”) that mounts to the dynamic drive letter (“K:”).

Step-by-step walk-through

To make it simple, I post the walk-through here:

1. Create the batch file.

Create a new .bat file (e.g. C:\StartUp.bat) with the following content:

(Update: Please note that batch script takes line-break as “end of command”, so if you find the script doesn’t work at your PC, try removing extra line-breaks. See the comments for more details.)

@rem assign the key variables.
SET _label=RAMDISK
SET _ramdrive=R:

@rem Below is the magic happens : Find the drive letter which has volume name set as _label, and mount it to _ramdrive
FOR /F "skip=1 tokens=1 delims=: " %%a IN ('wmic logicaldisk where "VOLUMENAME='%_label%'" get caption') DO ( subst %_ramdrive% %%a:\ )

@rem if you want to copy some files or mount your cache folders, do it after this line.

If you are interested in how this magic script works, read below. Otherwise you can safely skip it and go on step 2.

The command :

wmic logicaldisk where "VOLUMENAME='RAMDISK'" get caption

will run once, and gives such result :

Caption
K:

This result is returned as a string. Then a for-loop will go through the result string line-by-line. For each line, the string is split by “:” into parts and the first part will be stored into variable %%a.

The for-loop options explained:

“skip=1” : Skip the first line (i.e. “Caption”). The first line is the header, we are not interested in it.

“tokens=1” : Only get the first part after the split. The first part is the drive letter.

“delims=:” : Use “:” as the delimiter to split the string.

So at the end the variable  %%a would be assigned with value “K”.  Then the subst command becomes

subst R: K:\

which will mount the R: to the K:

2. Specify the Drive Label for RAMDisk

Open the Dataram RAMDisk Configuration Utility, check the “Disk Label” box and enter a disk label “RAMDISK” in the text box below.

You may assign name other than “RAMDisk”, but if you do, remember to update the DOS batch script with

SET _label=TheNewDriveLabel

3. Assign the startup script

So that the computer will run the StartUp.bat each time when the computer boots. There are a few steps to do this.

3.1 Press Win + R (or find “Run” on start menu), a “Run” dialog will appear. Enter “gpedit.msc” and select the “gpedit” in Programs.

Note: For Win7 Home version where the gpedit.msc is not bundled, you may consider using the Task Scheduler to launch the script after Windows starts. Please visit this page for the details: http://windows.microsoft.com/en-US/windows7/schedule-a-task

3.2 In the “Group Policy Editor”, click “Local Computer Policy” -> “Windows Settings” -> “Scripts(Startup/Shutdown)”  then on the right side, click “Startup”

Startup script

3.3 On the “Scripts” tab, click “Add…” and add the “C:\StartUp.bat” to the list. Click “OK” to finish.

4. That’s it!

Now restart the computer and there will be a “R:” drive that is mounted to the RAMDisk, no matter what drive letter the RAMDisk actually is.

Conclusion

The advantage of this solution over the Dataram’s solution is: this solution doesn’t require a large disk image file, thus the overhead of reading and loading the disk image is none. This reaults in a faster system booting and less disk space occupied.

A final note about this solution: There will be two drive letters assigned to the same drive. My solution creates a new virtual drive with consistent drive letter (“R:”), and mount it to the actual RAMDisk (“K:”). Although the RAMDisk can always be referred as “R:”,  the “K:” is still there. Currently I have no idea how to remove or hide the “K:” in the batch script. So you will end up with both “K:” and “R:” accessing to the same drive.

Comments are welcomed!

Updated-19/06/2011: Merijn (see comments below) suggested a very good approach using the “diskpart” command instead of “subst”. It is a better solution because it alters the drive letter of the RAMDisk directly, so at the end you won’t have two drives on “My Computer” accessing the same drive.

Check if an array is associative or sequential(indexed) in PHP

In PHP, depending on the type of the keys in an array, the array can be identified as associative (if the key is a string like $arr[‘mykey’]) or sequential (some may call it indexed, i.e. if the key is an integer like $arr[1], $arr[2], etc.).

Although the internal representation of both associative and sequential arrays are the same in the PHP’s core (Both are ordered map), in some situations we still need to differentiate one from another. There is no built-in function to tell if an array is associative or sequential, so we have to write our own.

The most popular method to check this is to compare the keys of an array with the result of range(), like this, this and this:


function isAssoc($arr){
	return array_keys($arr) !== range(0, count($arr) - 1);
}

The problem of using range() is that it only works when the keys of the array are continuous integer numbers. That is, the keys of an array has to be {0,1,2,3,4,…..}. If the array’s smallest key is not 0 but 1, like {1,2,3….}, or there is a “hole” in between, like {1,2,4,5,8,…}. They will be treated as associative array.

Whether this method makes sense depends on how you define the term ”sequential array” and “associative array”. Unfortunately I didn’t find any “official” definition about them in PHP’s manual (as they doesn’t matter to PHP). My personal definition is that when there exist a non-integer key in an array, the array is not sequential. So for the arrays with keys like  {1,2,100, 1000, 1500}, it is still sequential. With this logic, my method to check associative / sequential arrays is this:


function is_asso($a) {
	foreach(array_keys($a) as $key)
		if (!is_int($key)) return TRUE;
	return FALSE;
}


This function will check every keys in the array until it find one that is not an integer, then it quit the loop and tell it is associative array. This function can handle sequential arrays with any starting index, and any hole in between.