May 072012
 

Disclaimer: This isn’t going to be about php, so if that doesn’t tickly your fancy feel free to skip this entry. It’s also a bit of a rant.

Every now and then you figure out you want to do some cool css trick or update some css or markup that’s been lagging behind. Last friday I was in total weekend mode so I figured I’d update some of the old markup and css in the project I’m currently working on. The header area in that project was one of the first pieces of html and css created when we started developing it, and it has been left largely untouched since those early days in 2009. It was in desperate need of some tender CSS love.

The first thing I wanted to fix was the header background. It’s got a gradient background image, with a logo that has the gradient as background color. Depending on which browser you’re using, the background image and the logo is always one or two pixels off, because that’s just the way the markup is. We still have to support relatively old and obscure browsers like IE7, so keeping backwards compatibility is important to not unnecessarily annoy our customer(s). So, since we’re living in 2012 nowadays it should be as easy as adding a css background gradient with a fallback to the background image:

background: url("/images/navbar_top.png") repeat-x; /* legacy background image */
background: linear-gradient(to bottom, #434A50 0%,#2B3036 80%); /* W3C */

Yeah, not so much. The CSS level 3 specification for background gradients is still not finished and all browsers that has implemented it so far has done so based on early drafts, their own interpretation of the specification as well as their own desires about how the specification should work. According to the MDN page about CSS linear gradients, achieving this effect across all browsers is not at all trivial.

First of all: the background gradient CSS3 specification is of course not yet final and it is still subject to changes. Also, every browser on the planet has its own way of specifying background gradients, and they’re all non-compatible as well as wildly different. If that wasn’t enough to completely turn you off CSS gradients, bear in mind that each browser also has different browser-/vendor-specific implementations depending on the browser version, as the recommended implementation has been changed quite a lot since Apple introduced this feature back in 2008. To end it all, the currently final version of the CSS3 recommended standard has switched around one of the keywords – instead of specifying where you want the gradient to start (“top”), you specify the direction (“to bottom”) – making the final recommendation inversely compatible with the currently available browser-specific implementations. So, to implement something as trivial as a background gradient you need to take into consideration several different versions of a multitude of browsers, as well as a totally moving specification. With that in mind, here is the bit of CSS I ended up using for that header background gradient:

background: url("/images/navbar_top.png") repeat-x; /* legacy background image */
background: -moz-linear-gradient(top, #434A50 0%, #2B3036 80%); /* FF3.6+ */
background: -khtml-gradient(linear, left top, left bottom, color-stop(0%,#434A50), color-stop(80%,#2B3036)); /* KHTML */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#434A50), color-stop(80%,#2B3036)); /* Chrome, Safari4+ */
background: -webkit-linear-gradient(top, #434A50 0%,#2B3036 80%); /* Chrome10+, Safari5.1+ */
background: -o-linear-gradient(top, #434A50 0%,#2B3036 80%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #434A50 0%,#2B3036 80%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#434A50', endColorstr='#2B3036',GradientType=0 ); /* IE6-9 */
background: linear-gradient(to bottom, #434A50 0%,#2B3036 80%); /* Current W3C recommendation */

I kid you not.
Luckily, with this blob of CSS we’re able to support pretty much all browsers in use today. Yay for us! Only marginally smaller than the byte size of the image we’re currently using!

I’ll follow up with a new post soon, with more juicy CSS fun. Comments are much appreciated!

Feb 282012
 

Timezone handling can sometimes be a bitch. Turns out, using a couple of PHPs classes and functions, it’s quite easy.

Let’s say you have a web application with the following scenario:
Users from around the planet, and your server is located in the UK.

You would initialize your DateTime object like this:

$timezoneUTC = new DateTimeZone('UTC');
$dateTime = new DateTime('2012-02-23 10:22', $timezoneUTC);

echo $dateTime->format('Y-m-d H:i e').'<br/>';

This should output:
2012-02-23 10:22 UTC

For an international user, seeing his or her local time would be nice.
To do this, simply change the timezone of the existing DateTime object, like this:

$timeZoneSweden = new DateTimeZone('Europe/Stockholm');
$dateTime->setTimeZone($timeZoneSweden);
echo $dateTime->format('Y-m-d H:i e').'<br/>';

$timeZoneNY = new DateTimeZone('America/New_York');
$dateTime->setTimeZone($timeZoneNY);
echo $dateTime->format('Y-m-d H:i e').'<br/>';

$timeZoneBKK = new DateTimeZone('Asia/Bangkok');
$dateTime->setTimeZone($timeZoneBKK);
echo $dateTime->format('Y-m-d H:i e').'<br/>';

This should output:
2012-02-23 11:22 Europe/Stockholm
2012-02-23 05:22 America/New_York
2012-02-23 17:22 Asia/Bangkok

The final code should look something like this:

$timezoneUTC = new DateTimeZone('UTC');
$timeZoneSweden = new DateTimeZone('Europe/Stockholm');
$dateTime = new DateTime('2012-02-23 10:22', $timezoneUTC);
$dateTime->setTimeZone($timeZoneSweden);

This will leave a DateTime object set with the users timezone.

And that’s it.

Dec 182011
 

Some time it is necessary to fool your PHP application to test functionality related to a “current date”.

Fooling php cli is simple using faketime:

Say you have this PHP script:

#!/usr/bin/env php 
<?php
print date( 'F j. Y [H:i]' )."\n";

Rendering this script with php through faketime will give you results like this:

$ faketime 'last Friday 5 pm' ./time.php 
December 9. 2011 [17:00]

The same can be achieved using the datefudge command:

$ datefudge "2007-04-01 10:23" ./time.php 
April 1. 2007 [10:23]

Faking time for a webapplication

Faking time for a web application is not that simple since apache will fork a process for each request and thus create a new php processes.
In this post I will show you how to use FakeTime Preload Library to fake time system wide while running tests on a web application.

First I need to install faketimelib as described on the librarys homepage.
In short:

wget http://www.code-wizards.com/projects/libfaketime/libfaketime-0.8.1.tar.gz
tar -xvzf libfaketime-0.8.1.tar.gz
cd libfaketime-0.8.1
vim README
make
sudo make install
export LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1

For the demonstration I will use a php script like this:

<?php
printf( "The current time of the server is: %s\n", date('l F j. Y [H:i:s]') );

Running this script should yield something like:

$ php time.php 
The current time of the server is: Friday December 16. 2011 [08:02:43]

Now I create a file in my home folder, faketimerc, with a new future time. I will use this file in different ways to show how I can manipulate the time.

echo "@2012-12-21 12:12:12" > faketimerc

If you now create an environment variable, FAKETIME, give it a future time, and running the same script would yield something like this:

$ php time.php 
The current time of the server is: Friday December 16. 2011 [08:19:39]
$ export FAKETIME=$(cat faketimerc)
$ php time.php 
The current time of the server is: Friday December 21. 2012 [12:12:12]
$ unset FAKETIME
$ php time.php 
The current time of the server is: Friday December 16. 2011 [08:19:56]

As you can see, the processes you run while the environment variable is set to a future time will get a fake time. Once the variable is unset new processes will get normal time.

This can be achieved also by creating a file. “.faketimerc” in your home folder:

$ php time.php 
The current time of the server is: Friday December 16. 2011 [08:23:12]
$ cp faketimerc .faketimerc
$ php time.php 
The current time of the server is: Friday December 21. 2012 [12:12:12]
$ rm -f .faketimerc 
$ php time.php 
The current time of the server is: Friday December 16. 2011 [08:23:36]

If you want to change time for a php application that runs though apache you may want to set the fake time system wide so that the php processes spowned use the faked time. To do this you need to create a file, /etc/.faketimerc, just the same as the one I created in my home folder.

I will use w3m for this demonstration. I assume a normal ubuntu server with apache2 installed.

sudo cp time.php /var/www
sudo cp faketimerc /etc/
$ sudo /etc/init.d/apache2 restart
 * Restarting web server apache2                                                                                                                                                                                                                                                   ... waiting
$ php /var/www/time.php
The current time of the server is: Friday December 21. 2012 [12:12:12]
$ date
fr. 21. des. 12:12:12 +0100 2012
$ w3m -dump localhost/time.php
The current time of the server is: Friday December 16. 2011 [11:16:24]

As you can see, the date command and when rendering the script with php, gives me the fake time, but when rendering the script through apache I loose the fake time. This is because the environment variable LD_PRELOAD is not set for the apache process.

To fix this I need to set LD_PRELOAD for apache by editing /etc/apache/envvars. Lets test it again:

$ sudo bash -c "echo \"export LD_PRELOAD='/usr/local/lib/faketime/libfaketime.so.1'\" >> /etc/apache2/envvars"
$ sudo /etc/init.d/apache2 restart
 * Restarting web server apache2                                                                                                                                                                                                                                                   ... waiting
$ w3m -dump localhost/time.php
The current time of the server is: Friday December 21. 2012 [12:12:16]

Fake time! :)

PS! If you also want your PostgreSQL server to use fake time:

$ sudo bash -c "echo \"LD_PRELOAD='/usr/local/lib/faketime/libfaketime.so.1'\" >> /etc/postgresql/8.4/main/environment"
$ sudo /etc/init.d/postgresql-8.4 restart
 Posted by at 12:47 pm
Nov 012011
 

When you need to see if any of the chars in $chars is in another string.. Whats the simplest way to search for them?
Hopefully none of the libraries have such functionality, so you need to go “a bit lower”.
The answer is, ofcourse, trivial:

$chars = "xy";
$string = "the quick brown fox jumps over the lazy dog";

$found = false;
for($l = 0; $l < strlen($chars); $l++) {
        if (strpos($string, $chars[$l]) !== false) {
                $found = true;
                break;
        }
}
if ($found) {
        /* Do stuff */
}

But hang on. This feels a bit weird. Surely PHP must have a better way of doing this?
Browsing through the string section in the PHP manual you’ll notice PHP has bucketloads of native string functions. If you have a background from other languages, you could even just try and see if PHP has a function of the same name (which quite often it does) that solves the problem.

And sure enough, it does: strpbrk!

$chars = "xy";
$string = "the quick brown fox jumps over the lazy dog";
if (strpbrk($string, $chars)) {
        /* do stuff */
}

Just give the “problem” a second thought before going crazy with your coding, and keep in mind you aren’t just working with Symfony, Drupal, SugarCRM, WordPress, … Your good old pal, PHP, is there too.

Oct 222011
 

Which resolution web application should be optimized to, is a question with different answer mainly depended on time when asked. The average screen size and resolution have been growing as a result of technology evolution and falling prices of electronic equipment.
Resolution usage, Source: http://www.w3counter.com/
Resolution usage, Source: http://www.w3counter.com/

At the same time mobile devices, like pads and cell phones, with small screens and low resolution, became capable to browse Internet in a more effective way.
Mobile browsing, Source: http://gs.statcounter.com
Mobile browsing, Source: http://gs.statcounter.com

So, what to do if I have in my requirements support for IPhone 3GS 3,5” 320×480 and PC screen with high resolution 1920×1080. Somehow I must optimize my application to these two resolutions.

One of the common solutions is to split the application on view level by implementing two versions of templates. One optimized for high and the other for low resolution devices. It is going to work, but is that really a good solution from design point of view. The main disadvantage is that the application is still not supporting all possible resolutions, is just optimized to high and low one. And of course another issue is maintainability and future compatibility.

What if instead of optimizing application to a specific resolution, I could support all of them. It is actually not a new idea. It has been ages since I could use size=”100%” attribute in some html tags. By doing this html element is always scaled to the maximum available size. It gives some flexibility. However scaling has limitations, sooner or later I will reach a stress point where scaling is not effective anymore and I would need to change layout by relocation some elements of interface.

So, to optimize web application to any resolution I would need to:

  • scale html objects
  • dynamically change layout
  • scale images

Generally speaking, I need an ability to alter my application, in order to continually reflect the environmental conditions.

This is what “Responsive Web Design” stands for.

In practice responsive web design is an intelligent use of flexible grids, layouts, images and CSS media queries.

Media Queries

With media queries I’m able to resolve the first two first problems, I can scale html elements and change layout.

Since CSS 2.1 there is a possibility to define custom style sheets for different media types.

@media print {
/* style sheet for print goes here */
}

@media screen {
/* style sheet for screen goes here */
}

CSS 3 offers an extension called media query, which allows to specify conditions when the specified style sheet will affect user interface.

@media screen and (max-width: 640px) {
/* Window size < 640px */
}

@media screen and (max-width: 800px) and (min-width: 640px) {
/* Window size between 640px and 800px */
}

@media screen and (max-width: 1024px) and (min-width: 800px), (max-width: 640px){
/* Window size between 1024px and 800px or less than 640px */
}

Here is an example of valid media query definitions:

<link rel="stylesheet" type="text/css" media="screen and (max-width: 640px)" href="shetland.css" />

@media screen and (max-width: 640px) {
/* Window size < 640px */
}

@import url("style.css") screen and (max-width: 640px);

If a web browser does not support media queries css is loaded always without any conditions, this is unwanted behavior. If you want to prevent loading css with media queries on not supported web browsers you can add word “only” before media type:

/* add word «only» to be ignored on web browsers with out support */
@media only screen and (max-width: 640px) {
/* Window size < 640px */
}

There is quite many criterias available in CSS3, however the two first on the below list are the most usable.

  • max-width / min-width
  • max-device-width / min-device-width
  • orientation (portrait/landscape)
  • device-aspect-ratio
  • min-resolution / max-resolution
  • monochrome
  • Min-color-index

See http://www.w3.org/TR/css3-mediaqueries for more

CSS Media queries are supported by the following web browsers:

  • Firefox 3.5+
  • Chrome
  • Safari
  • Opera 9.5+
  • Opera Mini
  • Android Browser
  • Opera Mobile
  • IE9+

CSS media queries are quite useful if the goal is to affecting mobile devices, it is supported by most of the web browsers used on mobile devices.
But if the goal is to support older web browsers Java Script comes with help.

Here is an example of loadCss() and removeCss() methods that can be used to dynamically load and remove css files.


/**
* Load CSS file
*/
function loadCss(filename){
var links = document.getElementsByTagName("link");
for (var i=0; i < links.length; i++) {
if(links[i].getAttribute("href") == filename) return;
}
var fr=document.createElement("link")
fr.setAttribute("rel", "stylesheet")
fr.setAttribute("type", "text/css")
fr.setAttribute("id", filename)
fr.setAttribute("href", filename)
document.getElementsByTagName("head")[0].appendChild(fr)
}

/**
* Remove CSS file
*/
function removeCss(filename) {
var links = document.getElementsByTagName("link");
var parent = document.getElementsByTagName("head")[0];
for (var i=0; i < links.length; i++) {
if(links[i].getAttribute("href") == filename) {
parent.removeChild(links[i]);
}
}
}

 

Adding below code to onResize event will be equal to: @import url(“mini.css”) screen and (max-width: 400px);

/* Activate mini.css file if window size is less than 400px */
if(windowWidth<400) {
loadCss('mini.css');
} else {
removeCss('mini.css');
}

The above example should in theory by compatible with old web browsers (including IE6) .

Images

The last of my needs is to scale images, here are few ways how I can do it.

Fluid images

When user opens my application on a mobile device with small screen and low resolution or changes web browser window size, it might happen that my images will reach a stress point where they will consume more space that the window is able to offer. To avoid this problem I need to scale them proportionally.

Fortunately there is a CSS property called max-width, if I set to 100% in theory I get all I need. My images will by default be displayed in their original size and will be proportionally scaled when needed.

But in practice this solution is affected by few problems, the main is that I will need to load oversized images in low resolution which can be a problem for small mobile devices. The other problem is a poor quality of scaled images on IE.

Hiding images

Another possibility is to have alternative versions of an image, depending on the environment images can be visible or hidden, just like on the example below:

.small-image {
display:none;
}

@media only screen and (max-width: 600px) {
.default-image { display:none; }
.small-image { display:inline; }
}

<img class="default-image" src="img1.jpg">
<img class="small-image" src="img2.jpg">

 

It works quite well, if windows size is below 600px (this is the stress point) small-image gets attribute display:inline; and default-image gets display:none;
Nice solution, compatible with all common web browsers, however IE always loads both images even if one of them will never be showed and it is not quite clean code.

“Content” attribute

It is possible to show images with combination of content property and url value, see example below:
@media screen and (max-width: 600px) {
.image1:before {
content:url(img2.jpg);
}
}
@media screen and (min-width: 600px){
.image1:before {
content:url(img1.jpg);
}
}
<span class="image1" />


But still, this code isn’t clean and is not compatible with some common web browsers.

Java Script way

JS come here with help as well.

One of the possibilities is to set a cookie with screen size information.
document.cookie = "screenWidth=" + screen.width;
and then serve all images through media content, which will send the right image version depending on screenWidth value:
<img src="media/?test.jpg">

CSS 3 way

In the future I’m expecting below code to be supported in all CSS3 compatible web browsers, for now only Opera supports this syntax partially.

<img src="test.jpg"
img-400px="test-400px.jpg"
>
@media (min-device-width:400px) {
img[img-400px] {
content: url(attr(img-400px));
}
}