Вы находитесь на странице: 1из 33

Published by S&S Media Group

November 2013
Issue 20

ALSO IN THIS ISSUE


Compile Your Style: Smarter CSS by Ragnar Kurm Clean up WordPress shortcodes by Jason Lengstorf Talks and the power of human interaction by Stefan Priebsch Column databases overview by Cory Isaacson Open source spotlight: RainLoop

Searching high and low


Stretching your data Equality in PHPland
Why == and === equal trouble Complex queries made easy with Elasticsearch

Through the looking glass

www.webandphp.com

Create your own picture processor in PHP

iStockphoto.com/ferrantraite

Letter from the Editor Contents

Needle in a haystack
Lets get straight into it opening this issue, we have three incredible tutorials for the less experienced dev. First, an introduction to the open-source search engine Elasticsearch, which powers the public search on both GitHub and SoundCloud, written by the primary developer of its PHP driver, Zachary Tong. Next, a quick tip from Jason Lengstorf for those of you working with shortcodes in everyones favorite blog/CMS/kitchen sink WordPress; followed by a crashcourse in PHPs built-in image lters from Robin Nixon. All three are ltering in different ways, dont you think? Alright, wordplay aside, stripping away the unnecessary cruft and drilling down to what really matters is an important philosophy for developers just take a look at the whole lean startup movement. In the words of Sir Jony Ive: True simplicity is derived from so much more than just the absences of clutter or ornamentation. Its about bringing order to complexity. Sometimes reducing that complexity paradoxically requires increasing it. Take Ragnar Kurms MLSBS workow, for example. To those working with a 200-line CSS le, learning to use a preprocessor and splitting it into many small pieces might seem like madness, not to mention the additional overhead of setting up these workows and automation scripts. But when youre up to your ears in inconsistent selectors and patching an already-bloated stylesheet with !important hacks, suddenly a clean and modular approach makes a whole lot of sense. Kurm has taken a programmers approach to writing stylesheets, automating as much as possible and taming CSSs nastier properties, which he explores in a two-part series beginning on page 17. The rst part starts with writing smarter CSS with SASS essential reading for anyone doing even a small amount of work with CSS. I wont spoil it for you, but part two (coming soon) gets even cooler. And then, there are some issues that on the surface look simple, but in reality must be studied in detail to truly understand. PHPs equality operator (== to its friends) seems simple enough; but its type conversion can result in some very odd outcomes that, to make sense of, require digging into the Zend engines source code. Luckily Sharon Levy has already done the hard work: you can read her solution on page 23. Enjoy the issue! Elliot Bentley, Editor

Contents
Community Corner

This months top events  Open source spotlight: RainLoop


Interview with Usenko Timur
Tutorial

3 4

Stretching your data with Elasticsearch 


Zachary Tong
Tutorial

Quick tip: Remove crappy markup from WordPress shortcodes 


Jason Lengstorf
Tutorial

Create your own picture processor in PHP 


Robin Nixon
Tutorial

11

Compile Your Style: Smarter CSS 


Ragnar Kurm
Feature

17

Equality in PHPland 
Sharon Lee Levy
Column

23

Column databases overview 


Cory Isaacson
Column

28

Talks and the power of human interaction 


Stefan Priebsch

32

www.webandphp.com

Web&PHP Magazine 11.13 | 2

Calendar

Community Corner

THIS MONTHS TOp EVENTS


A handpicked selection of some of the most // November 530 exciting PHP events from around the world

November 7 // Orlando, FL, USA // Free

November 16 // Madison, WI, USA // $ 100

The Orlando PHP Meetup Group Monthly Meetup


http://www.meetup.com/OrlandoPHP/ events/137057312/ This months meetup is a Coding Dojo, where participants take it in turn to code in front of a live audience. Sounds terrifying. November 89 // Maastricht, Netherlands // 110

Madison PHP Conference


http://2013.madisonphpconference.com/ Organised by the local usergroup, this conference includes a Foundations Track for beginners in addition to a Professional Track. November 2122 // Paris, France // 250

November 26 // Brisbane, Australia // Free

Brisbane Web Tech Nov 2013


http://www.meetup.com/brisbane- webtech/events/126487932/ This months speaker is Darren Mackey on multivariate statistical theory and its applications to big data.

Forum PHP Paris 2013


http://afup.org/pages/forumphp2013/ The City of Light hosts a two-day event in both French and English, including a talk from PHP 5.5 release manager Julien Pauli.

CodeConnexx
http://codeconnexx.com/ Hosted by PHP Women, this not all technical conference includes keynotes from Lorna Mitchell and Ross Tuck.

To suggest an event for our calendar, email elliotb@webandphp.com.

www.webandphp.com

Web&PHP Magazine 11.13 | 3

OSS Spotlight

Community Corner

Open source spotlight: RainLoop


Meeting the people behind the most exciting homegrown OSS projects. This month, we speak to Usenko Timur about RainLoop, a web-based email client built in PHP.

Web & PHP Magazine: Whos behind RainLoop? Usenko Timur: An ordinary developer from Russia. Also, there are several people who helped me, and that help is really appreciated. WPM: Why did you decide to start it? And why did you make it open source? Timur: Thats quite a long story. Initially, I have created MailSo, PHP library for handling mails. Its main idea was to make sure that size of messages does not affect amount of memory used by PHP. Then I thought I should develop some other tool so that development of the library goes in right direction. Thats when I started making webmail client based on MailSo library. There were like 7 attempts to create an application which would satisfy me in terms of code quality and design. The 8th attempt, which involved KnockoutJs library released around that time, has provided the result I was looking for. Thats how RainLoop appeared, and eventually it became much more complex than MailSo.

As for the second question, RainLoop isnt exactly open source. Its free, yes, but I only provide the package Ive built. PHP source there can be freely reviewed and modied, but JS les are minied to speed up loading, and the CSS is a result of processing less-les. WPM: Whats the appeal of hosting your own email client? Timur: I guess, this is where everyone has their own reasons. Some people are happy with GMail while other look for better control over their mails so they install their own mail servers and use webmail clients they nd appropriate. WPM: How can people get involved in the project, and what can they do to help out? Timur: Currently, what I need the most is translations and issue descriptions used in logs. Im trying to examine, process and implement those as soon as possible. Later, a plugin system will be added and documented, and anyone will be able to extend product functionality to ones liking. And of course, Id like to know what kind of features people want to see in

RainLoop though with any new features added, RainLoop must stay as a simple email application, easy for understanding and conguring. WPM: Where do you hope to see the project in a years time? Timur: This all depends on project users. Their feedback will help me perfect the current functionality. Im staying realistic as to what I can achieve, but what can be improved will be improved.

Homepage: http://rainloop.net/ GitHub: https://github.com/RainLoop/rainloopwebmail

www.webandphp.com

Web&PHP Magazine 11.13 | 4

Elasticsearch Tutorial

Put a bounce in your step

Stretching your data with Elasticsearch


Complex queries and fuzzy matches are easy with open source search and analytics tool Elasticsearch. Zachary Tong shows how to get to grips with the PHP driver.

by Zachary Tong

Image licensed by Ingram Image

Regardless of your domain, you have data. Perhaps it is e-commerce data and product listings. Maybe you have a million clickstream entries from advertising. Or perhaps you have billions of log records that you need to analyze. Whatever it is, you have data. And you need to access that data somehow, to interrogate it and reveal trends, patterns and insights that benet your business. Storing data is almost never a problem ... but how do you query it? Consider the three following questions: Structured: Which employees are over 30? Unstructured: Which employees have names starting with Zac? Analytics: How many employees does each department have?

Traditional databases can answer the rst question without a problem. Dates, numbers, exact values, ranges structured search is simple for a database. Unstructured search, however, is nearly impossible with most databases. Their data structures simply do not allow them to answer unstructured, full text questions with any kind of performance. Likewise, analytics and aggregations are also very difcult. Realtime aggregations are even harder, and as data grows, you often resort to batch aggregations that run on a schedule. Finally, what happens if you want to ask the following question: Show me the number of employees in each department who are over 30 and have names starting with Zac? That will be painful in a database no matter how well cleverly you organize your data and tweak your query. Unfortunately, these types of queries are often the most valuable to your business.

www.webandphp.com

Web&PHP Magazine 11.13 | 5

Elasticsearch Tutorial

Introducing Elasticsearch Elasticsearch was born as a full text search engine, but it has grown to answer all four scenarios presented above. Elasticsearch can handle structured search just as well (and just as fast!) as unstructured search and real-time analytics. At a high-level, Elasticsearch is a distributed document store where every eld is indexed and searchable in near-real-time. It speaks HTTP over a RESTful API, which means it is dead simple to interact with. It runs on one laptop just as well as on a 100 node cluster indexing literally terabytes of data. It may seem too good to be true, but it's simply the result of Elasticsearchs underlying data structure. While most databases rely on structures like B-Trees, Elasticsearch uses an inverted index [1]. The nature of inverted indices means that all elds are indexed, and lookups are fast regardless of how much data you have. But more important than technical details, Elasticsearch is easy to get started with. You can get a cluster running in under ve minutes. Your rst cluster The best way to get familiar with Elasticsearch is to try it yourself. Lets download Elasticsearch [2], install it and start a node (Listing1). The only requirement is that Java must be installed on your system. Once you start the node, youll see some startup text print to the terminal. Youll notice that Elasticsearch returned JSON output as the body of the HTTP response. Elasticsearch speaks JSON as its language of choice. Everything is JSON, including documents, requests and responses. This makes it very easy to interact with Elasticsearch from within PHP; JSON can easily be encoded/decoded into more useful associative arrays.

