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):
And here are some pictures from the event.
Till next time!
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.
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]);
}
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 = [];
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
}
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);
};
}
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.
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();
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
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));
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);
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.
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];
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
}
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.
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.
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();
}
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.
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.
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.
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!
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.
In most of the examples the rule of writing comments has been omitted due to consistency and clarity of this document.
This document includes all rules defined in a General Coding Standard
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.
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 {
}
}
}
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();
}
}
}
}
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).
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()));
}
// ...
}
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;
}
}
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;
}
}
This subchapter defines a standard to write code blocks. It contains mostly self-explenatory examples of specific blocks of code with comments where neccessary.
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;
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();
}
It is strongly suggested to use a "else if" form instead of "elseif".
Examples:
// ...
while ($this->getCount() > 0) {
// ...
}
Examples:
// ...
do {
// ...
} while ($this->getCount() > 0);
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) {
// ...
}
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.
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.
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);
}
}
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:
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:
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();
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.
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).
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() {
// ...
}
}
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.
Continue and break should be used very cautiously.
The two main problems with these commands are:
Mixing continue with break in the same loop is a sure way to disaster. In general you are strongly discouraged from using these commands.
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.
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;
}
}
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.
}
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:
Examples:
// ...
private $targetFilter;
// ...
if ($this->targetFilter !== NULL) {
$this->targetFilter = new TargetFilter();
}
// ...
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);
}
}
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.
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.
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;
}
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
}
}
Do not use the closing PHP tag. Ever.
Do not use improper language in your source code. Ever. It is advised to put in funny comments though.
This chapter describes general rules that didn't fit in previous chapters.
All files containing code or being inserted into PHP files / parsed by the PHP have to have a ".php" extension.
Introducing global variables is forbidden.
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:
This is a list of functions that are commonly mistaken to do something else. The list also includes the recommendation for function usage.