// Since defined at outermost scope, $greeted may be considered a global variable $greeted = 0; // ------------ // Must use, 'global', keyword to inform functions that $greeted already exists as // a global variable. If this is not done, a local variable of that name is implicitly // defined function howManyGreetings() { global $greeted; return $greeted; } function hello() { global $greeted; $greeted++; echo "high there!, this procedure has been called {$greeted} times\n"; } // ------------ hello(); $greetings = howManyGreetings(); echo "bye there!, there have been {$greetings} greetings so far\n"; |
// Conventionally-defined function together with parameter list function hypotenuse($side1, $side2) { return sqrt(pow($side1, 2) + pow($side2, 2)); } // ---- // Alternative is to define the function without parameter list, then use // 'func_get_arg' to extract arguments function hypotenuse() { // Could check number of arguments passed with: 'func_num_args', which // would be the approach used if dealing with variable number of arguments $side1 = func_get_arg(0); $side2 = func_get_arg(1); return sqrt(pow($side1, 2) + pow($side2, 2)); } // ------------ // 1. Conventional function call $diag = hypotenuse(3, 4); // ------------ // 2. Function call using, 'call_user_func' library routines $funcname = 'hypotenuse'; // a. Pass function name, and variable number of arguments $diag = call_user_func($funcname, 3, 4); // b. Package arguments as array, pass together with function name $args = array(3, 4); $diag = call_user_func_array($funcname, $args); // ---------------------------- $nums = array(1.4, 3.5, 6.7); // ------------ // Pass-by-value function int_all($arr) { return array_map(create_function('$n', 'return (int) $n;'), $arr); } // Pass-by-reference function trunc_em(&$n) { foreach ($n as &$value) $value = (int) $value; } // ------------ // $nums untouched; $ints is new array $ints = int_all($nums); // $nums updated trunc_em($nums); |
// Strictly-speaking, PHP is neither lexically [no environment capture] nor // dynamically [no variable shadowing] scoped. A script in which several // functions have been defined has two, entirely separate, scopes: // // * A 'top-level' scope i.e. everything outside each function // // * A 'local scope' within each function; each function is a self-contained // entity and cannot [via conventional means] access variables outside its // local scope. Accessing a variable that has not been locally defined // serves to define it i.e. accessing a variable assumed to be global // sees a local variable of that name defined // // The way 'global' variables are provided is via a predefined array of // variable names, $GLOBALS [it is one of a special set of variables known // as 'superglobals'; such variables *are* accessable in all scopes]. Each // entry in this array is a 'global' variable name, and may be freely // accessed / updated. A more convenient means of accessing such variables // is via the 'global' keyword: one or more variables within a function is // declared 'global', and those names are then taken to refer to entries // in the $GLOBALS array rather than seeing local variables of that name // accessed or defined function some_func() { // Variables declared within a function are local to that function $variable = 'something'; } // ---------------------------- // Top-level declared variables $name = $argv[1]; $age = $argv[2]; $c = fetch_time(); $condition = 0; // ------------ function run_check() { // The globally-declared variable, '$condition', is not accessable within // the function unless it declared as 'global. Had this not been done then // attempts to access, '$condition', would have seen a local variable // of that name declared and updated. Same applies to other variables global $condition, $name, $age, $c; $condition = 1; // ... } function check_x($x) { $y = 'whatever'; // This function only has access to the parameter, '$x', and the locally // declared variable, '$y'. // Whilst 'run_check' has access to several global variables, the current // function does not. For it to access the global variable, '$condition', // it must be declared 'global' run_check(); global $condition; // 'run_check' will have updated, '$condition', and since it has been // declared 'global' here, it is accessable if ($condition) { ; // ... } } |
// Local scopes are not created in the same way as in Perl [by simply enclosing // within braces]: only via the creation of functions are local scopes created // Doesn't create a local scope; '$myvariable' is created at top-level { $myvariable = 7; } // '$myvariable' is accessable here echo $myvariable . "\n"; // ------------ { $counter = 0; // Local scope created within function, but not within surrounding braces // so: // * '$counter' is actually a top-level variable, so globally accessable // * 'next_counter' has no implict access to '$counter'; must be granted // via 'global' keyword function next_counter() { global $counter; $counter++; } } // ---------------------------- // PHP doesn't, AFAIK, offer an equivalent to Perl's BEGIN block. Similar // behaviour may be obtained by defining a class, and including such code // in its constructor class BEGIN { private $myvariable; function __construct() { $this->myvariable = 5; } function othersub() { echo $this->myvariable . "\n"; } } // ------------ $b = new BEGIN(); $b->othersub(); // ---------------------------- // PHP, like C, supports 'static' local variables, that is, those that upon // first access are initialised, and thence retain their value between function // calls. However, the 'counter' example is better implemented as a class class Counter { private $counter; function __construct($counter_init) { $this->counter = $counter_init; } function next_counter() { $this->counter++; return $this->counter; } function prev_counter() { $this->counter; return $this->counter; } } // ------------ $counter = new Counter(42); echo $counter->next_counter() . "\n"; echo $counter->next_counter() . "\n"; echo $counter->prev_counter() . "\n"; |
// AFAICT there is no means of obtaining the name of the currently executing // function, or, for that matter, perform any stack / activation record, // inspection. It *is* possible to: // // * Obtain a list of the currently-defined functions ['get_defined_functions'] // * Check whether a specific function exists ['function_exists'] // * Use the 'Reflection API' // // So, to solve this problem would seem to require adopting a convention where // a string representing the function name is passed as an argument, or a local // variable [perhaps called, '$name'] is so set [contrived, and of limited use] function whoami() { $name = 'whoami'; echo "I am: {$name}\n"; } // ------------ whoami(); |
// In PHP all items exist as 'memory references' [i.e. non-modifiable pointers], // so when passing an item as a function argument, or returning an item from // a function, it is this 'memory reference' that is passed, and *not* the // contents of that item. Should several references to an item exist [e.g. if // passed to a function then at least two such references would exist in // different scopes] they would all be refering to the same copy of the item. // However, if an attempt is made to alter the item is made, a copy is made // and it is the copy that is altered, leaving the original intact. // // The PHP reference mechanism is used to selectively prevent this behaviour, // and ensure that if a change is made to an item that no copy is made, and that // it is the original item that is changed. Importantly, there is no efficiency // gain from passing function parameters using references if the parameter item // is not altered. // A copy of the item referred to by, '$arr', is made, and altered; original // remains intact function array_by_value($arr) { $arr[0] = 7; echo $arr[0] . "\n"; } // No copy is made; original item referred to by, '$arr', is altered function array_by_ref(&$arr) { $arr[0] = 7; echo $arr[0] . "\n"; } // ------------ $arr = array(1, 2, 3); echo $arr[0] . "\n"; // output: 1 array_by_value($arr); // output: 7 echo $arr[0] . "\n"; // output: 1 $arr = array(1, 2, 3); echo $arr[0] . "\n"; // output: 1 array_by_ref($arr); // output: 7 echo $arr[0] . "\n"; // output: 7 // ---------------------------- // Since, 'add_vecpair', does not attempt to alter either, '$x' or '$y', it makes // no difference whether they are 'passed by value', or 'passed by reference' function add_vecpair($x, $y) { $r = array(); $length = count($x); for($i = 0; $i < $length; $i++) $r[$i] = $x[$i] + $y[$i]; return $r; } // ... count($arr1) == count($arr2) || die("usage: add_vecpair ARR1 ARR2\n"); // 'passed by value' $arr3 = add_vecpair($arr1, $arr2); // 'passed by reference' [also possible to override default 'passed by value' // if required] $arr3 = add_vecpair(&$arr1, &$arr2); |
// PHP can be described as a dynamically typed language because variables serve // as identifiers, and the same variable may refer to data of various types. // As such, the set of arguments passed to a function may vary in type between // calls, as can the type of return value. Where this is likely to occur type // checking should be performed either / both within the function body, and // when obtaining it's return value. As for Perl-style 'return context', I // don't believe it is supported by PHP // Can return any type function mysub() { // ... return 5; // ... return array(5); // ... return '5'; } // Throw away return type [i.e. returns a 'void' type ?] mysub(); // Check return type. Can do via: // * gettype($var) // * is_xxx e.g. is_array($var), is_muneric($var), ... $ret = mysub(); if (is_numeric($ret)) { ; // ... } if (is_array($ret)) { ; // ... } if (is_string($ret)) { ; // ... } |
// PHP doesn't directly support named / keyword parameters, but these can be // easily mimiced using a class of key / value pairs, and passing a variable // number of arguments class KeyedValue { public $key, $value; public function __construct($key, $value) { $this->key = $key; $this->value = $value; } } function the_func() { foreach (func_get_args() as $arg) { printf("Key: %10s|Value:%10s\n", $arg->key, $arg->value); } } // ---- the_func(new KeyedValue('name', 'Bob'), new KeyedValue('age', 36), new KeyedValue('income', 51000)); // ---------------------------- // Alternatively, an associative array of key / value pairs may be constructed. // With the aid of the 'extract' built-in function, the key part of this array // may be intsntiated to a variable name, thus more closely approximating the // behaviour of true named parameters function the_func($var_array) { extract($var_array); if (isset($name)) printf("Name: %s\n", $name); if (isset($age)) printf("Age: %s\n", $age); if (isset($income)) printf("Income: %s\n", $income); } // ---- the_func(array('name' => 'Bob', 'age' => 36, 'income' => 51000)); // ---------------------------- class RaceTime { public $time, $dim; public function __construct($time, $dim) { $this->time = $time; $this->dim = $dim; } } function the_func($var_array) { extract($var_array); if (isset($start_time)) printf("start: %d - %s\n", $start_time->time, $start_time->dim); if (isset($finish_time)) printf("finish: %d - %s\n", $finish_time->time, $finish_time->dim); if (isset($incr_time)) printf("incr: %d - %s\n", $incr_time->time, $incr_time->dim); } // ---- the_func(array('start_time' => new RaceTime(20, 's'), 'finish_time' => new RaceTime(5, 'm'), 'incr_time' => new RaceTime(3, 'm'))); the_func(array('start_time' => new RaceTime(5, 'm'), 'finish_time' => new RaceTime(30, 'm'))); the_func(array('start_time' => new RaceTime(30, 'm'))); |
// The 'list' keyword [looks like a function but is actually a special language // construct] may be used to perform multiple assignments from a numerically // indexed array of values, and offers the added bonus of being able to skip // assignment of one, or more, of those values function func() { return array(3, 6, 9); } // ------------ list($a, $b, $c) = array(6, 7, 8); // Provided 'func' returns an numerically-indexed array, the following // multiple assignment will work list($a, $b, $c) = func(); // Any existing variables no longer wanted would need to be 'unset' unset($b); // As above, but second element of return array discarded list($a,,$c) = func(); // ---------------------------- // Care needed to ensure returned array is numerically-indexed list($dev, $ino,,,$uid) = array_slice(array_values(stat($filename)), 0, 13); |
// Multiple return values are possible via packing a set of values within a // numerically-indexed array and using 'list' to extract them function some_func() { return array(array(1, 2, 3), array('a' => 1, 'b' => 2)); } // ------------ list($arr, $hash) = some_func(); // ---------------------------- function some_func(&$arr, &$hash) { return array($arr, $hash); } // ------------ $arrin = array(1, 2, 3); $hashin = array('a' => 1, 'b' => 2); list($arr, $hash) = some_func($arrin, $hashin); |
// AFAICT, most of the PHP library functions are designed to return some required // value on success, and FALSE on exit. Whilst it is possible to return NULL, or // one of the recognised 'empty' values [e.g. '' or 0 or an empty array etc], // FALSE actually seems to be the preferred means of indicating failure function a_func() { return FALSE; } a_func() || die("Function failed\n"); if (!a_func()) die("Function failed\n"); |
// Whether PHP is seen to support prototyping depends on the accepted // definition of this term: // // * Prototyping along the lines used in Ada, Modula X, and even C / C++, // in which a function's interface is declared separately from its // implementation, is *not* supported // // * Prototyping in which, as part of the function definition, parameter // information must be supplied. In PHP a function definition neither // parameter, nor return type, information needs to be supplied, though // it is usual to see a parameter list supplied [indicates the number, // positional order, and optionally, whether a parameter is passed by // reference; no type information is present]. In short, prototyping in // PHP is optional, and limited function func_with_one_arg($arg1) { ; // ... } function func_with_two_arg($arg1, $arg2) { ; // ... } function func_with_three_arg($arg1, $arg2, $arg3) { ; // ... } // The following may be interpreted as meaning a function accepting no // arguments: function func_with_no_arg() { ; // ... } // whilst the following may mean a function taking zero or more arguments function func_with_no_arg_information() { ; // ... } |
// Unlike in Perl, PHP's 'die' [actually an alias for 'exit'] doesn't throw // an exception, but instead terminates the script, optionally either // returning an integer value to the operating system, or printing a message. // So, the following, does not exhibit the same behaviour as the Perl example die("some message\n"); // Instead, like so many modern languages, PHP implements exception handling // via the 'catch' and 'throw' keywords. Furthermore, a C++ or Java programmer // would find PHP's exception handling facility remarkably similar to those // of their respective languages. A simple, canonical example follows: // Usual to derive new exception classes from the built-in, 'Exception', // class class MyException extends Exception { // ... } // ... try { // ... if ($some_problem_detected) throw new MyException('some message', $some_error_code); // .. } catch (MyException $e) { ; // ... handle the problem ... } // ---------------------------- class FullMoonException extends Exception { // ... } // ... try { // ... if ($some_problem_detected) throw new FullMoonException('...', $full_moon_error_code); // .. } catch (FullMoonException $e) { // ... rethrow the exception - will propagate to higher level ... throw $e; } |
// Please refer to discussion about PHP scope in section two of this chapter. // Briefly, PHP assumes a variable name within a function to be local unless // it has been specifically declared with the, 'global', keyword, in which // case it refers to a variable in the 'superglobal' array, '$GLOBALS'. Thus, // inadvertant variable name shadowing cannot occur since it is it not possible // to use the same name to refer to both a local and a global variable. If // accessing a global variable care should be taken to not accidentally update // it. The techniques used in this section are simply not required. // *** NOT TRANSLATED *** |
// In PHP once a function has been defined it remains defined. In other words, // it cannot be undefined / deleted, nor can that particular function name be // reused to reference another function body. Even the lambda-like functions // created via the 'create_function' built-in, cannot be undefined [they exist // until script termination, thus creating too many of these can actually // exhaust memory !]. However, since the latter can be assigned to variables, // the same variable name can be used to reference difference functions [and // when this is done the reference to the previous function is lost (unless // deliberately saved), though the function itself continues to exist]. // // If, however, all that is needed is a simple function aliasing facility, // then just assign the function name to a variable, and execute using the // variable name // Original function function expand() { echo "expand\n"; } // Prove that function exists echo (function_exists('expand') ? 'yes' : 'no') . "\n"; // Use a variable to alias it $grow = 'expand'; // Call function via original name, and variable, respectively expand(); $grow(); // Remove alias variable unset($grow); // ---------------------------- function fred() { echo "fred\n"; } $barney = 'fred'; $barney(); unset($barney); fred(); // ------------ $fred = create_function('', 'echo "fred\n";'); $barney = $fred; $barney(); unset($barney); $fred(); // ---------------------------- function red($text) { return "<FONT COLOR='red'>$text</FONT>"; } echo red('careful here') . "\n"; // ------------ $colour = 'red'; $$colour = create_function('$text', 'global $colour; return "<FONT COLOR=\'$colour\'>$text</FONT>";'); echo $$colour('careful here') . "\n"; unset($$colour); // ---- $colours = split(' ', 'red blue green yellow orange purple violet'); foreach ($colours as $colour) { $$colour = create_function('$text', 'global $colour; return "<FONT COLOR=\'$colour\'>$text</FONT>";'); } foreach ($colours as $colour) { echo $$colour("Careful with this $colour, James") . "\n"; } foreach ($colours as $colour) { unset($$colour); } |
// PHP sports an AUTOLOAD facility that is quite easy to use, but, AFAICT, is geared // towards the detection of unavailable classes rather than for individual functions. // Here is a rudimentary example: function __autoload($classname) { if (!file_exists($classname)) { // Class file does not exist, so handle situation; in this case, // issue error message, and exit program die("File for class: {$classname} not found - aborting\n"); } else { // Class file exists, so load it require_once $classname; } } // ------------ // Attempt to instantiate object of undefined class new UnknownClassObject(); // Execution continues here if class exists // ... // ---------------------------- // It is also possible to perform [quite extensive] introspection on functions, // variables etc, so it is possible to check whether a function exists before // executing it, thus allowing a non-existent functions to be searched for and // loaded from a source file, or perhaps dynamically defined. An example of what // could be described as a custom autoload facility appears below. $colours = array('red', 'blue', 'green', 'yellow', 'orange', 'purple', 'violet'); foreach ($colours as $colour) { $$colour = create_function('$text', 'global $colour; return "<FONT COLOR=\'$colour\'>$text</FONT>";'); } // Let's add a new colour to the list array_push($colours, 'chartreuse'); foreach ($colours as $colour) { // Checking whether function is defined if (!function_exists($$colour)) { // Doesn't exist, so dynamically define it $$colour = create_function('$text', 'global $colour; return "<FONT COLOR=\'$colour\'>$text</FONT>";'); // Alternatively, if it exists in a source file, 'include' the file: // include 'newcolours.php' } echo $$colour("Careful with this $colour, James") . "\n"; } foreach ($colours as $colour) unset($$colour); |
// *** Warning *** Whilst PHP *does* allow functions to be defined within other // functions it needs to be clearly understood that these 'inner' functions: // * Do not exist until the outer function is called a first time, at which time // they then remain defined // * Are global in scope, so are accessable outside the function by their name; // the fact that they are nested within another function has, AFAICT, no bearing // on name resolution // * Do not form a closure: the inner function is merely 'parked' within the // outer function, and has no implicit access to the outer function's variables // or other inner functions function outer($arg) { $x = $arg + 35; function inner() { return $x * 19; } // *** wrong *** 'inner' returns 0 * 19, not ($arg + 35) * 19 return $x + inner(); } // ---------------------------- function outer($arg) { $x = $arg + 35; // No implicit access to outer function scope; any required data must be // explicity passed function inner($x) { return $x * 19; } return $x + inner($x); } // ------------ // Equivalent to previously-shown code function inner($x) { return $x * 19; } function outer($arg) { $x = $arg + 35; return $x + inner($x); } |
// @@INCOMPLETE@@
// @@INCOMPLETE@@
|