Indexing Documents with PHP While we can interact with the node using curl (and youll notice that the documentation online is entirely in curl + JSON requests), we ultimately want to use PHP to query Elasticsearch from within our application. Elasticsearch maintains an ofcial PHP client which we will use for the rest of the article. The PHP client is installed with Composer (see installation directions here [3]). Elasticsearch is a document-based system. Each document is simply a JSON object that contain the elds and values that you wish to store. The PHP client transparently converts associative arrays to JSON and back, so you never actually have to deal with raw JSON. In the rst part of Listing2, we create a document that has some data. Youll notice that $document

contains a number of elds with different data types: strings, integers, arrays, and even objects. Elasticsearch will accept all of these. Next we specify an index with a type and an id. Indices and types are basically logical namespaces to organize data. An Elasticsearch cluster can contain multiple indices, and a single index can contain multiple types. You can search across multiple indices and multiple types, at the same time, with no loss in performance. See this article [4] for more details about the nature of an index in Elasticsearch. Lastly, youll notice that when we index the data we didnt need to dene a schema rst. Elasticsearch will auto detect the schema from your rst document, which means you can prototype systems quickly and easily.

Listing 1
$ curl -L -O https://download.elasticsearch.org/elasticsearch/elasticsearch/ elasticsearch-0.90.5.tar.gz $ tar -xzf elasticsearch-*.tar.gz $ cd elasticsearch-* $ ./bin/elasticsearch -f $ curl 'http://localhost:9200/?pretty' { "tagline" : "You Know, for Search", "ok" : true, "status" : 200, "name" : "Contrary", "version" : { "number" : "0.90.5", "snapshot_build" : false } }

Listing 2
$client = new Elasticsearch\Client(); $document = array( 'name' => 'John Smith', 'age' => 26, 'hobbies' => array('biking', 'surng'), 'employer' => array( 'name' => 'MegaCorp', 'size' => 49293 ) ); $params['body'] = $document; $params['index'] = 'company'; $params['type'] = 'employees'; $params['id'] = 'JohnSmith'; $ret = $client->index($params); */

www.webandphp.com

Web&PHP Magazine 11.13 | 6

Elasticsearch Tutorial

Listing 3
$params = array(); $params['index'] = 'company'; $params['type'] = 'employees'; $params['body']['query']['match']['name'] = 'John'; $results = $client->search($params); print_r($results['hits']['hits']); > Array ( [0] => Array ( [_index] => company [_type] => employees [_id] => JohnSmith [_score] => 0.19178301 [_source] => Array ( [name] => John Smith [age] => 26 [hobbies] => Array ( [0] => biking [1] => surng ) [employer] => Array ( [name] => MegaCorp [size] => 49293 ) ) ) )

Search Ok, so we've indexed a document. Lets do some searching! In Listing3, we are going to perform a simple search for John. Elasticsearch has a number of different queries [5] that you can choose from, but in this example we are using the Match Query [6]. The Match Query is a good default choice, offering a great search box experience with little conguration. In Listing3 we are searching the company index and employees type (which makes sense since that is where the document was indexed). The Match Query is dened in the body of the search request. It will search the name eld of every document for our query text. The search results come back as a big associative array. The list of matching documents (ranked by relevance score) is printed at the end of Listing3. You can see that our original document was matched, and the search result contains the entirety of the original document (plus some extra meta-data). Fuzzy Searching That example was kind of boring. Lets try something that a database would struggle with: dealing with typos. There are many ways to deal with typos in Elasticsearch depending on your requirements, but we are going to use the Match Query again. Youll soon learn that the Match Query is basically the swiss-army knife of Elasticsearch. The Match query can be congured to tolerate a certain amount of fuzziness when analyzing documents. This operation allows a number of edits to be made to the query. For example, if we replace the o in John with an a, that counts as a single edit. The default fuzziness of 0.5 will allow one edit, and thus, Listing4s query will match the document even though Jahn is not stored in any document.

This may seem expensive computationally: how can you check typos without comparing every single value in the index? Luckily, Elasticsearch uses some very complicated Finite State Transducers that ensure that the operation is performant and does not, in fact, do a simple table scan.

Filtering Data The queries we have looked at so far all calculate relevance scores. Some results are more relevant than other results, which is reected with a higher score. In many queries, there are certain elements that dont need relevance scoring. For example, a number is either inside a range or it is not. There is no concept of being more in the range it is just a yes/no answer. In these situations, you should use a lter [9]. Filters perform the role of structured search in Elasticsearch, and are very efcient since they can skip the entire scoring phase. Furthermore, Elasticsearch aggressively caches lter bitset [10] so that subsequent ltering is even faster. Filtering documents is

Listing 4
$params = array(); $params['index'] = 'company'; $params['type'] = 'employees'; $params['body']['query']['match']['name'] = array( 'query' => 'Jahn', 'fuzziness' => 0.5, ); $results = $client->search($params); print_r($results['hits']['hits'][0]['_source']['name']); > John Smith

www.webandphp.com

Web&PHP Magazine 11.13 | 7

Elasticsearch Tutorial

simply a bitwise AND across multiple lters an operation that is extremely fast. In Listing5, we use a Filtered Query [11]. This is a special compound query that accepts a query and a lter. The query section is only executed on documents that match the lter, greatly reducing the number of computations required. The query section is similar to what weve seen before: a Match Query looking at name. The lter section is using a Range lter [12] and matching all documents where the age eld is greater than 20.

more, it allows your application to build components of the query in separate locations and then stitch them all together in one location.

References
[1] http://en.wikipedia.org/wiki/Inverted_index [2] https://gist.github.com/polyfractal/elasticsearch.org/download/ [3] http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/ current/_installation_2.html [4] http://www.elasticsearch.org/blog/what-is-an-elasticsearch-index/ [5] http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/ query-dsl-queries.html [6] http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/ query-dsl-match-query.html [7] http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/ query-dsl-queries.html [8] http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/ query-dsl-match-query.html [9] http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/ query-dsl-lters.html [10] http://www.elasticsearch.org/blog/all-about-elasticsearch-lter-bitsets/ [11] http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/ query-dsl-ltered-query.html [12] http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/ query-dsl-range-lter.html

Composability You might have noticed in the last query that we reused a Match Query inside of the Filtered Query. The Elasticsearch query DSL is entirely composable. Queries and Filters are independent components that can be nested inside of other compound queries/lters. This makes the query DSL very powerful. You can easily tailor queries to your exact specication. Further-

Listing 5
$params = array(); $params['index'] = 'company'; $params['type'] = 'employees'; $query['match']['name'] = 'Smith'; $lter['range']['age']['gt'] = 20; $params['body']['query']['ltered'] = array( 'query' => $query, 'lter' => $lter ); $results = $client->search($params); print_r($results['hits']['hits'][0]['_source']['name']); > John Smith

Conclusion Elasticsearch offers much, much more than what we discussed in this article. Weve only scratched the surface, but hopefully it has given you a taste for the power of Elasticsearch. Luckily, Elasticsearch has been built with excellent defaults. In many cases, you can use defaults and simple queries until the point in time that you need more power. Then you can investigate a few new features and add them to your repertoire. In this way, your query sophistication grows as you become more accustomed to the query DSL. But the system is designed to be easy from day one, which means you can begin prototyping right away, even if you only have a minimal understanding of how Elasticsearch works. Elasticsearch is a system where the more you use it, the more uses you nd. I hope this article piqued your interest and you explore Elasticsearch some more in the future!

Zachary Tong is a developer at Elasticsearch and author of the ofcial PHP client. Hes been writing articles about Elasticsearch for nearly two years, and has released several plugins to help newcomers understand what Elasticsearch is doing under the hood.

www.webandphp.com

Web&PHP Magazine 11.13 | 8

WordPres Tutorial

Image licensed by Ingram Image

Clearing the books

Quick tip: Remove crappy markup from WordPress shortcodes


Clear up the messy HTML output produced by WordPress shortcodes with Jason Lengstorfs handy function.
by Jason Lengstorf

Shortcodes in WordPress [1] are a handy way to insert wrappers into our content for styling content without requiring our clients to learn HTML. Unfortunately, WordPress has a few quirks when rendering shortcodes that can introduce bugs in your layout.

When this shortcode is processed, it adds a <div> with the class highlight:
function my_shortcode_highlight( $attr, $content ) { return '<div class="highlight">' . $content . '</div>'; } add_shortcode('highlight', 'my_shortcode_highlight');

The Problem The easiest way to explain the issue is with an example. Lets say we want to add a wrapper around a block of text to highlight it. A logical solution is to add a shortcode called [highlight], like so:
[highlight] Content goes here... [/highlight]

The built-in WordPress wpautop lter will add junk markup to your output:
<div class"highlight"> </p> <p>Content goes here...</p> <p> </div>

These extra <p> tags can result in extra margin and padding, and its just generally ugly and undesirable.

www.webandphp.com

Web&PHP Magazine 11.13 | 9

WordPres Tutorial

The Fix Fortunately, the x is pretty painless. All we have to do is add a quick ltering function to functions.php and call that lter in our shortcode function. Step 1: The Filtering Function: In functions.php, add a new function (Listing1). What were doing here is setting up two regular expressions [2] (regex for short) in $patterns as an array. The rst regex, #^\s*</p>#, starts at the beginning of the string (^), allows for zero or more whitespace characters (\s), then matches a closing paragraph tag (</p>). This gets rid of the mismatched closing paragraph tag that shows up before the shortcodes content. The second regex pattern, #<p>\s*$#, looks for an opening paragraph tag (<p>) and zero or more whitespace characters (\s) at the end of the string ($). This prevents the shortcode markup from having an empty paragraph at the bottom, which is typically the culprit when formatting issues arise.

Both patterns, when present, are replaced with an empty string thus removing them and the modied markup is returned from the function. If the one or both of the patterns are not found, no changes are made and the markup is returned as usual. Step 2: Calling the Filtering Function: With the ltering function added, the next step is to include it in any shortcodes that require ltering. As a general guideline, any shortcode that wraps output with a tag (such as a <div>) will need to be ltered. All we need to do to lter the output is run $content through the copter_remove_crappy_markup() function and use its return value stored in $clean instead:
function my_shortcode_highlight( $attr, $content ) { $clean = copter_remove_crappy_markup($content); return '<div class="highlight">' . $clean . '</div>'; } add_shortcode('highlight', 'my_shortcode_highlight');

