HackMeUp #11 - special edition

Posted on 11/01/2010 by Andrzej Tucholka Lead Code Architect

During the previous weekend, we've had another HackMeUp event in Tuenti. This time it was special due to reaching the first round edition - 11th. The main difference was the length of the HMU which started on Friday morning and finished late Sunday. This gave us the opportunity to do a bit bigger projects but also to play some games while hacking.

A winner project (Tuentopoly) implemented by Mauricio Morales and Andreas Duchen amazed us all. It is a working proof of concept of a Monopoly type of a game based on Tuenti Places and general Tuenti Local features. Congratulations to the winners and may the iPads serve you well!

Since this was a special edition and there were so many of incredible ideas being implemented we have decided to give recognition to the 2nd project which was @nnotations implemented by David Iglesias and Nicolas Beliard. @nnotations allow using the @ sign for starting quick searches and automatic linking of user profiles, places, photos and other to any text field you might find in Tuenti. Congratulations!

Other projects we've worked on during the weekend included (in random order):

  • Windows Phone 7 App - bringing Tuenti to Windows Phone 7
  • Improvements to script monitoring - helping us monitor the backend scripts execution
  • c2es - es2c - a translator from Spanish to Cani (new-wave l33t?)
  • Sausage party - an additional layer on Tuenti Local maps showing gender, age and other statistical information grouped by locations where people check in
  • Napalm - a BitTorrent based source code deployment system
  • collectM - redefining storage of data collections in memcache
  • Tuenti U - introducing university/school collaboration to Tuenti
  • Page suggestions - suggesting pages a user might be interested in joining
  • Video suggestions - suggesting videos a user might be interested in watching
  • Checkin history - allowing users to track information about their checkins
  • Views analysis - a tool analysing our source code and displaying a graph of view-controller relations
  • Tuenti Unhosted - encrypting user data prior reaching Tuenti servers.

And here are some pictures from the event.

Till next time!

Top 13 JavaScript Mistakes

Posted on 10/13/2010 by Prem Gurbani Frontend Architect
Sergio Cinos Architecture Engineer

Recently we've defined a list of most common Javascript mistakes that developers make. These cover a wide variety of topics and I'm sure you will find among them at least one that you've committed yourself. We describe the theory behind each of the problems/bad practices and show concrete solution(s).

Do you think we're missing something obvious? Leave a comment and let us know about it.

1. Usage of for..in to iterate Arrays

Example:


var myArray = [ “a”, “b”, “c” ];
var totalElements = myArray.length;
for (var i = 0; i < totalElements; i++) {
   console.log(myArray[i]);
}

The main problem here is that the for..in statement does not guarantee the order. Effectively this means that you could obtain different results at different executions. Moreover, if someone augmented Array.prototype with some other custom functions, your loop will iterate over those functions as well as the original array items.

Solution: always use regular for loops to iterate arrays.


var myArray = [ “a”, “b”, “c” ];
for (var i=0; i<myArray.length; i++) {
    console.log(myArray[i]);
}

2. Array dimensions

Example:


var myArray = new Array(10);

There are two different problems here. First, the developer is trying to create an array already containing 10 items, and it will create an array with 10 empty slots. However, if you try to get an array item, you will get ‘undefined’ as result. In other words, the effect is the same as if you did not reserved that memory space. There is no really good reason to predefine the array length.

The second problem is that the developer is creating an array using Array constructor. This is technically correct. However it’s slower than the literal notation.

Solution: Use literal notation to initialize arrays. Do not predefine array length.


var myArray = [];

3. Undefined properties

Example:


var myObject = {
    someProperty: “value”,
    someOtherProperty: undefined
}

Undefined properties (such as someOtherProperty in the above example) will create an element in the object with key 'someOtherProperty' and value 'undefined'. If you loop through your array checking the existence of an element the following 2 statements will both return 'undefined':

typeof myObject['someOtherProperty'] // undefined typeof myObject['unknownProperty'] // undefined

Solution: if you want to explicitly declare a uninitialized properties inside an object, mark them as null


var myObject = {
    someProperty: “value”,
    someOtherProperty: null
}

4. Misuse of Closures

Example:


function(a, b, c) {
    var d = 10;
    var element = document.getElementById(‘myID’);
    element.onclick = (function(a, b, c, d) {
        return function() {
            alert (a + b + c + d);
        }
    })(a, b, c, d);
}

Here the developer is using two functions to pass the arguments a, b and c to the onclick handler. The double function is not needed only adding complexity to the code.

The variables a, b and c are already defined in the inner function because they are already declared as parameters in the main function. Any function inside the inner function will create a closure with all variables defined by main function. This includes ‘regular’ variables (like d) and arguments (like a, b and c). Thus It is not necessary to pass them again as parameters using a auto-executable function.

See JavaScript Closures FAQ for an awesome explanation about closures and contexts.

Solution: use closures to simplify your code


function (a, b, c) {
    var d = 10;
    var element = document.getElementById(‘myID’);
    element.onclick = function() {
        //a, b, and c come from the outer function arguments.
        //d come from the outer function variable declarations.
        //and all of them are in my closure
        alert (a + b + c + d);

}; }

5. Closures in loops

Example:


var elements = document.getElementByTagName(‘div’);
for (var i = 0; i<elements.length; i++) {
    elements[i].onclick = function() {
        alert(“Div number “ + i);
    }
}

