There seems to be a lot of confusion around the different names given to what are all effectively sub-routines. Some people erroneously believe they are all essentially the same thing, and technically, whilst they are all simply different words for sub-routines, understanding the subtle differences between each can help you write better code.
Sub-routines
Before you can accurately define types of sub-routine, you need to know what a sub-routine is. It is actually quite simple to define: A sub-routine is a repeatable piece of procedural code you can call by name.
Functions
A function is essentially a sub-routine that returns one or more values. A function should calculate its return value based on its input; It should provide an answer about its arguments, or compute a new value based on its arguments. Here are some examples of functions in PHP:
<?php
function isAGreaterThanB($a, $b)
{
return $a > $b;
}
This function provides an answer about its arguments. Namely, is $a greater than $b?
<?php
function multiply($a, $b)
{
return $a * $b;
}
This function calculates a new value based on the values of its inputs, specifically, multiplying $a and $b together and returning the result.
The above examples are considered Pure Functions because they don’t rely on or modify anything outside of their own scope, and they don’t cause any side-effects; that means they don’t write any files, produce any output etc. Given the same input, a pure function will always return the same value.
Procedures
A procedure is a sub-routine whose point is side-effects, not a return value that represents a computed result for the caller. It may return nothing, or only a minimal success flag; the work you care about is the effect (writing a file, printing, mutating state by reference). Here are two procedures in PHP:
<?php
function logMessage($message, $level = 'debug')
{
file_put_contents("{$level}.log", $message, FILE_APPEND);
}
Here the side-effect is writing a message to a log file. There is no return value for the caller to use - that keeps the routine in the “procedure” bucket rather than the “function” bucket in the sense used above.
<?php
function multiply(&$a, $b)
{
$a = $a * $b;
}
This is an example of a procedure that mutates (modifies) its input value. It is almost identical to our multiply function above, however instead of returning a new value, we pass $a by reference and assign it a new value.
Methods
I have deliberately left methods for last, because a method is really a function or procedure that is executed in the context of an object. That means there are two types of methods: A function method and a procedure method.
A function method calculates a new value based on the values of its inputs and/or the scope of the object instance it’s being executed on.
Here are the above function examples in an object context:
<?php
class Integer
{
protected $a;
public function __construct($a)
{
$this->a = $a;
}
public function isGreaterThan($b)
{
return $this->a > $b;
}
public function multiply($b)
{
return $this->a * $b;
}
}
Following this logically, a procedure method is a procedure that produces side-effects that can include modifying the state of the object instance it’s being executed on.
Here are the above procedure examples in an object context:
<?php
class Logger
{
protected $level;
public function __construct($level = 'debug')
{
$this->level = $level;
}
public function logMessage($message)
{
file_put_contents("{$this->level}.log", $message, FILE_APPEND);
}
}
and our multiply example:
<?php
class Integer
{
protected $a;
public function __construct($a)
{
$this->a = $a;
}
public function multiply($b)
{
$this->a = $this->a * $b;
}
}
Naming the Sub-routine You Mean
It’s sometimes easy to forget these distinctions when writing PHP, as everything starts with the function keyword, however, if you ask yourself: “What type of sub-routine do I really want here?” you will hopefully find that you start to make better decisions and will write more logical and maintainable code.