References
[1] The WordPress Shortcode API: http://codex.wordpress.org/Shortcode_API [2] More info about regular expressions: http://www.regular-expressions.info/ [3] Star this code on Gist: https://gist.github.com/jlengstorf/5370457 [4] Copter Labs: http://bit.ly/1g5bmgN [5] Realtime Web Apps (book): http://amzn.to/15RKxF3

Listing 1
<?php /** * Removes mismatched </p> and <p> tags from a string * * @author Jason Lengstorf <jason@copterlabs.com> */ function copter_remove_crappy_markup( $string ) { $patterns = array( '#^\s*</p>#', '#<p>\s*$#' ); return preg_replace($patterns, '', $string); }

With the lter applied, our previously janky shortcode output becomes a perfectly valid bit of HTML:
<div class="my_shortcode"><p>Content goes here...</p></div>

TL;DR WordPress outputs crappy markup when using shortcodes to add wrapper elements. We can correct the markup by adding a quick ltering function to our shortcode declarations. Ps: If you like this tip, why not star it on Gist [3]?
Jason Lengstorf has spent the last decade or so solving problems on the Internet. He runs Copter Labs [4] and writes about geeky things (such as his most recent book, Realtime Web Apps [5]).

www.webandphp.com

Web&PHP Magazine 11.13 | 10

Images in PHP Tutorial

Who needs Photoshop?

Create your own picture processor in PHP


Using just a web server and the PHP scripting language, its easy to build a simple image editor with many of the features of top photo editing packages.

Figure 1: PHP is much more than a simple scripting language its also an amazingly powerful image processor

by Robin Nixon

Manipulating images is a tool that more and more web programmers are having to add to their repertoire due to the growth of Web 2.0 and the requirement to make sites more social with thumbnails, avatars and so on. Websites for freelancers, for example, often need to support image uploads of photos of the freelancers themselves as well as of their work. Likewise auction, classieds and other sites that sell products on behalf of the surfer need a way to accept an upload of the products for sale. But its not sufcient to simply support http le upload, because images will be posted to you of all sizes and dimensions. Many of the latest digital cameras offer in excess of ten megapixels and the de-

fault le sizes are very high resolution (thousands by thousands of pixels) and take up several megabytes of space. Therefore you need to be able to take uploaded images and resize them to suitable dimensions for the website in question, and also save them in the le format of your choice. Along the way you may well need to either offer your users the opportunity to adjust the nal image, or automatically do so yourself. This is particularly necessary when you have resized an image down substantially to a thumbnail, as you will almost certainly have to sharpen it. Before you start, though, you must ensure that your web (or development) server has the GD Lib [1] compiled into PHP as that is the only way these functions will work.

Figure 2: With the GD Lib graphic library compiled in, PHP provides a wide selection of image transformations

www.webandphp.com

Web&PHP Magazine 11.13 | 11

Images in PHP Tutorial

Uploading images For the purposes of this article I have limited the supported le types to GIF, PNG and JPG. In practice you will probably nd these three are sufcient for most purposes anyway. The following code snippet is the rst part of the complete program le which should be saved as picproc.php [2]. It contains the opening code to tell the web server to process PHP, and then the main html used by the program is output (Listing1). Working through this code sequentially, what it does is start off by setting the font face to a suitable sized monospaced one to aid with simple formatting. Then the <pre> and <ul> tags are issued to force all following output to appear exactly as shown and indented from the left margin. Then a heading is printed. After this single-line page set up we get to the nitty gritty code where a form is started which will send its input as multipart data to picproc.php (the le itself).

Next the inputs are dened which include the le to be sent (a browse button will automatically appear when the program is run), and the action to perform. This includes 14 items, whose actions you can tell from the code. Finally an option to also resize the uploaded le is given by offering the input of two elds, W and H for width and height. Then, once it's all done, the submit button is added and the form is closed.

Listing 1
<?php echo <<<_END <font face='Courier New' size='4'><pre> <ul> <b><u>PHP PICTURE PROCESSOR</u> <form method='post' action='picproc.php' enctype='multipart/form-data'> UPLOAD : <input type='le' name='p' size='1'> ACTION : <select name='a'> <option value=0>No Action <option value=1>Sharpen <option value=2>Blur <option value=3>Brighten <option value=4>Darken <option value=5>More Contrast <option value=6>Less Contrast <option value=7>Greyscale <option value=8>Invert <option value=9>Reder <option value=10>Greener <option value=11>Bluer <option value=12>Edge Detect <option value=13>Emboss <option value=14>Sketchify </select> RESIZE : W <input type='text' name='w' size='1'> H <input type='text' name='h' size='1'> <input type='submit'></form></pre> </ul> _END;

Saving the uploaded image The above code displays the html form required to upload an image, perform one of 14 actions on it and/or resize the image. So you have a few things to do once the image arrives at the server, the rst of which is to populate the variables you will be using:
$p = $_FILES['p']['name']; $a = $_POST['a']; $w = $_POST['w'];

Figure 3: Using sophisticated resampling techniques PHP can redimension an image while keeping most of its integrity intact

Figure 4: Whether you simply need to set an image to greyscale, or need more powerful tools, PHP has most of the graphics functions you need

www.webandphp.com

Web&PHP Magazine 11.13 | 12

Images in PHP Tutorial

Listing 2
if ($p) { move_uploaded_le($_FILES['p']['tmp_name'], $t); switch($_FILES['p']['type']) { case "image/gif": $s = imagecreatefromgif($t); break; case "image/jpeg": $s = imagecreatefromjpeg($t); break; case "image/png": $s = imagecreatefrompng($t); break; } @imagejpeg($s, $t); } else $s = @imagecreatefromjpeg($t);

$h = $_POST['h']; $t = "pic.jpg"; $rn = rand(0, 65535);

places to ensure the program output is clean. If you intend other people to use any of this code you should rst remove all the @ signs in order to catch error messages and decide how to handle them.

Listing 3
switch($a) { case 1: @imageconvolution($s, array(array(-1, -1, -1), array(-1, 16, -1), array(-1, -1, -1)), 8, 0); case 2: @imagelter($s, IMG_FILTER_GAUSSIAN_BLUR); case 3: @imagelter($s, IMG_FILTER_BRIGHTNESS, 20); case 4: @imagelter($s, IMG_FILTER_BRIGHTNESS, -20); case 5: @imagelter($s, IMG_FILTER_CONTRAST, -20); case 6: @imagelter($s, IMG_FILTER_CONTRAST, 20); case 7: @imagelter($s, IMG_FILTER_GRAYSCALE); case 8: @imagelter($s, IMG_FILTER_NEGATE); case 9: @imagelter($s, IMG_FILTER_COLORIZE, 128, 0, 0, 50); case 10: @imagelter($s, IMG_FILTER_COLORIZE, 0, 128, 0, 50); case 11: @imagelter($s, IMG_FILTER_COLORIZE, 0, 0, 128, 50); case 12: @imagelter($s, IMG_FILTER_EDGEDETECT); case 13: @imagelter($s, IMG_FILTER_EMBOSS); case 14: @imagelter($s, IMG_FILTER_MEAN_REMOVAL); }

break; break; break; break; break; break; break; break; break; break; break; break; break; break;

If a le has been uploaded, $p will be the les contents. $a will be the action to perform on the le or No action if it is set to 0. If set, $w and $h contain the new width and height to apply to the image. $t is a simple string variable containing the name of the le as saved to the server, while $rn is a random number whose use you will see later. But the variable that concerns you immediately is $p because, if it is set, an image has been uploaded and you must deal with it as in Listing2. The rst thing to do after detecting that an image was uploaded is to save it as a le on the server using the value stored in $t (pic.jpg). The reason the .jpg extension is used here is because that is the format the image will be saved as for our internal use. To ensure this, the next few lines of code determine the type of image that was actually uploaded and then convert from that format to the PHP internal image format, before then re-saving the image as a JPG le. Note that there is no error checking here for any other mime types being uploaded. That is beyond the scope of this article and something you will need to take into account. Anyway, on exit from this routine there is an else statement which will run if no image was uploaded. If this is the case, an attempt is made to load in a previously saved image for further manipulation. This is because you dont want to keep re-uploading the main image to try different effects unless you wish to revert to the original one. Note the use of the @ sign prexed to the function call. This suppresses any error messages if no image has been uploaded yet. Because there is no room in this tutorial to build in all the possible error checking you might need, this technique has been used in many

Processing the image At this point you should now have an original image stored on the server as pic.jpg, and loaded into PHPs image workspace, ready to manipulate with the code in Listing3. As you can see, many of the actions supported by our program are implemented using the imagelter() function. This is built into the GD library used by PHP and offers a wide range of transforms, of which this article only covers a few examples. The function takes the image to be manipulated, and an integer lter type and parameters which are sometimes optional and sometimes mandatory, depending on the lter type. As a basic start point I have chosen values for these actions that seem reasonable and which can be applied several times to achieve further changes. You may wish to experiment with the values to obtain results more to your liking. Further details are in the box-out entitled Using imagelter() and imageconvolution(). The one exception is the use of the imageconvolution() function to obtain a sharpening effect. This has been used as there is no sharpen lter type available for imagelter(). There are many other effects you can achieve with this function so it is also worth investigating further. Once execution of this code segment completes you should have an image in memory ready to save back to the server. But rst, theres a nal test to make because the user may have chosen to resize the image. Image resizing Simply dropping or adding vertical or horizontal lines to change an images dimensions is very messy to

www.webandphp.com

Web&PHP Magazine 11.13 | 13

