Words from Alejandro U. Alvarez
PHP
Articles about php development
PHP: Display all files/pictures in a folder
May 4th
If we want to create a quick gallery of files/pictures, it is quite a pain to do so manually. And since this is some code I'm always reusing I thought I might share it here with everyone else:
Let's suppose we are in the base directory (www.mysite.com/gallery.php) and the pictures are in a folder named pictures (www.mysite.com/pictures/), open gallery.php and where you want the pictures to appear, use this code:
$handle = opendir(dirname(realpath(__FILE__)).'/pictures/'); while($file = readdir($handle)){ if($file !== '.' && $file !== '..'){ echo '<img src="pictures/'.$file.'" border="0" />'; } }
This code is really easy to understand, we first define a variable "$handle", which will contain the handle to the absolute path of the folder (I always go for absolute paths since I find them much safer)
dirname(realpath(__FILE__)) returns the absolute path to the current file (gallery.php) and then we add to the end of it /pictures/
Then, we loop through all the files in the directory, that simple if statement I used is to prevent some the function returning . or .. as file names sometimes. We then display the image in standard html
Take care!
Alex
Display timezone-specific dates in PHP
Apr 17th
It's common to have a website designed for one country (For example Spain or the UK) and have it in a server elsewhere (In the US for example). You will notice that sometimes when displaying a date this way, it shows the local time at the server!
Instead of manually correcting this time difference there is a much safer way of getting around this issue, using built-in PHP functions:
Take a look at this:
<?php $timezone = new DateTimeZone( "Europe/Madrid" ); $date = new DateTime(); $date->setTimezone( $timezone ); echo $date->format( 'H:ia (D, M jS, Y)' ); ?>
Which would generate the following output (At the time of writing of course!)
22:52pm (Sat, Apr 17th, 2010)
As you can see it is extremely easy to set up new timezones and to display dates for those zones specifically. You could even prompt your user for his/her own timezone, or add it as a specific setting for them
Best website translation method
Mar 22nd
Ok your website has grown. You want it in different languages but let's face it, it is a challenging task. I have worked on the process of translating several websites, ground up, and it really isn't hard. It is just quite boring, but easy after all.
My preferred method is by using PHP's built in function gettext(). If you are using a shared domain most chances are it is installed. If it isn't, or you run a VPS or dedicated hosting, ask your hosting administrator to install it.
To check whether you have the getttext module installed run the following script:
// Check if gettext is ready to use if(!function_exists("gettext")){ echo "gettext isn't installed"; }else{ echo "gettext is supported!"; }
Well once you see the message gettext is supported! you can carry on
Setting up localisation:
We need php to determine the current language. There are many many ways of doing so, however my preferred method is by using subdomains. From the point of view of a Search Engine, your content is new and it has it's own URL, and from your point of view it takes almost no extra effort.
So go to your website control panel if it is shared, if not simply create a new subdomain for each language and point it to the root. I recommend you use the standard two-letter code from the ISO 639 for each subdomain. For example if my website is http://djs-music.com, I would create the new subdomain for a Spanish version: http://es.djs-music.com (These links are from the latest website I developed, in different languages, so that you can see a working example)
If the default language is English, I wouldn't create a subdomain for that, I would simply leave it as default when there is no subdomain selected.
Now let's create a new php file called local.php, it will handle all this, at the top, we will set up an array with the available languages, the default one... etc
Inside local.php place the following code, it is ready to use so you shouldn't have to change anything:
<?php /* Localisation script by Alejandro U. Alvarez * http://urbanoalvarez.es * This has no license actually... But since I am sharing this please be kind, and link back* * -------------- CONFIGURATION ----------------- * */ // Write here all possible subdomains $langs = array('es','en','no'); // Defaults $defaultlang = 'en'; // Your website with no www // Place a \ before each . $webUrl = 'djs-music\.com'; // Your charset/codeset $codeset = "UTF8"; // Translations folder. // This is where you will have folders each named with the locale (i.e. en_EN) // It is recommended you put it in the root and name it locale $localePath = './locale'; // Translations files name // This is the name of the .mo files. It is recommended you name them messages.mo // Inside the localePath there should be one folder per translation // named with the locale (i.e. en_EN), and inside there should be a .mo file // preferably called messages.mo Place the name of the file here: $translationFile = 'messages'; /* ---------- NOW COMES THE ACTUAL CODE --------- */ // intial state $lang = false; $local = false; $defaultLocale = $defaultlang.'_'.strtoupper($defaultlang); // Get from domain: $url = parse_url($_SERVER["SERVER_NAME"]); if(!eregi('^(www\.)*('.$webUrl.')$',$url['path']) ){ $sub = substr($url['path'], 0, 2); //Returns lang code (2 letter) if(in_array($sub,$langs)){ $local = strtolower($sub).'_'.strtoupper($sub); $lang = $sub; } } if(!$local){ $local = $defaultLocale; } if(!$lang){ $lang = $defaultLang; } $_SESSION['lang'] = $lang; $_SESSION['locale'] = $local; // Set locale // Windows compatibility putenv('LANG='.$local.'.'.$codeset); putenv('LANGUAGE='.$local.'.'.$codeset); bind_textdomain_codeset($translationFile, $codeset); // Set locale putenv('LC_ALL='.$local.'.'.$codeset); setlocale(LC_ALL, $local.'.'.$codeset); bindtextdomain($translationFile, $localePath); textdomain($translationFile); ?>
Everything is explained in comments, so you shouldn't have any problems setting the file up. You should include this as high as possible in your code, but after you start a session with session_start();
Of course at the moment it will not work since we are missing all the important files! So let's move on.
Setting up our website for translation:
We now need to indicate which portions of our website are going to be translated. We will do this by wrapping the desired pieces of text in the gettext function, which has an abbreviated name _():
echo _('Text to be translated');
If you need more advanced strings, like text strings that contain variables inside them consider the following example:
I have in my website a text string that reads: echo "You are $years old"; This string is special, we cannot translate You are, and then old, because in other languages (Such as Spanish it wouldn't make sense). For cases like this we will use PHP's function sprintf:
(By the way, sprintf returns the string, printf prints the string, we use sprintf because we need the string returned in order to have it translated with gettext)
This function takes at least 2 parameters, the string, and one substitution. How it works is you place "Identifiers" inside the string, that sprintf changes for the corresponding value: Take a look at the previous example with printf:
echo _(sprintf("You are %d old",$years));
Now we can have it translated with the age in the exact position in other languages
Creating the translation folders and files:
We now need the .mo files that php's gettext uses for the translations. It is very easy as you will see:
If you have already checked the configuration at the top of the local.php file, there were two variables called $localePath and $translationFile, leave these to the defaults for now.
Go to the root of your website and create a new folder called locale, inside this folder, you will have to create one new folder for each language except for the default. So if your website is already in English, and you are going to translate it to Spanish, you should create a new folder inside locale called es_ES.
Now inside each of these folders (In my example inside the folder es_ES) you will have to create another folder called LC_MESSAGES. So your folder structure for the example would look like this:
./
- locale
- es_ES
- LC_MESSAGES
- es_ES
- Other folders and files in your root...
Now that the folder structure is set up, we need to get a gettext translation program. Download and install Poedit (Note that it has Windows, Mac and Linux versions, and that it's free)
Once in Poedit, go to File -> Catalogs manager. A pop-up window will appear, in it click the blank page icon to create a New Translations Project. This is useful to have all your Catalogs for one website under the same project.
When you click on New Translation project, a new window appears, enter the name you desire, and in the directories, click on New Item, and once there enter the exact path to your project root (i.e. C:\Username\projects\DJs-Music ) And click Ok.
Now close that and go to File -> New Catalog, a new window will pop up, this is a very important window:
Start in the tab Project Info (Default tab)
- Project name and version: This is just to help track the translation. You can enter for now Your Website 1.0
- Team: Only useful for translations with multiple teams. For example Spanish 1
- Email address: Same as before... spanish@yourwebsite.com
- Language: Select the language you are going to translate now (For example Spanish)
- Charset: IMPORTANT Make sure it matches the charset in your website (Preferably UTF-8)
- Source code charset: Your code charset (It's in the configuration area, preferably UTF-8)
- Plural forms: Leave it blank for now, that is quite advanced for now)
Now click on the tab Paths: Here you will have to add the paths inside your project where the php files containing strings you want to translate are. Click on the New Item icon to add one. If you want to add your root folder for example type .
If you want all files inside a folder called /more-files/, enter more-files\.
Forget the tab keywords for now, it is also for more advanced translations.
Click Ok, and now click on the Earth icon, in the left column (Original string) you should see all the text strings you enclosed between the function _(), if you don't see them go back and check all the previous steps.
Now go on and translate all the strings. To translate simply click on a string on the left column and enter the translation on the box at the bottom of the screen. At the bottom left you will see the translated percentage.
When you are done go to File -> Save as, and save it as messages.po inside the LC_MESSAGES folder for the corresponding language locale (So if this was the Spanish translation you would save this file inside locale/es_ES/LC_MESSAGES/)
As well as the messages.po file, you will see if you go to that folder another file called messages.mo, that is the one that you have to upload to your server in order for this to work.
Check everything:
Alright so let me walk you through all the steps you should have taken:
- Create the file local.php and include it in every file you want to translate
- Create the subdomains and point them to the root
- Configure all the variables in local.php
- Wrap all the text you want to translate in _('Your text here')
- Create the folder structure and the messages.mo files using Poedit
- Upload everything to the server
If you did all of this, everything should work fine. You can see a working example of this in DJs Music for example: English version, Spanish version
How did this go? Tell me in the comments and ask me any doubt
Parse links in user comments
Sep 13th
When you allow users to comment and post stuff to your website, it is interesting and useful allowing them to post links and other stuff. But how can we do so easily?
Surely there is BBCode, phpBB, allowing only some HTML tags... etc but how easy is this approach for the end user? Of course some users will be familiar with BB code, or with HTML; others will be curious enough to learn how to use it, but most won't. And we want our users to be able to do so.
The solution: URL Parsing
How about this: They simply post the URL of whatever they want to include (A link, a picture, a YouTube video... ) and we detect that, and take the corresponding action.
First of all we need something that detects links, I have written a simple regexp to do so:
function parse($text){ return preg_replace_callback('@(https?://([-\w\.]+)+(:\d+)?(/([\w/_\.-]*(\?\S+)?)?)?)@', 'parseUrl', $text); }
This is valid for almost all URLs, as long as http is the beggining. This function calls a callback function whenever a URL is found, called parseURL, which will then take care of the URL.
parseURL
Now that the URL is found, we need to take care of it: The url is stored in a parameter returned from the function preg_replace_callback. It is contained in the first element of the returned array.
function parseURL($url){ $link = $url[0]; }
We will parse the full url with a built-in function called parse_url(), which will return the following data:
- scheme - e.g. http
- host
- port
- user
- pass
- path
- query - after the question mark ?
- fragment - after the hashmark #
To get the file format we will check the extension:
$ext = substr(strrchr($url['path']),'.'),1);
Image formats:
$imgs = array('jpg','jpeg','gif','png','tif'); // You can write more if you want, this is only an example
Now let's check if it is or not an image:
if(in_array($ext,$imgs)){ return '<img src="'.$link.'" alt="This is a picture" class="insertedPic" />'; }
This way if a user inserts a link to a picture, the picture is displayed. You can now add a link, or change in any way the result of this.
If it is a YouTube video it would also be good to embed it, so we will first check if it is:
if(eregi('^(www\.)*(youtube\.).{2,3}$',$url['host'])){ // Check for youtube video return youtubeEmbed($url['query']); }
As you can see, if the link comes from youtube, we will embed it using our custom function youtubeEmbed:
youtubeEmbed()
function youtubeEmbed($params){ parse_str($params); if(substr($v , strlen($v)-3 ,3) == '<br '){ $v = substr($v , 0 ,strlen($v)-3); } if($v){ return ' <div class="addedLink"/><object width="200px" height="150px" style="display:block; z-index:1"><param name="movie" value="http://www.youtube.com/v/'.$v.'"></param><param name="allowFullScreen" value="true"></param><param name="wmode" value="transparent"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/'.$v.'&rel=1&color1=0xFFFFFF&color2=0x666666&border=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="200" height="150" wmode="transparent"></embed></object><br '; }else{ return false; } } </pre/> I won't go into too much detail here, it is quite simple, we take the parameter $v, which is the video ID, and then we proceed to the video embed... You can do the same with Metacafe, Vimeo, College Humor, Google Video... etc and the process would be basically the same for all. <h2>Further uses</h2> I use this class to detect internal links in some of my websites. If the link points to a page with a picture for example, I show a small version of it, if it points to a user profile I show the user's name and some data... etc The options are endless, and once you have everything parsed it is very easy to add new stuff. It really makes it simple for users to share pictures and videos, and it is the safest way of doing so, as well as the best way if you ever want to change the behavior, since in the database all you store is the raw URL.
Calculate age in PHP from timestamp
Sep 8th
If you ever wanted to calculate someone's age in PHP from a birth timestamp, you must take into account that the age is more than the number of years, since days and months are also important, so I wrote a simple function that will return the exact age for a given timestamp:
function getAge($birth){ $t = time(); $age = ($birth < 0) ? ( $t + ($birth * -1) ) : $t - $birth; return floor($age/31536000); }
Basically we first get the current time and store it in a variable (To avoid having to call the function time more than once)
Then we get the age in milliseconds (Taking into account that before 1969 timestamps are negative, thus the ternary operator)
Now we have the date in milliseconds, we divide it by the number of milliseconds in a year (60*60*24*365)
And that is basically it
*
* -------------- CONFIGURATION ----------------- *
*/