In this example, we want to trigger an action (display “Div number 1”, “Div number 2”... etc) when the user clicks on the different divs on the page. However, if we have 10 divs in the page, all of them will display “Div number 10”.

The problem is that we are creating a closure with the inner function, so the code inside the function has access to variable i. The point is that i inside the function and i outside the function refers to the same variable (i.e.: the same position in memory). When our loop ends, i points to the value 10. So the value of i inside the inner function will be 10.

See JavaScript Closures FAQ for an awesome explanation about closures and contexts.

Solution: use a second function to pass the correct value.


var elements = document.getElementsByTagName(‘div’);
for (var i = 0; i<elements.length; i++) {
    elements[i].onclick = (function(idx) { //Outer function
        return function() { //Inner function
            alert(“Div number “ + idx);
        }
    })(i);
}

The outer function is a function that executes inmediatly, receiving i as a parameter. That parameter is called idx inside the outer function, thus inner function creates a closure with idx (instead of i). Therefore idx is completly different from across iterations (i.e. they point to different memory address). This example is very important to understand how closures work. Be sure to read it and play with the code in your browser until you fully understand what’s going on there.

6. Memory leaks with DOM objects

Example:


function attachEvents() {
    var element = document.getElementById(‘myID’);
    element.onclick = function() {
        alert(“Element clicked”);
    }
};
attachEvents();

This code creates a reference loop. The variable element contains a reference to the function (it is assigned to onclick properties). Also, function is keeping a reference to the DOM element (note that inside the function you have access to element because of the closure). So JavaScript garbage collector cannot clean neither element nor the function, because both are referenced by each other. Most JavaScript engines aren’t clever enough to clean circular references.

Solution: avoid those closures or undo the circular reference inside the function


function attachEvents() {
    var element = document.getElementById(‘myID’);
    element.onclick = function() {
        //Remove element, so function can be collected by GC
        delete element;
        alert(“Element clicked”);
    }
};
attachEvents();

7. Differentiate float numbers from integer numbers

Example:


var myNumber = 3.5;
var myResult = 3.5 + 1.0; //We use .0 to keep the result as float

In JavaScript, there is no difference between float and integers. Actually, every number in JavaScript is represented using double-precision 64-bits format IEEE 754. In plain words, all numbers are floats.

Solution: don’t use decimals to “convert” numbers to floats.


var myNumber = 3.5;
var myResult = 3.5 + 1; //Result is 4.5, as expected

8. Usage of with() as a shortcut

Example:


team.attackers.myWarrior = { attack: 1, speed: 3, magic: 5};
with (team.attackers.myWarrior){
    console.log ( “Your warrior power is ” + (attack * speed));
}

Before talking about with(), let’s see how JavaScript contexts works. Each function has an execution context that, put in simple words, holds all the variables that the function can access. Thus the context contain arguments and defined variables. Also a context points to a “parent” context (the context that our caller function has). For example, if functionA() calls functionB(), functionB’s context points to functionA’s context as its parent context. When accessing any variable inside a function, the engine first search in his own context. If not found, it switches to the parent context and so on until it finds the variable or it reaches the end of the context chain. Execution contexts are what makes closures work.

What with() actually does, is to insert an object into our context chain. It injects between my current context and my parent’s context. In this way, the engine first searches in the current context for the requested variable, and then it searches for it in the recently injected object, and finally in the “real” parent context. As you can see, the shortcut used in the example code above is a side effect of using context injection. However usage of with() is very slow, and thus using it for shortcuts is just insane.

Just a side note. Every book recommends not to use with(). Nevertheless all of them are focused on its usage as a shortcut. Context injection is really useful and you may find that you need to use it in advanced JavaScript. In those cases, it’s acceptable to use with() with its real meaning. Although be aware that it’s it’s still very slow from a performance perspective.

See JavaScript Closures FAQ for an awesome explanation about closures and contexts.

Solution: don’t use with() for shortcuts. Only for context injection when you really need it.


team.attackers.myWarrior = { attack: 1, speed: 3, magic: 5};
var sc = team.attackers.myWarrior;
console.log(“Your warrior power is ” + (sc.attack * sc.speed));

9. Usage of strings with setTimeout/setInterval

Example:


function log1() { console.log(document.location); }
function log2(arg) { console.log(arg); }
var myValue = “test”;
setTimeout(“log1()”, 100);
setTimeout(“log2(” + myValue + “)”, 200);

Both setTimeout() and setInterval() can accept either a function or a string as the first parameter. If you pass a string, the engine will create a new function using Function constructor. This is very slow in some browsers. Instead pass the function itself as the first argument; it’s faster, more powerful and clearer.

Solution: never use strings for setTimeout()or setInterval()


function log1() { console.log(document.location); }
function log2(arg) { console.log(arg); }
var myValue = “test”;
setTimeout(log1, 100); //Reference to a function
setTimeout(function(){ //Get arg value using closures
    log2(arg);
}, 200);

10. Usage of setInterval() for heavy functions

Example:


function domOperations() {
    //Heavy DOM operations, takes about 300ms
}
setInterval(domOperations, 200);

We can have a problem when using intervals where operation time is bigger than the interval step time. In the example we are doing a complex (i.e.: long) operation with DOM objects every 200ms. If the domOperations() function takes more than 200ms to complete, each step will overlap with the previous step and eventually some steps may get discarded. This can become a problem.