Images in PHP Tutorial

Listing 4
if ($w) { list($tw, $th) = getimagesize($t); $s1 = imagecreatetruecolor($w, $h); imagecopyresampled($s1, $s, 0, 0, 0, 0, $w, $h, $tw, $th); imagejpeg($s1, $t); imagedestroy($s1); } else @imagejpeg($s, $t); @imagedestroy($s);

First the temporary workspace used for the image is returned to the server, then if the le pic.jpg exists on the server it is displayed using the html <img> tag. And now you can see the purpose of the $rn random variable that was dened earlier. It is attached to the image URL in the form pic.jpg?rn=123 to ensure that the web browser downloads and displays the image directly from the server each time the program is run, rather than from its cache which is what it would otherwise do. And if you string those segments together in sequence and save them as the program picproc.php, you will have a basic image editor in just a few lines of PHP. Using the programs structure, you should easily be able to add plenty of additional functionality and new features.

automatically resize an uploaded image to no more than either 100 pixels wide or high, whichever is the larger (or any amount you choose), and then sharpen the result (because the resampling usually has a blurring effect on thumbnails). To get you started, the piece of code in Listing5 will do just that for you:

Listing 5
$max = 100; $tw = $w; $th = $h; if ($w > $h && $max < $w) { $th = $max / $w * $h; $tw = $max; } elseif ($h > $w && $max < $h) { $tw = $max / $h * $w; $th = $max; } elseif ($max < $w) $tw = $th = $max;

say the least. To best resize an image it needs to be resam pled so that, whether you are adding or removing data, it retains as much of its integrity as possible, as in Listing4. To do this, the images current dimensions are looked up and stored in $tw and $th. Then a new workspace is created in $s1 with the width and height specied in the input. Given that data, the original image is then resampled to ll the new workspace and saved back to the server using the imagejpeg() function. To return temporary memory back to the server imagedestroy() is also called. Youll notice that this code is only executed if $w has been dened. Again, this is a quick and dirty way to assume that a redimensioning is in order. No check is made to see whether $w is sensible, and whether $h even exists, so garbage inputs will return garbage results without suitable error checking being added. Finally, you are ready to display the image if it exists:
@imagedestroy($s); if (le_exists($t)) echo "<img src=$t?rn=$rn>";

Roll your own graphics features


There are many ways you can improve this program. Obviously, the rst of which has been covered and that is to provide adequate error checking. When receiving uploads to a public web server its essential that you remove any possibility of being hacked and for users to create strange and unwanted results. Features you could add include enlarging only parts of an image. For example you could use an image map of the picture as part of the input, and then the area surrounding the mouse click would be enlarged to ll the whole image. This is a technique often used to help users select thumbnails from larger images. Or you could just take parts of the project, many of which are handy in their own right. For example you could write a function to

Just set $max to your preferred value, and ensure $w and $h are the current width and height of your image. This code will then set $tw and $th to the correct proportional values required to ensure that, whether the image is portrait, landscape or square, the resulting thumbnail will be no greater than $max pixels in its largest dimension. Just insert this code into the appropriate place in the program le. Or at the very least, if youve never used it before, you could just take the le upload section and make use of it when CVs, databases or other les need to be uploaded to your server.

www.webandphp.com

Web&PHP Magazine 11.13 | 14

Images in PHP Tutorial

Using imagefilter() and imageconvolution()


The imagelter() function provides a powerful set of ready-made transformations you can use on images. The format of the function call is:
imagelter($image, ltertype [, $arg1, ... $arg4])

IMG_FILTER_EDGEDETECT: Uses edge detection to highlight the edges in the image IMG_FILTER_EMBOSS: Embosses the image IMG_FILTER_GAUSSIAN_BLUR: Blurs the image using the Gaussian method IMG_FILTER_SELECTIVE_BLUR: Blurs the image IMG_FILTER_MEAN_REMOVAL: Uses mean removal to achieve a sketchy effect IMG_FILTER_SMOOTH: Makes the image smoother use $arg1 to set the level of smoothness Sometimes imagelter() will not have the exact transform you require but thats OK because you can create your own using imageconvolution(). For example, the following two function calls in turn emboss and sharpen an image based on a three by three matrix, which is applied across the whole image. Try changing the parameters to see what effects you can create there are examples in Listing6.

Listing 6
imageconvolution($image, array( array(2, 0, 0), array(0, -1, 0), array(0, 0, -1) ), 1, 127); imageconvolution($image, array( array(-1, -1, -1), array(-1, 16, -1), array(-1, -1, -1) ), 8, 0);

$image should be an image resource, there can be up to four arguments in $arg1 through $arg4, and ltertype can be any of the following:
IMG_FILTER_NEGATE: Reverses all colours of the image IMG_FILTER_GRAYSCALE: Converts the image into greyscale IMG_FILTER_BRIGHTNESS: Changes the brightness of the image use $arg1 to set the level of brightness IMG_FILTER_CONTRAST: Changes the contrast of the image use $arg1 to set the level of contrast IMG_FILTER_COLORIZE: Like IMG_FILTER_GRAYSCALE except you can specify the colour use $arg1, $arg2 & $arg3 in the form of red, green & blue and $arg4 for the alpha channel (The range for each colour is 0 to 255)

Everything you could want to know about the GD Image functions can be found online at [3].

Robin Nixon is the author of Learning PHP, MySQL, JavaScript & CSS, the top-selling PHP web-development book since 2009. For more information see lpmj.net or visit nixonpublishing.com to see some of Robins other books.

References
[1] http://php.net/manual/en/image.installation.php [2] https://gist.github.com/webandphp/6992461 [3] http://php.net/manual/en/ref.image.php

www.webandphp.com

Web&PHP Magazine 11.13 | 15

My SQL Conference London 2013


November 11th & 12th London, UK
2 days of tutorials & breakout sessions by My SQL experts
www.percona.com/live

CSS Tutorial

by Ragnar Kurm

Give it some SASS

Compile Your Style: Smarter CSS


Faced with a super-sized stylesheet, Ragnar Kurm developed a workflow for simplifying, modifying and automating his CSS. In the first instalment of this two-part guide, he explores the features of preprocessor SASS and how to optimise your selectors.

Have you ever written CSS for a big website with multiple layouts and found yourself lost in the growing CSS les? Even if you have structured your les, the problem lies in how to work with multiple layouts side-by-side. So far I have not found any convincing proposal of how to approach the situation. I had a CSS project that took more than half a year and I learned a lot during that time. Because I have extensive background in programming, I took some programming tools to writing my CSS. This article covers a workow to work with multiple layouts side-byside. On the way we learn many new approaches to working with CSS. In all of these aspects I give only an introduction, because complete references and tutorials for each are already available. All of these methods or steps can me used independently as well.

Image licensed by Ingram Image

Diagnosing an overweight stylesheet First, lets take a look what happens when a site style grows big. There are variety of symptoms and indications:
Some values repeat a lot. Maybe the most common is the sites default color, used in backgrounds, titles, highlights, etc. In many places dependent values are used. These, as the name suggests, are values which depend on some other value. For example, if the content region is 75% wide then the sidebar value will be 25% wide. So the sidebar value is derived from the content width. Some bits of CSS gets repeated here and there, like rounded corners. Browsers will take more time to render your page. Meaning that the page appears after a brief pause once loaded.

www.webandphp.com

Web&PHP Magazine 11.13 | 17

CSS Tutorial

CSS les grow big which causes: Hard to orient in CSS Loss of overview Lack of structure Increasing danger that changing some value interferes something else Theming for multiple layouts (mobile, narrow, normal, wide) becomes uncomfortable, because layouts live in separate les. Creating graphical elements for different layouts requires extra effort. Fortunately, there is remedy for all of that. There are few tools and few concepts to be learned, but we will go through it step-by-step. Any of these steps can be applied alone or in combination to a theme of any size.

Although it is not compulsory, I have created a small boilerplate package [4] which I now use at every site I create. I nd that it eases the job a lot.

Listing 1: The holy grail of MLSBS


@if $LAYOUT == "all" #logo display: block background-position: center background-repeat: no-repeat @if $LAYOUT == "narrow" #logo width: 100px height: 100px background-image: url(logo-narrow.png) @if $LAYOUT == "normal" #logo width: 200px height: 200px background-image: url(logo-normal.png) @if $LAYOUT == "wide" #logo width: 300px height: 300px background-image: url(logo-wide.png)

The Goal I found that keeping the style information for multiple layouts (different designs depending on screen size or mobile device) in different les is almost unmanageable for me. So I started to look for a better way to have good manageability and have a good overview at the same time. The workow I evolved is multiple layouts side-byside, or MLSBS for short. It allows different layouts to be put into one le, side-by-side, so that styling information for a single element in different layouts is visible at same time. Over time the methodology has evolved, and nowadays if I start a new styling project I cannot imagine doing without it. Here is an example of how our styling information may appear side-by-side, in this case for logo element <div id="logo"></div>. Note that the syntax which is used here is a preprocessor syntax, but property values are given verbatim. The Master Plan What do we need to learn in order to efciently handle multiple layouts? There are many steps, summarized in Figure1. In part one of the article, we will be exploring how to write smarter CSS, covering the following subjects:
Style Preprocessor: SASS. A style preprocessor is central tool to achieve multiple layouts side-by-side. Preprocessors give dynamics to static CSS. In the context of this article, we are specically interested in conditionals. SASS plugin: Awareness of Environment. In order to guide the preprocessor externally, there is

a requirement to be able to specify preprocessor variables from the command line. Therefore we must add a custom SASS plugin. Style and Evaluation: Matching and Specicity. Page rendering speed depends on many factors, and style complexity is one of them. Style complexity is determined by selectors. To write good selectors, one needs to know how the browser matches selectors. Though this is not prerequisite to have multiple layouts side-by-side, it is almost essential to know when building big theme. In part two, we will explore ways to structure, compile and automate our style: Structuring: Splitting and Joining. To keep a style modular there is a need to split the theme into smaller pieces. But in order to avoid too many pieces, these should be joined later on after compiling.

Figure 1: The various elements of the MLSBS strategy

www.webandphp.com

Web&PHP Magazine 11.13 | 18

CSS Tutorial

Automated imagework with ImageMagick. In order to minimize manual image manipulation for different screen sizes, it is convenient to use image scaling on the y from the command line. Media Queries. To be able to select between layouts, one needs to use media queries which then execute the respective style. This is not directly related to the workow, but is just a prerequisite for choosing the layout from the client side. Automatics: Make/Makele. To use all the steps efciently from the command line, we need to bind them somehow. Makele keeps track what needs to be done and carries out necessary operations.

I personally like to keep one selector per line, though this is not a prerequisite. Having one selector per line provides a better overview and is easier to edit. For example, if there is an extra selector to be deleted, it is slightly easier to delete the entire line than a portion in the middle. In the long run, less typing matters. Preprocessors have many powerful abilities: selector nesting, variables, multiple data types, calculations, conditions, loops, style reuse with mixins, custom functions, built-in functions and le inclusion. How can we compile SASS to CSS? Easy:
$ sass global.sass global.css

Variables are placeholders for values. Values can be of many data types like numbers, colors or strings (Listing 4). Variables allow us to specify a value once and use this throughout the theme. They are also handy for calculations. The following example gives an idea of how to dene variables and what kind of values they can hold. Well cover their usage in a moment. Calculations means combining one or more values or variables to get an altered or combined value. It is possible to do arithmetic with dimensions, operations with colors, combine strings to name some of the options (Listing5).

Style Preprocessor: SASS In my theming project there were many repeated and dependent values, lots of repeated code and even more things which were hardcoded, clumsy and uncomfortable. As programmer I could identify many such problems and started to consider using things like C preprocessor or the M4 macro language. Fortunately I found there is an excellent solution for that CSS-specic preprocessors. There are few of them available and I picked SASS. The following introduction and examples are SASS-specic. SASS syntax is the shortest of the different options, which I prefer because it means less typing, fewer errors and a better overview. Preprocessors are built on top of CSS. Compiling your style results in plain CSS. It takes theming closer to programming instead of the continuous hardcoding of CSS. Available preprocessors are SASS [1], LESS [2] and Stylus [3]. There is comprehensive documentation available about preprocessors so Ill give only a very brief overview to give you an idea of how they work. You can see an example of SASS in action in Listing2.

Nesting is a way to write selectors. Child selectors are placed under parent selectors. It avoids repeating common parts of selectors and make HTML-CSS matching much easier (Listing3). It saves time, less typing, less errors, gives better overview and is cleaner way targeting HTML elements.

Listing 3: Nesting
/* SASS source */ #region-left, #region-right a:link, a:visited text-decoration: none color: green /* CSS result */ #region-left a:link, #region-left a:visited, #region-right a:link, #region-right a:visited { text-decoration: none; color: green; }

Listing 2: SASS example


/* SASS source */ $color_main: #3bbfce $content_width: 75% #region-content width: $content_width h1, a color: $color_main background-color: lighten($color_main, 50%) #region-sidebar width: 100% - $content_width /* CSS result */ #region-content { width: 75%; } #region-content h1, #region-content a { color: #3bbfce; background-color: white; } #region-sidebar { width: 25%; }

Listing 4: Variable examples


$spacing_width: 10% $default_font_size: 1.5em $spacing: 16px $content: 800px $color_main: #aabbcc $color_second: rgba(255, 0, 0, 0.75) $path_img: '/les/images/'

www.webandphp.com

Web&PHP Magazine 11.13 | 19

CSS Tutorial

Listing 5: Calculations
/* SASS source */ #frame width: ($spacing + $content + $spacing) color: darken($color_main, 10%) background-image: url($path_img + 'logo.jpg') /* CSS result */ #frame { width: 832px; color: #8aa2b9; background-image: url("/les/images/ logo.jpg"); }

You dont need to use a calculator to get dimensions right, nor use GIMP or ColorZilla to get a darker shade of your main color. The whole thing becomes dynamic. Conditional statements check some value and, based on that, executes one (or another) piece of code selectively (Listing 6). Though not used often, they are prerequisite for MLSBS.

Listing 8: Mixins Listing 6: Conditions


/* SASS source */ $page_width: 800px $content: 800px $sidebar: 220px @if $content + $sidebar != $page_width @warn "Content #{$content} + Sidebar #{$sidebar} must match page width #{$page_width}" /* Output error message: */ WARNING: Content 800px + Sidebar 220px must match page width 800px on line 6 of a.sass /* SASS source */ @mixin rounded($RADIUS) border-radius: $RADIUS -webkit-border-radius: $RADIUS -moz-border-radius: $RADIUS -o-border-radius: $RADIUS -khtml-border-radius: $RADIUS .block @include rounded(20px)

Loops cause a certain piece of code to be executed repeatedly. Usually there is a variable that keeps track how many cycles are done. Loops are useful to generate sequences (Listing7). Style reuse mixins are piece of style that are incorporated into style. It allows to reuse a common code optionally based on arguments generate variations. It helps to get rid of non-semantic selectors and code repetition (Listing8). Custom functions are pieces of code that calculate some value and return it (Listing9). They are more useful for cal/* CSS result */ culations rather than dening styles. Built-in functions are provided by the .block { preprocessor for data manipulation (Listborder-radius: 20px; ing10). There is a load of different kinds -webkit-border-radius: 20px; of built-in functions, for example color -moz-border-radius: 20px; manipulation, transparency manipulation, -o-border-radius: 20px; arithmetic function, list functions etc. -khtml-border-radius: 20px; File inclusion means loading the con} tents of one le into another. The most common use-case is probably to have a le with global denitions, used like this:
@import "colors.sass" @import url("http://fonts.googleapis.com/ css?family=Droid+Sans");

Listing 9: Custom functions


/* SASS source */ /* CSS result */ .gallery-1 { width: 600px; } .gallery-2 { width: 4100px; }

Listing 7: Loops
/* SASS source */ $size: 2em @for $i from 1 through 6 h#{$i} font-size: $size $size: 0.9 * $size /* CSS result */ h1 { font-size: 2em; } h2 { font-size: 1.8em; } h3 { font-size: 1.62em; } h4 { font-size: 1.458em; } h5 { font-size: 1.312em; } h6 { font-size: 1.181em; }

@function gallery-width($cols, $width, $padding) @return $cols * ($padding + $width + $padding) .gallery-1 width: gallery-width(5, 100px, 10px) .gallery-2 width: gallery-width(10, 400px, 5px)

SASS plugin: Awareness of Environment In order to achieve MLSBS, there is a need to be able to read the context in which it is being compiled specically, to access environment variables to distinguish which layout is currently being compiled. A custom plugin is the best way to achieve this, and it is relatively easy to create your own plugins. Heres an ex-

www.webandphp.com

Web&PHP Magazine 11.13 | 20

CSS Tutorial

ample plugin which imports and uses environment variables. First, create the le env.rb (this Ruby script is the entire plugin (Listing11)). To include the plugin, SASS needs to be executed like this:
$ sass ... --require env.rb ...

How could it be useful? For example, if you have a different color scheme for the development site and for the production site. It helps to be more aware where you are doing your changes and not to mix them up. Assume that you have set the environment variable STAGE=devel (Listing12). Compilation in this case would be:

$ STAGE=devel sass --require env.rb tags.sass tags.css

This is one way to feed an environment variable to SASS, but there are other options as well like specifying environment variables from the shell login prole.

Listing 10: Built-in functions


/* SASS source */ $color: #123456 .element color: grayscale($color) background-color: rgba($color, 0.5) width: round(100px/3) .element:hover color: $color /* CSS result */ .element { color: #343434; background-color: rgba(18, 52, 86, 0.5); width: 33px; } .element:hover { color: #123456 }

CSS evaluation by browser As a theme or CSS le grows, it starts to require more browser resources for rendering, and the browser may hang for a moListing 12: Making use of the environment variable ment before the page appears. How can we combat that? First, there /* CSS result */ /* SASS source */ are many sophisticated tools which $STAGE: env(STAGE) body { help you analyze CSS evaluation. In background-color: blue; addition to that, there are two con@if $STAGE == "devel" } cepts which are worth understandbody ing selector matching and selector background-color: blue specicity. As there is already lots of information available on this, I will @if $STAGE == "live" body provide only the most essential tips.
background-color: green

Listing 13: Selector matching speed


#main-navigation { } body.home #page-wrap { } .main-navigation { } ul li a.current { } ul { } ul li a { } * { } #content [title='home'] #main-nav > li { } ul#main-navigation { } html body ul li a { } /* ID (Fastest) */ /* ID */ /* Class */ /* Class */ /* Tag */ /* Tag */ /* Universal (Slowest) */ /* Universal */ /* Slower than it might seem */ /* Don't */ /* Worst */

Listing 11: SASS plugin env.rb


module SASS::Script::Functions def env(var) assert_type var, :String SASS::Script::String.new(ENV[var.to_s()]) end end

Selector matching How does a browser match selectors against HTML? Browsers read selectors from right to left. I repeat, right to left. This might be a bit counter-intuitive at rst glance, but this is most practical way. Processing each element in a selector takes resources. Therefore the fewer elements, the better. Also, failing rules are quicker than matching rules (Listing13). Selector specicity In order to write good selectors it helps to know about selector specicity. If selectors have too low

/* Credit: Chris Coyier, http://css-tricks.com/efciently-rendering-css/ */

www.webandphp.com

Web&PHP Magazine 11.13 | 21

CSS Tutorial