setInterval() schedules a function to be executed only if there isn’t another execution already waiting in the main execution queue. The JavaScript engine only adds the next execution to the queue if there is no another execution already in the queue. This may yield to skip executions or run two different executions without waiting 200ms between them. To make it clear, setInterval() doesn’t take in account how long it takes domOperations() to complete its job.

Solution: avoid setInterval(), use setTimeout()


function domOperations() {
    //Heavy DOM operations, takes about 300ms

//After all the job is done, set another timeout for 200 ms setTimeout(domOperations, 200); } setTimeout(domOperations, 200);

11. Misuse of ‘this’ There is no example for this common mistake as it is very difficult to build one to illustrate it. The value of this in JavaScript is very different from other languages, where the this behaviour is usually clearer. However, in JavaScript this is a bit different.

First of all, the value of this inside a function is defined when the function is called, not when it is declared. Its very important to understand this, because the value of this depends on how the function is called. In the following cases, this has a different meaning inside myFunction

* Regular function: myFunction(‘arg1’);

this points to the global object, wich is window for all browers.

* Method: someObject.myFunction(‘arg1’);

this points to object before the dot, someObject in this case.

* Constructor: var something = new myFunction(‘arg1’);

this points to an empty Object.

* Using call()/apply(): myFunction.call(someObject, ‘arg1’);

this points to the object passed as first argument.

In this way you can have the same function (myFunction in the example above), that internally use this. However, the value of this is not related to the function declaration itself, only to the way that function is called.

12. Usage of eval() to access dynamic properties

Example:


var myObject = { p1: 1, p2: 2, p3: 3};
var i = 2;
var myResult = eval(‘myObject.p’+i);

The main problem lies in that starting a new execution context with eval() is extremely slow. The same can be achieved using square bracket notation instead of dot notation.

Solution: use square bracket notation instead of eval()


var myObject = { p1: 1, p2: 2, p3: 3};
var i = 2;
var myResult = myObject[“p”+i];

13. Usage of undefined as a variable

Example:


if ( myVar === undefined ) {
    //Do something
}

This check usually works, but it does by pure chance. In the code above undefined is effectively a variable. All JavaScript engines will create the variable window.undefined initialized to undefined as its value. However note that variables isn’t read-only, and any other code can change its value. It’s very weird to find a scenario where window.undefined has a value different from undefined. (nobody will never actually do undefined = 10;). But why take the risk? It is better to use typeof checks.

Solution: use typeof when checking for undefined.


if ( typeof myVar === “undefined” ) {
    //Do something
}

Spawnable Framework - Scripting for everyone

Posted on 9/02/2010 by Nick Flink Lead Dev Tools Engineer

In tuenti we are constantly running scripts. We run them to change the configuration of the site, to update the code on the site and even to check out our current working branch. Having a good scripting framework is obviously important when it comes to automating manual tasks. The way that we have chosen to look at scripts is a bit different than other approaches, because our scripts are quite important to us. We set up our servers so that we can deploy scripts to nearly every server and run them in a consistent manner across all of them. How we have managed to do that is to put our scripts at the top-most layer (same as the traditional index file), as a slightly different entry point. So if index.php is the cgi entry point to our site, Spawn.php is the cli entry point to the same site but is generally used for different purposes.

Using scripts

Scripts are often used to migrate users using older modules of our site to use the new ones or to prime memcache as to not strain the db with every little change. Other times we want to write scripts that work well from the command line with arguments manual pages etc. Sometimes we want to run a script on another server, perhaps even as another user. Many times you want to just run some script every day to analyze a specific set of data.

Requirements

Our scripts need to be maintainable (with code using regular domain layer where possible), easy to use and write for developers and easily executed in parallel. To increase maintainability we've decided that all scripts will have one entry point. Taking this approach leads us to: php Spawn.php . To make the lives of our developers easier, the system has to be equipped with bash completion and by default support execution in different threads. Spawn.php can only run jobs that implement the Spawnable interface. To run a job, Spawn.php simply calls a static function run() as shown below in the Spawnable interface.


interface Spawnable{
    static public function run();
    public function startJob();
}

Script types

CommandLineArgJob

The command line arg job allows easy use with the bash command line. It has bash completion, man pages, and uses reflection to construct your CommandLineArgJob with the same names used in the constructor on the command line. For example the following code class would be constructed with $number=5 and then startJob would run and echo the sent number.


class MyJob extends CmdLineArgJob{
    private $number;
    public function __construct($number){
        $this->number = $number;
    }
    public function startJob(){
        echo 'number = ' . $this->number;
    }
}

me@aserver:~#php Spawn.php MyJob number=5 number = 5

Bash completion tells you all of the optional arguments and if you miss one that isn't optional eg. $number, the framework will tell you that it was required.

ForkedJob

We use forked jobs mainly for migrations. Executed jobs are being controlled by a parent process which fetches arguments for each of them from the input the script has received. As long as there is data for the next job and it hasn't exceed the maximum number of active jobs, it will fork new threads with new jobs assigned to them. Developers can optimize the number of used resources against the speed of execution by changing the allowed number of active forked threads.

QueuedJob

The final job type is the most complicated and flexible one. The system requires a queue client daemon (QCD from now on) to be running on a target machine specifically configured to run particular types of jobs. A page execution can then create a job and enqueue it into a named queue located on one of our queue servers. If there is a QCD waiting on the queue server socket when the job arrives at the queue server it will be routed to the server on which the QCD is running. This allows us to specifically configure servers to run different jobs. Jobs that are transfered to the QCD through a queue server must extend the QueuedJob, which has an additional method named enqueue() beyond the normal run() and startJob() methods. QueuedJobs are transfered as serialized php classes, so when QCD deserializes them, all of the member variables remain intact. Finally the QCD calls the startJob() method and the job is executed. This has an interesting effect where in the constructor is actually called on the frontend server and the startJob is called on the processing server.

An example QueuedJob could appear like this: php Spawn.php ExampleJob a:1:{i:0;a:2:{i:0;s:10:"ExampleJob";i:1;O:10:"ExampleJob":2:{s:15:"^@ExampleJob^@id";s:3:"156";}}} An advantage to the serialized packaging is the ability to send by email specific commands. For example if I were to find a bug in the above ExampleJob, I could even send by email the packaged command line to another developer who would simply copy paste the line into a command prompt to reproduce and start debugging.

HackMeUp #10

Posted on 9/01/2010 by Andrzej Tucholka Lead Code Architect

Last Friday, we've had another HackMeUp event in Tuenti. During the day we've had our Engineers twisting their minds around a lot of projects which included (ordered by number of votes). When looking at awesomeness of those, have in mind that all of those projects are one day stunts!

  • WINNER - Nearby places on mobile - see description below
  • Flickr integration - integrating your tuenti albums with flickr, allowing to view photos from flickr through Tuenti.
  • Grouping friends - implementing the management of groups of your friends and integration with some of the common actions you could do on Tuenti.
  • Radio player - use Tuenti player as an internet radio client handling radio playlists and with different types of visualization.
  • Strawberry Jam - an integration of our internal tool (Marmite) with CruiseControl allowing to automatically detect who and when broke a unit test.
  • Amazing extension/Share on Tuenti - Chrome extension transforming the way Tuenti is displayed and make uploads of pictures from the internet much easier
  • CSV Importer - import your data, contacts into Tuenti from a portable format handled by many popular sites like LinkedIn, Facebook etc.
  • URL Shortener - a Tuenti service for shortening URLs
  • ChromedTuenti - Chrome extension allowing easy single and mass uplaods to Tuenti along with the ability to instantaneously share URLs via your status
  • Places/Emots - Tweaks to Places service and emoticons list for the chat

And a note from the winners: Jorge J. Barroso Carmona Mobile Applications Developer Miguel Lara Encabo Senior Mobile Engineer

We decided to play around with one of the new features available in the social network –Tuenti Sitios– and more specifically finding out places close to your location.

As it was a quick one day project we focused on developing this feature for iPhone and Android, facing in each platform the challenges of tracing the location of the device, following its orientation and displaying an overlay with the places on top of a camera view. We went for the simplest approach possible: projecting the place on a circular strip parallel to the ground. We though about using sphere projections similar to those on OpenGL, but we decided it was too much of an overkill.

By projecting on a 2D wrapped-around strip we simplify the process of positioning the place to just finding out the angle ?, based on the longitude and latitude of both the device (O) and the place (P). Once ? is determined, knowing the orientation of the device is enough to project it on the strip.

And few additional photos from the event.

PHP Coding Standard

Posted on 7/30/2010 by Andrzej Tucho?ka Lead Code Architect

Notice:

In most of the examples the rule of writing comments has been omitted due to consistency and clarity of this document.

1. General rules

This document includes all rules defined in a General Coding Standard

2. Code layout

All of us highly value our time, so we want the results of your work (especially when confronted with other people) to comply with the accepted quality standards. This is meant to assure that you don't build a castle that is accessible only by yourself. Guides defined in this chapter address not only the quality of the visual side of the source code, but also allow us to avoid common errors that are created by poorly structured source code. It is our intention to create a friendly and healthy work environment, so it is neccessary to communicate with each other in an elegant way.

2.1. Brackets { }

Always include braces in the source code. Even if the body of some construct is only one line long, do not drop the braces. Opening braces never go on their own line and are separated by a single space from previous block. Closing braces always have their own line, except for if..else and try..catch statements where else and catch are preceeded by closing and succeeded by opening brackets.

Examples:


class Parser {

public function execute() { if (...) { foreach (...) {

} } else {

} } }


2.2. Brackets ( )

Direct usage of parentheses is allowed only after methods. Keywords (like if, while...) should be separated with a space from the opening bracket.

Note that when instantiating a class, we are actually calling its constructor method, so this is the only exception from rules defining parentheses usage.

You are not allowed to use parentheses with the "return" statement.

Examples:


class TuentiControl {

// ...

public function draw() { if ($this->isVisible) { foreach ($this->elements as $drawableElement) { $drawableElement->draw(); } } } }


2.3. Logical operators

All operators need to be separated from corresponding values with spaces. Also, the equation defined by usage of every single operator has to be enclosed within parentheses.

Above rules do not apply to operator NOT (!). This operator should be preceded with a space and should be applied directly before the value to be negated. Operator NOT does not have to be preceeded with space if there is an opening parenthesis just before it.

You should only use C-style operators (&&, ||).

Examples:


// ...
    if (($this->isVisible && !$this->isEmpty) || $this->forceDraw) {

} // ...


In case of the need to break the line, the line break should be done on the operator and the operator should start the next line. Next line that continues expression from the previous one, always has to be be indented one more level (with regards to general indenting rules).

2.4. Quotes

You should always use single quotes unless you specifically need variable interpolation to be done on that string. Remember that all literals should be used in conjuction with constants. Double quoted strings will be evaluated/parsed by PHP creating an unnecessary performance decrease.

Examples:


class Image {

const LOG_IMAGE_ERROR = 'Image error'; const LOG_IMAGE_WARNING = 'Image warning'; const LOG_IMAGE_NOT_STORED = "Image could not be stored.\nFull error information:\n%s";

// ... } catch (ImageNotStoredException $e) { $this->logService->noteThis(Image::LOG_IMAGE_ERROR, sprintf(Image::LOG_IMAGE_NOT_STORED, $e->getMessage())); } // ... }


2.5. Shortcuts

  • Operators - it is allowed to use a shortcut operator (++, --) only in separate line and for loops.
  • Conditionals - it is allowed to use short form of conditionals only in assignments that are one-liners.
  • PHP tags - it is allowed to use only this form of <?php tag. All other forms are strictly forbidden.
  • strings concatenation - it is allowed to use "." operator for string concatenation. Always add a space before and after the operator. It is allowed to break the lines while concatening strings, but in this case each new line has to start with "." operator (as with all operators).

Examples:


class ChatRoom {

// ...

public function getUserList() { $output = (isset($this->userList)) ? $this->userList->getString() : Lang::EMPTY;

return Lang::USER_LIST_TITLE . Lang::USER_LIST_COLUMNS . $output; } }


2.6. Indenting

Use an indent of 1 tab character. Do not align your code other then adding additional indentation level. It is advised to have editors configured to expand a tab character to the width of 4 spaces. All additional tools displaying source code will have this configured. The target line length is 80 characters; i.e., developers should aim to keep code as close to the 80-column boundary as is practical. However, longer lines are acceptable. The maximum length of any line of PHP code is 120 characters.

In a special case when there is a need to break the line within a logical equation, it should be indented by one more level. In this case each new line should be started by a logical operator that should be applied to the equation.

When passing arguments to a method, they must be separated by comma-space (', ').

Examples:


class Foo {

// ...

public function Bar($name, $surname) { $foo = ''; if ((isset($name) && (count($name) > Foo::MIN_NAME_LENGTH) && (count($name) forceNamePrint && ($name != Foo::EMPTY_NAME))) {

$foo = $this->prefix.$name; } return $foo; } }


2.7. Code blocks

This subchapter defines a standard to write code blocks. It contains mostly self-explenatory examples of specific blocks of code with comments where neccessary.

2.7.1. switch..case

Do not enter too many routines in each case statement. switch..case is a control block, not a group block. Usually we should limit it to 4-5 commands, but it cannot exceed 15 lines per switch. Since this block will be contained inside the methods, see also a point about defining proper method for additional size limits.

The default case has always to be defined and the break statement has to have the same indentation level as the source code.

Examples:


// ...
    switch ($entityType) {
        case Entity::TYPE_LOG:
            $this->logInformation($value);
            break;

case Entity::TYPE_FILE: $this->deleteFile($value); break;

default: throw new EntityTypeNotDefined($entityType); break; }


Falling through case statements is strongly discouraged except where several options share the exact same code. If you have few similarities in your case statements, most of the times an additional method encapsulating these routines should be defined.

Examples:


// ...

case a: case b: case c: doStuff(); break;


2.7.2. if..

Examples:


// ...
    if ($this->isCallable()) {
// ...
    } else {
// ...
    }

// ...

if ($this->getPreviousItem() == $this->currentItem) { // ... } else if ($this->getPreviousItem()->getOrder() currentItem->getOrder()) { // ... } else { // ... }


All comparisons should be positive whenever possible.

Examples:


// Instead of:
    if (!($this->isDisabled())) {
        $this->render();
    } else {
        throw new RenderingDisabledControl($this->getId(), $this->getType());
    }

// Use: if($this->isDisabled()) { throw new RenderingDisabledControl($this->getId(), $this->getType()); } else { $this->render(); }


Do not implement checks like "if ($myVariable)" if $myVariable might not be a boolean value. Also, the variable has to be named with accordance to bool semantics ($is...) to make this even more clear.

It is strongly suggested to use a "else if" form instead of "elseif".

2.7.3. while

Examples:


// ...
    while ($this->getCount() > 0) {
// ...
    }

2.7.4. do..while

Examples:


// ...
    do {
// ...
    } while ($this->getCount() > 0);

2.7.5. foreach

Define iterators for collection classes that are being used in your code.

Examples:


class CommentsIterator implements Iterator
{
    private $commentsCollection;

public function __construct(CommentsCollection $collection) { $this->commentsCollection = $collection; }

public function rewind() { $this->commentsCollection->rewind(); }

public function current() { return $this->commentsCollection->getCurrent(); }

public function key() { return $this->current()->getId(); }

public function next() { $this->commentsCollection->next(); return $this->current(); }

public function valid() { $isValid = ($this->current() !== false); return $isValid; } }

// ...

foreach ($myComments as $id => $comment) { // ... }


2.7.6. class

Do not write code that is not encapsulated within a class.

As a rule each class should be created within a separate file. It is acceptable to create proper Exception classes inside the class "owning" them. See exception handling for more details on exceptions.

2.7.7. method

Do not write routines that are not encapsulated in methods.

A public method should preserve the class invariants of the object it is associated with, and should always assume that they are valid when it commences execution (private methods do not necessarily need to follow this recommendation). To this effect, preconditions are used to constrain the method's parameters, and postconditions to constrain method's output, if it has one. If any one of either the preconditions or postconditions is not met, a method may raise an exception. If the object's state does not satisfy its class invariants on entry to or exit from any method, the program is considered to have a bug.

A size (in lines) of the method content should not exceed 25 lines of code.

Examples:


class Foo {

public function __construct() { // ... }

private function prepareResult() { // ... }

protected function execute() { // ... } }


See Proper method code for more information on methods.

2.7.8. try..catch

If your code can fix the exception, then always encapsulate unsafe code in a try..catch clause and handle all exceptions that can be thrown by enclosed routines. Do not catch base exception class, but provide specific behavior for each type of exception. Exceptions are meant to communicate the developer information about the problem, not to hide information about an error from end-user. This is why we need to be specific when throwing and catching exceptions. Also, if a try..catch clause encloses too much of the code it usually means your code design is probably broken.

Remember that, the catch clause always has to be sorted from most detailed (in terms of inheritance) exception to most general one.

Examples:


// ...

try { // ... } catch (MySqlException $e) { // ... } catch (DbException $e) { // ... } catch (Exception $e) { // ... } // ...

/** * This is a general exception for all mysql related errors * * @author Andrzej Tucholka */ class MysqlException extends DbException { private $errorMessage = 'A general mysql error occurred';

public function __construct($message = NULL) { parent::__construct(($message !== NULL)? $message: $this->errorMessage, ExceptionCodes::STORAGE); } }


2.8. Documentation and comments

It is our intention to create a preconfigured development environment that will help developers in documenting their source code.

Consider your comments a story describing the system. Expect your comments to be extracted by a robot and formed into a manual page. Class comments are one part of the story, method signature comments are another part of the story, method arguments another part, and method implementation yet another part. All these parts should weave together and inform someone else at another point in time just exactly what you did and why. Do not swear in comments or use words that are considered vulgar.

Comments shoud document decisions you made while writing the code. See missing code for a good example of that kind of decisions.

All documentation blocks ("docblocks") must be compatible with the phpDocumentor format. For more information, visit phpDocumentor webpage.

Some general rules about writing comments:

  • The sharp, '#', character should not be used to start comments.
  • Each comment has to be indented to the left.
  • Always write comments before the statement.
  • Code commented out should be deleted before merging back into the main branch.
  • Don't commit commented out code without writing a reason why it shoudl stay there

If a method or a class is deprecated from a specific point in time onwards, a leading comment has to be expanded by @deprecated command containing information about the date from when this code is deprecated, the deprecating reason and should point to the new valid code.

Every file that contains PHP code must have a header block at the top of the file that contains these information (add parameters if they apply):

Examples:


/**
 * <>
 *
 * LICENSE: This file can only be stored on servers belonging to Tuenti Technologies S.L.
 *
 * @copyright 2008, (c) Tuenti Technologies S.L.
 * @author Andrzej Tucholka
 * @package General
 * @subpackage Config
 * @todo <>
 */

If there are multiple people working on the file, list each author in a separate doc-param providing general scope of responsibilities in addition to your name.

Every class must have a docblock that contains these information (add parameters if they apply):

Examples:


// ..

/** * <> * * @author Andrzej Tucholka * @todo <> */


If there are multiple people working on the class, list each author in a separate doc-param providing general scope of responsibilities in addition to your name.

Every method must have a docblock that contains these information (add parameters if they apply):

Examples:


// ..

/** * <> * * @author Andrzej Tucholka * @throws <> * @param TypeOfParameter NameOfTheParameter Comment * @return TypeOfReturn NameOfReturn Comment * @todo <> */


If there are multiple people working on the method, list each author in a separate doc-param providing general scope of responsibilities in addition to your name.

If your method is not returning any result and therefore it's result should be discarded your documentation tag should be @return void

It is possible, that the return values of your methods will not be simple types nor objects. In case of arrays you should apply more detailed rules for describing your possible return values. Below you'll find examples demonstrating how the TypeOfParameter? and Description should be expanded to provide full information for array based results.

Examples:


// ..

/** * Returned value is a array of objects with meaningless keys * @return User[] An array containing a list of banned users * * Returned value is an array of objects with meaningful keys * @return User[int] Format: array($userId => User) * * Returned value is an array of arrays * @return int[int][] Format: array($userId => array($inviteCount)) * * Returned value is an array of arrays * @return string[string][int] Format: array($userName => array($friendId => $friendName)) * * Returned value is an array of arrays with meaningful positions * @return string[string][int] Format: array($userName => array(BEST_FRIEND(0) => $friendName, WORST_FRIEND(1) => $friendName)) */


A special case of the method is one that we consider to be an entry point to the system. These methods need to have an extended description including additional tags documenting an entry point:

  • @epoint-public-action - Defines if the method is an entry point. If it's not, the tag is optional.
  • @epoint-changes-state - Description defines a scope of changes done to the user state
  • @epoint-privacy-control - Description defines a way in which the privacy check is being done on the entry point.
  • @epoint-summary - Describes the risks, how it should be tested and any additional information associated with the entry point.

Examples:


// ..

/** * Shows general canvas * * Three params are available to use by others controllers, in order to reuse * this action (p.e. photo edit) * * @epoint-public-action NO (this doc tag is optional but must go here when an action is public) * * @epoint-changes-state YES * - It deletes notifications when: a) you are viewing a new tagged photo where you appear in, * b) you are viewing a photo where you are tagged in and that photo has new comments * * @epoint-privacy-control YES * Possible cases, a photo can be viewed when: * - if current user is friend with the owner of the photo * - if current user has common friend with the owner and the owner allows friends-of-friends * to see his photos * - if owner has photos as public * - if current user is viewing a photo in a page and the page allows it * * @epoint-summary Displays a photo and receives a key which may be altered by attackers, * tests should pass wrong keys or privacy-closed photo keys. * More information: (you should provide useful information here when necessary) * * @author Mauricio Morales * @param Photo $item * @param boolean $performCleanUp defaults to true * @param boolean $doPreload specifies whether a preload is required or not * @return View */ public function viewPhotoAction($item = NULL, $performCleanUp = TRUE, $doPreload = TRUE) {

}


When commenting imported modules, you should provide a comment block describing the usage scope of that module (see below). In other cases - general imports etc, the comment block is not necessary.

Examples:


// ..

/** * <> * * @see */


Documenting class fields is optional, but should be used in all non-obvious cases - usually with arrays describing the contents of keys and values. In case of documenting the field use a doc block like this: Examples:


// ..

/** * Contains a database configuration $serverIp => $weight; where weight is used for load balancing * * @var array contains database configuration */ private $databaseConfig = array();


2.9. Packages/subpackages structure and usage

The names of packages and subpackages can be only one word containing only letters, digits and "_", "-", "[" or "]" characters.

If below structure is not sufficient, feel free to create new subpackages to address your documentation needs but keep the same naming style and update A-Team on the new packages.

  • Api
    • Exceptions
  • Control
    • Base

    • Interfaces
    • Exceptions
  • Domain
    • Base

    • Interfaces
    • Exceptions
    • ... name of the domain module
  • General
    • Config

    • Exceptions
  • Helpers
    • Domain

    • Storage
    • ... other groups by area
  • Storage
    • Mysql

    • Memcache
    • Sphinx
    • Queues
    • Cached-collections
    • Exceptions
    • ... other storage types
  • Tests
    • Base <-- all framework tests go into this subpackage

    • ... name of the domain/control module.

3. Coding practices

This chapter introduces practices that have to be respected within the source code. They aim in increasing safety of the code and its performance.

We hope to expand this list with time, so do not hesitate to submit new ideas to the content owner (defined on top of this document).

3.1. Lazy initialization

Do not do any real work in an object's constructor. Inside a constructor initialize variables only and only perform actions that can't fail and do not require too much time to compute. It is not our intention to always define open method. This situation should be only happening if the object needs to be prepared by performing some specific routines at runtime. Nevertheless, those routines cannot be put into the constructor.

Create an open() method for an object which completes construction. open() should be called after object instantiation.

Examples:


    class Device
    {
        const UNINITIALIZED_DEVICE_NAME = 'Uninitialized device name';

private $name = ''; private $type = 0;

public function __construct($deviceType) { $this->name = Device::UNINITIALIZED_DEVICE_NAME; $this->type = $deviceType; }

public function open() { // ... } }


3.2. Proper method code

Methods are not designed to remember data. Each of them should work strictly on the arguments passed through parameters. It is acceptable for methods to access state of the object in which they are defined.

Method, being associated with a particular object, may access or modify the data private to that object in a way consistent with the intended behavior of the object. Consequently, rather than thinking "a method is just a sequence of commands", a programmer using an object-oriented language will consider a method to be "an object's way of providing a service" (its "method of doing the job", hence the name); a method call is thus considered to be a request to an object to perform some task.

Always define methods with lowest visibility possible, to ensure the clarity of object's interface. Remember, that after you define a method public, it is probably going to stay that way for a long time.

Try to avoid usage of PHP magic methods (get, set, call) due to performance reasons. It is acceptable to use them only in situations that bring a huge value to the clarity of the code, and where they are not called too frequently.

See method description for more information on methods.

3.3. Usage of continue and break

Continue and break should be used very cautiously.

The two main problems with these commands are:

  • They implicitly bypass the test conditions of the loops
  • They might bypass the increment/decrement expressions
  • They break the default code structure defined by regular expressions

Mixing continue with break in the same loop is a sure way to disaster. In general you are strongly discouraged from using these commands.

3.4. "Missing code"

When writing an empty code block, always insert a comment describing why this block is (and should continue to be) empty. This rule also applies to code that was intentionally omitted. If you see a possibility of the code to execute and decide to ignore it (because of some environment boundaries) write a comment about it so the reader of the code will have the same knowledge as you have.

3.5. Safe code

Communication between classes on the level of sending messages and polymorphic calls, should be properly secured. Each message sent from different class inheritance level or a different object should be checked for validity upon arrival. Also, calls to subclass routines have to be enclosed in exception handling routines to prevent unexpected termination of execution.

Examples:

abstract class BasicObjectFactory {

abstract protected function instantiateObject($className);

protected function getObject($objectType) { $result = FALSE; if ($objectType == BasicObject::PROTOTYPE_OBJECT) { try { $result = instantiateObject(BasicObject::PROTOTYPE_CLASS_NAME); } catch (CreateObjectInstanceException $e) { // If the object doesn't get created, return default value } }

return $result; } }


3.6. Floats

Don't use floating-point variables where discrete values are needed. Using a float for a loop counter is a great way to shoot yourself in the foot. Always test floating-point numbers as =, never use an exact comparison (== or !=).

It is typical that simple decimal fractions like 0.1 or 0.7 cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9.

This is due to the fact that it is impossible to express some fractions in decimal notation with a finite number of digits. For instance, 1/3 in decimal form becomes 0.3.

If higher precision is necessary, the arbitrary precision math and gmp functions are available.

Also, it is important to remember about locale settings (0.1 vs 0,1) when converting floating values to strings.

Examples:


/*
 * Two floating point numbers should be considered equal if their absolute
 * difference does not exceed a certain value epsilon. Epsilon in this case
 * defines the precision of the comparison
 */
    $epsilon = 0.000001;

if (abs($firstFloat - $secondFloat) < $epsilon) { // It can be assumed that the numbers are equal. }


3.7. Usage of NULL

Remember that NULL is not a value. It should be treated and identified with unknown state rather then empty. This is especially important with SQL queries (check SQL Coding Standard for details).

Handling NULL in your code:

  • when declaring a variable that will be checked against NULL and is not set in the constructor, you should initialize it with NULL
  • when verifing if something isn't NULL always use a strict comparison

Examples:


//  ...
    private $targetFilter;

// ... if ($this->targetFilter !== NULL) { $this->targetFilter = new TargetFilter(); } // ...


3.8. Type hinting

The use of type hinting is encouraged where possible with respect to the module design.

Examples:


    class MyClass {

public function test(OtherClass $otherclass) { echo $otherclass->var; }

public function test_array(array $input_array) { print_r($input_array); } }


3.9. Require / include

If a module uses another module, then the using module is responsible for loading the other one. If the use is conditional, then the loading should also be conditional.

If the file(s) for the other component should always load successfully, regardless of input, then use PHP's require_once statement.

The include, include_once, require, and require_once statements should not use parentheses.

Defined paths for including/requiring modules should always start with LIB_PATH constant to avoid lookups during the execution. If you are including/requiring files that are placed in a different root directory, use different constant to define a full path pointing to the file.

In general you should use only use require_once. Other forms are permitted (with a strong preferrence of require), but only in special cases like in templating, configuration loading etc.

3.10. One statement per line

Each line of the code can contain only one statement. The exception from this rule are logical statements, that contain execution and logic substatements. Still, in this case it is required to preserve as much clarity as possible. Also, the usage of time-consuming execution substatements is highly discouraged in logical statements.

3.11. Execution path breaking

The only acceptable way to break your code execution in the middle of the routine is by throwing an exception. Do not put return in the middle of the method / code.

Examples:


// (BAD) Instead of:
    public function getCoolKids() {
        // ... do some stuff

if ($this->isDisabled()) { return FALSE; }

// ... do some more stuff

return $coolKidsArray; }

// (GOOD) Use: public function getCoolKids() { // ... do some stuff

if ($this->isDisabled()) { throw new CannotRetrieveDataFromDisabledSet(); }

// ... do some more stuff

return $coolKidsArray; }


3.12. Passing one/many items through a parameter

If the method you have just written can accept a parameter to be a single value or an array of these values, you need to assure that the method will perform properly in both cases. To assure that check if the variable that represents the input parameter is an array. If not, wrap in into one and proceed like in the case of an array. This approach is meant to assure that there is only one processing code in your method.

Examples:


//  ...

public function delete($ids) { if (!is_array($ids)) { $ids = array($ids); }

foreach ($ids as $id) { // ... do some stuff } }


3.13. Closing PHP tag

Do not use the closing PHP tag. Ever.

3.14. Profanity

Do not use improper language in your source code. Ever. It is advised to put in funny comments though.

4. Miscellaneous

This chapter describes general rules that didn't fit in previous chapters.

4.1. File extensions

All files containing code or being inserted into PHP files / parsed by the PHP have to have a ".php" extension.

4.2. Globals usage

Introducing global variables is forbidden.

4.3. Optimizations

Always try to optimize your loops if operations are going on at the comparing part, since this part is executed every time the loop is parsed through. For assignments a descriptive name should be chosen.

Examples:


    for($i = 0, $size = count($post_data); $i < $size; $i++) {
// ...
    }

Also, avoid using in_array() on huge arrays, and do not place them into loops if the array to check consist of more than 20 entries. in_array() can be very time consuming and uses a lot of cpu processing time. For little checks it is not noticable, but if checked against a huge array within a loop those checks alone can be a bunch of seconds. If you need this functionality, try using isset() on the arrays keys instead, actually shifting the values into keys and vice versa. A call to isset($array[$var]) is a lot faster than in_array($var, array_keys($array)) for example.

Golden rules of optimization:

  • First make it work, then make it fast. Beware of premature optimization.
  • Optimize only the slow stuff.

4.4. Common mistakes

This is a list of functions that are commonly mistaken to do something else. The list also includes the recommendation for function usage.

  • empty() - commonly mistaken due to different results with different content. Most of the time it will not be neccesary to use this function. Usage of this function is discouraged.
  • isset() - commonly misused to verify if the variable has a value. In case of standard variables check against a NULL should be performed. In case of array values, array_key_exists check should be performed. Do not use this function.
  • unset() - commonly misused to reset a variable. In case of standard variables assign a NULL value rather then unset it. Only use this function to remove an array value.
  • goto - that was introduced in PHP 5.3. Do not use it.
  • is_a - do not use at all. Leaves instances of objects in the process' memory

Pages

Follow us