pecicity, they will match too many elements. If a s selector is too specic it will be difcult to be overridden later on if necessary. According to the CSS3 specication: A selectors specicity is calculated as follows: count the number of ID selectors in the selector (= a) count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= b) count the number of type selectors and pseudoelements in the selector (= c) ignore the universal selector Concatenating the three numbers a-b-c (in a number system with a large base) gives the specicity. Often it is easier to understand these rules just by looking at examples. Listing14 is also from the CSS3 spec.

Listing 14: Selector specificity


* LI UL LI UL OL+LI H1 + *[REL=up] UL OL LI.red LI.red.level #x34y #s12:not(FOO) /* a=0 b=0 c=0 -> specicity = 0 */ /* a=0 b=0 c=1 -> specicity = 1 */ /* a=0 b=0 c=2 -> specicity = 2 */ /* a=0 b=0 c=3 -> specicity = 3 */ /* a=0 b=1 c=1 -> specicity = 11 */ /* a=0 b=1 c=3 -> specicity = 13 */ /* a=0 b=2 c=1 -> specicity = 21 */ /* a=1 b=0 c=0 -> specicity = 100 */ /* a=1 b=0 c=1 -> specicity = 101 */

Application How can we apply these concepts to our own work? Most of the time, the selectors I use consist of one element which is usually an ID or a Class. Heres an example:
<table id="the-grid" class="my-grid"> <tr class="my-grid-tr"> <td class="my-grid-td"> ... </td> <td class="my-grid-td"> ... </td> </tr> </table>

Next month In this rst part, weve looked at writing smarter CSS with the use of a preprocessor, a custom plugin and performance-aware selectors. In the next issue, well consider how best to structure our SASS les, and how to automate every aspect of our style from SASS compilation to image formatting. Finally we see how all these methods t together to have multiple layouts, side-by-side.

Ragnar Kurm began programming in his teens and later studied Informatics at Tallinn Technical University. He currently runs his own company, and before that worked for 10 years at an ISP. Ragnar has extensive programming experience in all major languages (ca 20-30) and continues to learn. In recent years, he has spent the majority of his time building webpages, but he loves backend programming most.

now for

Available

Here is a style example that matches above HTML:


.my-grid-td { padding: 10px; } .my-grid-tr:hover { background-color: grey; } .my-grid { width: 100%; } #the-grid { border: 3px solid red; }

References
[1] http://sass-lang.com/ [2] http://lesscss.org/ [3] http://learnboost.github.io/stylus/ [4] https://github.com/ragnarkurm/compile-your-style

$ 3.99
Buy now Buy now at:
Also available to buy buy on: Also on:

www.developerpress.com

www.webandphp.com

PHP equality Feature

Beyond the line with == and ===

you review the equality comparison tables [2]. A noteworthy difference between equality in PHP and that of JavaScript involves comparing null with false which yields true in PHP but false in JavaScript. Years ago, PHP developers favored equality far more than identity. Since the double-equals symbol occurs in math as well as the C programming language, often part of the skill set of PHPs early adopters, the bias naturally appeared. Nowadays, after misadventures in PHPland, you may hear developers advocate vigorously for replacing the loosely-typed equality with the stricter identity, suggesting that otherwise there may be hell to pay. The following snippet, devoid of hashing for simplicitys sake, vividly illustrates the potential peril of coding with ==:
<?php $password = 123; $user_pass = "123xyz"; if ($password == $user_pass) { echo "Welcome, partner!";} // user gains entry!

Image licensed by Ingram Image

Equality in PHPland
PHPs humble equality operator can return some confusing results. Sharon Lee Levy digs into the Zend engines source code to find a solution.
by Sharon Lee Levy

$user_pass validates because strings beginning with a number acquire that value in a numeric context. So, you may do crazy addition, as follows:
<?php $fruit_count = "10oranges" + "20apples"; var_dump( $fruit_count ); // 30

One of the questions posed during the Q&A period of my talk PHP: Quirks, Gotchas & Wizardry [1] at the Web and PHP Conference, September 2013 concerns how to apply the equality operator appropriately, in light of its potential for misuse. The question raises another why does PHP even bother to retain that operator?

PHP has two operators to test for equality, == and ===, respectively equality and identity, similar to JavaScript. Douglas Crockford in his book JavaScript: The Good Parts advocates that users avoid doubleequals which he deems as evil. He claims that == and != present issues because they attempt to coerce the values. The rules by which they do that are complicated and unmemorable. His words might also describe the corresponding PHP counterparts if

If you replace equality in the previous password example with identity, the operands fail to match in type and consequently differ in value, too, denying the user access. The website phpsadness.com displays a stunning, related comparison [3] of two numeric strings, each exceeding the boundaries for integers. The code provoked a heated online debate [4]. Formerly, PHP converted such strings to oats, despite precision is-

www.webandphp.com

Web&PHP Magazine 11.13 | 23

PHP equality Feature

sues. Core contributor Gustavo Lopes resolved the matter by modifying the Zend engines zendi_smart_ strcmp to adhere to the following rule annunciated in a subsequent related discussion [5]: If both strings look like integers (no decimal separator nor exponent) but they were both converted to doubles because of being too large in absolute value, if they both compare equal in a double comparison, and if theyre both larger than 2^53-1 in absolute value, then compare them as a string. This description should have reassured users about the reliability of equality but the fallout from this episode may have soured some on double-equals.

data types with each another. In the following example, I have some code in Python 2.51 as follows:
name = "Wanda" print "W"+"anda" # Wanda print name is "W"+"anda" # true print name == "W"+"anda" # true

The Transitivity Issue The author of PHP: a fractal of bad design [6] criticizes PHPs equality for lacking transitivity and so does phpwtf.org [7]. Transitivity is a property of equality according to mathematical logic, such that if a equals b, and b equals c, then a must equal c. So, if equality is

Ive moved it over to PHP for kicks with the least amount of tampering:
<?php $name = "Wanda"; print "W"+"anda"; // 0 print $name === "W"+"anda"; // print $name == "W"+"anda"; // 1

Listing 1
<?php $objA = new StdClass(); $objA->clr = "blue"; $objB = new StdClass(); $objB->clr = "blue"; $objC = $objA;

Applicability of Equality One should keep an open mind; sometimes equality offers the best choice. Suppose you were to compare objects, as in Listing1. Since $objA and $objB are instances of the same class rather than referring to the same object, the only sensible basis for comparison involves equality. By means of ==, PHP saves you from having to manually check whether the instances share the same properties and values. Changing the color of $objC affects $objA, of course, destroying the equivalency of $objA with $objB since these instances now differ in their respective color value. The other scenario for == involves numeric strings which web applications encounter in data sent via the http protocol, i.e. form data which whether originally character-based or numeric, comes across as text. Databases, too, return data regardless of type generally as strings. Using the == simplies the task of comparing numbers with strings, as in Listing2. If the code were to use === the rst song title would fail to display since the expression would evaluate as false. The double-equals greatest utility consists in permitting you to conveniently compare different

PHP requires the dot operator for concatenation instead of the + symbol. It avoids ambiguity such as confusing an expression for representing addition rather than joining strings. But, lets live with the code as is and see what transpires. The two strings sum up to a zero, since each evaluates as zero in the numeric context afforded by the plus operator. The third statement results in false since Wanda is not identical to zero. False fails to make an appearance for performance reasons, even though its absence may surely rankle some users. In the last line, the double-equals coerces $name to acquire the value of zero owing to the numeric context, causing the comparison to evaluate as true, whose value in the string context of construct print is 1. If we alter the context and compare Wanda with Boolean data, then $name will temporarily acquire the value of true, as follows:
<?php print $name == true; // 1 print $name; // Wanda

var_dump( $objA == $objB); // true var_dump( $objA === $objB); // false $objC->clr = "green"; var_dump( $objA === $objC); // true var_dump( $objA == $objB); // false

Listing 2
<?php $songtitle = "1"; if ($songtitle == 1) { echo "Youre the One that I Want"; } else { echo "Youre the Cream in My Coffee"; }

www.webandphp.com

Web&PHP Magazine 11.13 | 24

PHP equality Feature

not transitive in PHP, must you scrupulously avoid its use? Mercifully, PHP compensates by allowing you to alter the context to obtain the desired result. Consider the following:
<?php var_dump( "null" == 0 ); // true var_dump( 0 == false ); // true var_dump( "null" == false) // false

<?php var_dump( "null" == 0 ); // true var_dump( 0 == false ); // true var_dump( (int) "null" == false) // true

Using this macro, the programmer could substitute the Skip any whitespace block of code with the following:
ZEND_SKIP_WHITESPACE(str,length);

Someone made an arbitrary decision to ignore the white space in a numeric string, and it is maintained for backwards capacity.
Behind the scenes temporary conversions occur with respect to the above snippet. There are all sorts of terms for describing this action: type juggling, type promotion, typecasting, and autoconversion. Take your pick! The == operator converts oranges to apples, so PHP may compare apples with apples. The rst line compares a string with an integer, which bestows numeric context and causes the operands to compare true. But a Boolean context distinguishes the next line which promotes the left operand to Boolean false. The last line may seem odd until you recall that in PHP in Boolean context only the empty string and 0 evaluate as false; every other string is true. If youre displeased with the preceding examples nal result, you can simulate transitivity by manually manipulating the context, as follows:

The int cast promotes the value of the left operand to a zero which in Boolean context changes its value to false, so the expression evaluates as true. The impact of equality lacking transitivity is debatable. While it effects sorting, since the sort() function is based on the quick-sort algorithm (a comparison algorithm), PHP compensates for this decit by offering developers a choice of ags [8].

Of course, macros can have side-effects so one should apply them with great care and thought per the recommendations of gnu.org [12]. Perhaps, for that very reason PHPs source code intentionally lacks such a macro. One may avoid a MACRO entirely, by dening white space as follows:
#dene WHITESPACE ' ','\t','\n','\r','\v','\f'

Equality Strangeness Equality sometimes creates a weird equivalence as follows:


<?php var_dump(" 2" == "2"); // true

Listing 3
/* Skip any whitespace * This is much faster than the isspace() function */ while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' ||  *str == '\f') { str++; length--; }

Why is the result true instead of false? Someone made an arbitrary decision to ignore the white space in a numeric string, so the expression becomes a binary comparison of 2 with itself. Apparently, a concern for backwards compatibility (BC) demands maintaining this oddity (see [9]). Internally, PHP determines whether the string qualies as numeric in the C-source code of function numeric_string_ex, an improved version of the former is_numeric_string. The function [10] reads the value, skipping any blank space before examining the next character, which the excerpt in Listing3 reveals. Renowned PHP expert, Dr. Paul Biggar suggests a macro as a neater solution than the preceding code in a code review [11]. I decided to try myself implementing his suggestion, show in Listing4.

Listing 4
#dene ZEND_IS_WHITESPACE(p,length) \ while (*p == ' ' || \ *p == '\t' || \ *p == '\n' || \ *p == '\r' || \ *p == '\v' || \ *p == '\f') { \ p++; length--; }

www.webandphp.com

Web&PHP Magazine 11.13 | 25

PHP equality Feature

Then, one could modify is_numeric_string_ex by declaring the following variables:


char space_chars[] = { WHITESPACE }; int i, length = 0;

The last step consists of revising the skipping white space code as follows:
length = strlen(str); for ( i=0; i <= length; i++ ) { if (*str == space_chars[i]) { str++; length--; } }

instead of == would have caused the conditional to evaluate as false, so once again the exibility of the loose double-equals operator prevails over the strict triple-equals. The arrays reindexing owes to array_values() [13] outputting the input array of values numerically indexed.

Once again the flexibility of the loose double-equals operator prevails over the strict triple-equals.
Equals [14]. (You may also review this snippets opcodes online [15]). The article helps you gain an understanding of opcodes, handlers and the fast_equal_function() (see lxr.php.net for sourcecode [16]). The default processing of this function looks for operands that are either long integers or doubles, but we have neither in this particular example! What now? The function invokes compare()[17]. So, is that it? Unlikely, especially given that, there may be a setback. None of the specied switch cases matches our data, but hopefully the default pertains. If you inspect it, you may begin to see the glimmerings of light at the end of the tunnel. Look at

The Action behind the Scenes All this discussion about equality and its coerciveness may seem like magic. I strongly recommend reading the C-source code if you seek to gain a deeper understanding of how equality works in PHP. Consider the following example:
<?php $a = "sharon"; $b = true; echo $a == $b; // true

One may argue that either one of the preceding solutions involves more lines of code than the present version of is_numeric_string_ex. However, code legibility is an important aesthetic value, especially for efciently maintaining the code. This one, small example of modicum import will hopefully peak your interest in taking a look at what lies beneath the proverbial hood of PHP.

To understand what happens internally with the above snippet, start by reading The Anatomy of

Listing 5
<?php $arr = array(2 => "late", false => "hood", 4 => "site"); $del = function ( $array, $key ) { unset( $array[ $key ] ); return array_values( $array ); }; $seek = "hood"; if ( ( $dex = array_search( $seek, $arr ) ) == false ) { $arr = $del( $arr, $dex ); } var_dump($arr); //[ 0 => "late", 1 => "site"]

Listing 6
case IS_STRING: \ if (Z_STRLEN_P(op) == 0 \ || (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) { \ Z_LVAL(holder) = 0; \ } else { \ Z_LVAL(holder) = 1; \ } \ break; \

Testing for Falsity and Zero Returning to equality, you may nd it convenient with respect to functions that may return 0 in a true result. For example, take the case of the zeroth element in an array which holds the value you seek. Or, there may be a situation which requires testing on the basis of falsity, as in Listing5. This snippet searches for an element whose value is hood and has a key of false. The key will be promoted to an integer of zero according to the rules governing key identiers, again a temporary change. $dex will possess that value after array_search() locates the value and returns its key promoted to zero. Using ===

www.webandphp.com

Web&PHP Magazine 11.13 | 26

PHP equality Feature

the section that begins with if (!converted) { [18] and youll see a conditional that matches what we have:
else if (Z_TYPE_P(op2) == IS_BOOL) { zendi_convert_to_boolean(op1, op1_copy, result); ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1) - Z_LVAL_P(op2))); return SUCCESS; }

factor in the community might fare as the result of such a catalytic experience? The PHP Community as a whole might gain tremendous benet from such a synergy!
Sharon Lee Levy is a Zend Certified Engineer in PHP5, equally comfortable working with UNIX, Linux or Windows platforms. Her accomplishments in public speaking include dynamic, multi-media presentations at code camps, PHP meetups and technical conferences, most recently at OSCON in July 2013 followed by the Web and PHP Conference, Sept 2013. php|architect has published several of her articles on subjects ranging from email verification to web-based retrieval, to PHPs support for closures. Most recently, she guest-blogged for Zends DevZone on PHPs Remarkable Hexadecimals. She previously conducted a webinar for Zend Technologies, reprising her UnCon Session The Truth about Lambdas and Closures in PHP (Sept 2011) in March 2012. Writing and public speaking activities compliment her primary focus, providing professional web development services for companies in diverse industries, from startups to the Fortune 500.

References
[1] http://webandphp.com/conference/ PHP%3AQuirks%2CGotchas%26Wizardry [2] http://php.net/manual/en/types.comparisons.php [3] http://www.phpsadness.com/sad/47 [4] https://bugs.php.net/bug.php?id=54547 [5] https://bugs.php.net/bug.php?id=62097 [6] http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-design/ [7] http://www.phpwtf.org/its-quite-logical [8] http://us2.php.net/manual/en/function.sort.php [9] http://markmail.org/message/ymzzvyhyf32diu [10] http://lxr.php.net/xref/PHP_5_5/Zend/zend_operators.h#116 [11] http://blog.paulbiggar.com/archive/introducing-malicious-code-reviews/ [12] http://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments. html#Macro-Arguments [13] http://us1.php.net/manual/en/function.array-values.php [14] http://blog.ircmaxell.com/2012/07/the-anatomy-of-equals-opcodeanalysis.html [15] http://3v4l.org/T386t/vld#tabs [16] http://lxr.php.net/xref/PHP_5_5/Zend/zend_operators.h#872 [17] http://lxr.php.net/xref/PHP_5_5/Zend/zend_operators.c#1457 [18] http://lxr.php.net/xref/PHP_5_5/Zend/zend_operators.c#1582 [19] http://lxr.php.net/xref/PHP_5_5/Zend/zend_operators.c#zendi_convert_ to_boolean

Since the second operand is a Boolean value, the MACRO zendi_convert_to_boolean converts the string. The next line performs the comparison, allowing for return values of 0, 1, or -1, which respectively correspond to $a being equivalent to $b, or else greater than or inferior to $b. One quick comment about zendi_convert_to_boolean. The code for this function explains why 0 fails to evaluate as true despite containing a value. Inspecting a portion of the MACRO [19] in Listing6 reveals that this choice, rather than mandated by logic, derives from a design decision.

Beyond the Code Equality in PHP is really about equivalence more than anything else. This notion is further reected in todays PHP Community. Predominantly male contributors ock to the PHP Internals List. Or, some may prefer to congregate on the PHP-FIG forum. Separate groups, equivalent to the extent that they are both devoted to improving PHP, but they are not identical in purpose. As for the women, where do they go? By in large, they interface vis a vis the PHPWomen IRC. Unlike the other two major groups, PHPWomen traditionally has focused as a support group mainly for female users although male developers have joined, too. Imagine someday a convention with all three groups in attendance! Who knows how the equality

Other Links of Interest


http://php.net/manual/en/language.oop5.object-comparison.php http://php.net/manual/en/language.operators.comparison.php http://stackoverow.com/questions/80646/how-do-the-equality-doubleequals-and-identity-triple-equals-comparis http://stackoverow.com/questions/589549/php-vs-operator http://slevy1.wordpress.com/2013/09/17/web-php-conference-2013-talkresources/

www.webandphp.com

Web&PHP Magazine 11.13 | 27

Big Data Column

Column databases overview


by Cory Isaacson

because a lot of data in the row-based structure must be scanned and read. To make matters worse, what happens when you have a table with many columns and you run complex queries across several column values? Its not practical to index them all as it slows down writes, plus the RDBMS can only effectively use one or two indexes to limit the number of rows to scan. This is all very important when it comes to Big Data scale for analytics, tables with 10s of Millions to Billions of rows.

Bio

Up to 100 times as fast as relational databases, column databases are the perfect choice for storing large, unchanging datasets.
In an earlier column, I reviewed some of the basic types of DBMS engines. Well, in this ever-expanding, rapidly changing eld of Big Data there are more types that deserve to be part of the discussion. In this article, I will cover Column Databases, a great solution for data warehouse type applications. This type of thing works ne, performs well and will go as fast as the RDBMS can process single row reads and writes. But what happens in the analytic case, when you need to scan lots of rows to nd specic information or summarize data? Here is an example query that can be very expensive with a row-based structure:
SELECT type, COUNT(*) FROM customer WHERE start_date BETWEEN '1/1/2013' AND '3/31/2013' GROUP BY type;

What is a Column Database? Its easiest to understand how a Column Database works by comparing it to traditional RDMBS engines. As you know, traditional RDBMS engines store data in a row-based fashion, like in Table1. This structure works very well for transactional applications as all data for each unique row is stored together. You can see that it is very convenient for adding, updating or retrieving a single customer row at a time, like the examples in Listing1.

This type of query often causes a table scan, unless you add an index on the key eld (start_date in this case). Even then it can still take a lot of processing,

Cory Isaacson is CEO/CTO of CodeFutures Corporation. Cory has authored numerous articles in a variety of publications including SOA Magazine, Database Trends and Applications, and recently authored the book Software Pipelines and SOA. Cory has more than twenty years experience with advanced software architectures, and has worked with many of the worlds brightest innovators in the eld of highperformance computing. Cory has spoken at hundreds of public events and seminars, and assisting numerous organizations address the real-world challenges of application performance and scalability. In his prior position as president of Rogue Wave Software, he actively led the company back to a position of protable growth, culminating in a successful acquisition by a leading private equity rm. Cory can be reached at: cory.isaacson@codefutures.com.

www.webandphp.com

Web&PHP Magazine 11.13 | 28

Big Data Column

customer_id
1 2

customer_name
Acme, Inc. XYZ Corp

start_date
3/1/2013 4/5/2011

type
Premium Standard

Table 1: Example customer table in a row based structure

Listing 1
INSERT INTO customer (customer_id, customer_name, start_date, type, zip_code) VALUES (3, 'Unlimited Enterprises, Inc.', '5/1/2013', 'Economy, 33333); UPDATE customer SET zip_code = 44444 WHERE customter_id = 2; SELECT * FROM customer WHERE customer_id = 3;

Introducing the Column Database A Column Database is designed to address 11111 this exact situation. As you probably have 22222 guessed, a Column Database stores data by column, instead of by row. The data is stored separately for each column, with pointers back to the logical row that the value belongs too. Tables2 6 illustrate what a column database structure might look like. Why does this work better for analytic queries? There are many factors, and some depend on the specic implementation of the column database engine itself. Here are some of the basic points that are important:
zip_code

SELECT type, zip_code, COUNT(*) FROM customer WHERE start_date BETWEEN '1/1/2013' AND '3/31/2013' GROUP BY type, zip_code;

Row
1 2
Table 2

customer_id
1 2

Row
1 2
Table 3

customer_name
Acme, Inc. XYZ Corp

Row
1 2
Table 4

start_date
3/1/2013 4/5/2011

Row
1 2
Table 5

type
Premium Standard

A column database can answer a query (like the COUNT/GROUP BY query above) very efciently. The way it works is it can scan a single column to nd the values it needs, determine which rows match that query, and then further lter by other column values. If you consider it this way, a Column Database is really a set of indexes, one for each column. Column data can be compressed using various algorithms, making the data much smaller. This is particularly true based on the cardinality of the column (i.e., the number if unique values in a single column). Low-cardinality columns (those with a small number of unique values) can compress extremely well, and be searched very quickly. High-cardinality columns (those with many unique values) will take longer to search, but still may be compressed, and because the scan is of a single column results can be produced much faster. Lets look at a slightly more complex query. Note: I am using SQL for the example queries, but not all column databases use the SQL language, some have other languages or query syntax:

In this query, we now have two columns we are grouping by: type and zip_code. A Column Database can perform this very effectively: rst scanning one of the columns (lets say zip_code), then taking that list of row numbers and ltering again by the second column (type). The results then must be sorted, again an easier operation because the data values are evaluated independently. Thus, the result can be returned extremely quickly, even with a large number of rows. In practice, we have seen Column Databases out-perform a traditional RDBMS by up to 100:1 they can be really fast for the right types of operations.

Things to Consider about Column Databases Because of the way Column Databases are structured, you cannot work with them in the same way as your traditional RDBMS. Here are some of the important points:
Data normally should be bulk-loaded in big chunks of rows (typically 1000 or more rows in a chunk). A Column Database must do a lot of work to build the column structure when new data is added, including compressing column information. You should not use it in a transactional manner, adding one row at a time. Therefore, Column Databases work best for the analytics use case, like a data warehouse of historical, non-changing data. Extracting your data can be slow. If you need to dump data out of a column database, depending on the particular engine implementation, the process can be far slower than what you would expect. The

Row
1 2
Table 6

zip_code
11111 22222

www.webandphp.com

Web&PHP Magazine 11.13 | 29

Big Data Column

reason is that the column database must uncompress and reassemble its column structure back into a row-based format. Some Column Database vendors support Materialized Views, which are presummarized or pre-computed complex queries stored for the next use. These can be very effective if you have a lot of similar or identical queries in your use case. Column Databases need a lot of memory. This is due to the fact that they work best when data for a given column can t in RAM. Therefore, the hardware requirements may be different that your traditional database.

Wrapping it up In this article, I have provided a basic tour and description of how Column Databases work. These DBMS engines are a powerful weapon on the Big Data arsenal, especially for heavy analytics use cases. Integrating a Column Database into your overall Big Data architecture can provide a very successful strategy for meeting this type of requirement. In future articles I will continue to review various types of DBMS engines, and then begin to cover some specic vendors and their capabilities.

1/2 dev.press

www.webandphp.com

Community Column

Talks and the power of human interaction


by Stefan Priebsch

For all the wonders of digital media, lets not forget the importance of speaking in person, writes frequent conference-goer Stefan Priebsch.

The room was already lled with people, even if not all of the seats were taken yet. The conference speaker was setting up his laptop to work with the projector. The topic of the talk was promising, and the speakers reputation impeccable. We were all looking for a talk that might have been the highlight of the day. Then, all of a sudden, a few union workers showed up and started to remove the PA. The speaker started his session. Talking to a room lled with around 100 people without amplication soon began to take its toll on the speakers voice. Since my eyesight is pretty bad, and I usually want to be able to read the slides, I was sitting in one of the front rows, so I could still understand the speaker. Maybe I was

not even paying too much attention, because I was following the conferences Twitter stream. At the time, using Twitter during a conference was still fancy, if only because wireless internet connectivity is notoriously bad at so many IT conferences. In one of the tweets, another attendee of the very session I was sitting in, complained that the speaker was hardly audible in the back rows. This is actually a true story, one I have frequently used to amuse myself and others. Why would somebody tell this to the whole world, that, not unsurprisingly, is rather remotely interested in such information, instead of raising their hand and politely asking the presenter to speak up (which in fact, I did, acting as a

Twitter proxy). Two people in the same room should prefer direct human-to-human communication over social media, after all. Or should they? This year, when I was (once again) speaking at an IT conference, the conference organizer had announced that they would make use of a great new service that some new startup company had created. This service would allow attendees to ask questions on a session they listen to. And they would be able to use their smartphone to do this. As an additional benet, other attendees can vote on questions they also think are worth while. At the end of the session, the speaker was to visit a web page (or use an app) to retrieve the questions asked. I was really curious. Would people use the system? Had the time of we do not need to speak to each other while staying in the same room nally come? Turns out not. To my knowledge, none of the attendees had used the system in any of the other sessions as well. To be very frank with you, I am not really sad about this. Social media has its place and all that, but when we stop talking to each other in person, even when we are gathered together in one location, things start going really, really wrong. There is a good reason why I prefer to speak to a live audience (well, multiple rea-

Bio
Stefan Priebsch unites expert knowledge with an extraordinary sense of when to use which tool. His specialities are object-oriented development and software architecture. As an internationally-acclaimed author and speaker he thrills auditoriums and likes to share his tremendous practical experience.

www.webandphp.com

Web&PHP Magazine 11.13 | 32

Community Column

sons, actually). I have done a few webinars, and was never really happy with them. You miss out on the interaction with attendees. You lack the non-verbal feedback, which is so important in helping you to either slow down or pace up based on the reactions of the listeners. Let me tell you: talking to your monitor is very different from talking to live persons.

Will the next big thing be presenters who answer the questions online instead of verbally?
Dear technology geeks: please do not let us forget that one of the last reasons why we still travel from time to time is so that we are able meet with other people. We want (and need) to interact with them, at a personal level. Once attendees can ask their questions online, why should they even attend the live event in the rst place? Will the next big thing be presenters who answer the questions online instead of verbally? Will we answer to nobody in particular by just typing back? This means that we would miss out on the non-verbal feedback that is so crucial for guring out whether somebody is satised with the answer, or maybe has trouble understand is, requiring further clarication. I have seen online discussions go completely awry. Browse through the PHP internals mailing list archive if you want good examples. I know people who are incredibly nice in person, but are extremely difcult to communicate with online. And as a consultant, I have seen teams discussing features and implementations through commit messages and the comment

functionality of a collaboration platform even though they all sat in the same room all day. Human communication comprises of a verbal and a non-verbal part. Researchers have stated different ratios between verbal and non-verbal communication, but all of them seem to agree that the latter has a share of more than 50%. This is why communicating online is far easier to misread or misinterpret then direct human interaction. In online communication, we lose most of our means of expressing ourselves, and the means to understand the other party. We should never do this without a very good reason.

About
Publisher Software & Support Media Ltd Editorial Ofce Address 86 Great Suffolk St London SE1 0BE www.sandsmedia.com Editor: Elliot Bentley (elliotb@sandsmedia.com) Authors: Cory Isaacson, Ragnar Kurm, Jason Lengstorf, Sharon Levy, Robin Nixon, Stefan Priebsch, Zachary Tong Creative Director: Jens Mainz Layout: Tobias Dorn, Petra Rth Sales: Ellen May +44 207 199 6234 ellenm@sandsmedia.com Contents copyright 2013 Software & Support Media Ltd. All rights reserved. No part of this publication may be reproduced, redistributed, posted online or reused by any means in any form, including print, electronic, photocopy, internal network, Web or any other method, without prior written permission of Software & Support Media Ltd. The views expressed are solely those of the authors and do not reect the views or position of their rm, any of their clients, or Publisher. Regarding the information, Publisher disclaims all warranties as to the accuracy, completeness, or adequacy of any information, and is not responsible for any errors, omissions, in adequacies, misuse, or the consequences of using any information provided by Pub lisher. Rights of disposal of rewarded articles belong to Publisher. All mentioned trademarks and service marks are copyrighted by their respective owners.

www.webandphp.com

Web&PHP Magazine 11.13 | 33

Вам также может понравиться