The author selected Open Sourcing Mental Illness Ltd to receive a donation as part of the Write for DOnations program.
When you find yourself copying and pasting blocks of code to reuse in different sections of your program, you’re ready to write a function. If you need to interact with the contents of a variable — change the case, find the length, trim, or sort — there is probably a built-in function for that. Functions are self-contained, reusable blocks of code that accomplish a specific task. They provide modularity and are one method of code reuse.
A function sits dormant until it is called. When a function is called, it performs the task defined in the function. A function typically takes input, performs an action, and returns the resulting output. The action that takes place inside of a function builds a relationship between the input and the output. The three main parts to think about when interacting with a function are the input, the relationship, and the output. Even when the input is not explicit, there is usually some implicit data relating to the code’s environment: date, time, system, file, line number, and so on.
In this tutorial, you will work with all the different pieces of a function, so that you can use functions in the best way to solve your challenge. We will start by writing user-defined functions. These functions will clarify the difference between arguments and parameters, pass variables by value and reference, set default values, and use type declaration to specify the acceptable types for input and output. By understanding scope, you’ll be able to manipulate the specific section of your code that needs to change while leaving other parts untouched. Once you understand the different aspects of a function and how they are affected by scope, you’ll be able to read and understand the documentation for the many built-in functions PHP provides right out of the box. You’ll use built-in functions to perform common programming tasks effortlessly.
Having the same code written in multiple places makes reading and understanding a project difficult. One of the “core tenets” or foundational principles of programming is “don’t repeat yourself,” often referred to as DRY programming. Variables keep us from writing the same data over and over. Functions keep us from writing the same action over and over.
When writing a function, the structure of the function must be built first. This block of code stays dormant until it is called or invoked. A function is created using the function
keyword plus a chosen name followed by parentheses. The body of the function is surrounded by curly braces and is the code that performs the action:
<?php
function hello() {
echo "Hello World!";
}
This function, named hello
, stays dormant, meaning it is doing nothing and the file will have no output until the function is called. To call a function, the name is used, followed by parentheses:
<?php
function hello() {
echo "Hello World!";
}
hello();
The call to the function is what causes the function to performs its action. In this case, it displays the output:
OutputHello World!
This simple example is not a good use of a function because there isn’t any input, which means there is no relationship, and only the output. Data used in multiple places should be stored in a variable instead, like this:
<?php
$hello = "Hello World!";
echo $hello;
These two lines provide the same result as the function and call:
OutputHello World!
Let’s expand the example function to accept input.
To make a hello
function that builds a relationship between input and output, the function should accept some input. This input is accepted as a variable between the parentheses of the function definition. This variable can then be used within the body of the function:
<?php
function hello($name) {
echo "Hello ".$name;
}
hello();
The function hello
now accepts one parameter, stored in the variable $name
. This variable is then used in the body of the function by concatenating the string “Hello” to the $name
variable using the .
string concatenation operator. Now the function requires that an input be passed when the function is called, but the input was not supplied, causing PHP to respond with an error:
OutputWarning: Uncaught ArgumentCountError: Too few arguments to function hello(), 0 passed in php shell code on line 1 and exactly 1 expected
When the hello
function is called, it needs an argument to use as the input, which sets the value of $name
. The term parameter is used when it is part of the function definition, and the term argument is used when it is part of the function call. They both refer to the input of a function. To provide input to the function, we pass an argument between the parentheses of the function call:
<?php
function hello($name) {
echo "Hello ".$name;
}
hello("Sammy");
The function can now use that string input to create the output of the greeting. The greeting changes based on the input passed when calling the function:
OutputHello Sammy
These basic arguments are called “positional arguments” because they must be passed in the same order as they are defined by the function. PHP does not limit the number of parameters separated by a comma, but a function should only execute a single action. We’ll look at connecting functions a little later.
By default, any parameter defined for the function requires that an argument be passed with the function call. This makes our output unique instead of just calling the same data from a variable. Sometimes we do not need a unique value for all of our parameters; instead, we can use a specific value if the function call does not include that argument. To specify a parameter as optional, give the parameter a default value. This value is used when an argument is not passed. It can be any valid value, including null
:
<?php
function hello($name, $greeting = "Hello") {
echo $greeting." ".$name;
}
hello("Sammy", "Bonjour");
There is now a second parameter named $greeting
. This variable is then used in the body of the function to change the value of greeting portion of the output:
OutputBonjour Sammy
The first parameter $name
does not have a default value, so an argument must be included with each call to the function. Because the second parameter has a default value, the function does not require that a second argument be passed:
<?php
function hello($name, $greeting = "Hello") {
echo $greeting." ".$name;
}
hello("Sammy");
When only a single argument is passed, the value of the $greeting
variable will be set to the default value of “Hello”:
OutputHello Sammy
When required parameters are used together with optional parameters in the same function, all required parameters must come before all optional parameters.
When a variable is completely optional and does not require a value at all, the default value can be defined as null
. The null
value holds space for a value without adding an additional value itself:
<?php
function hello($name, $greeting = "Hello", $punctuation = null) {
echo $greeting." ".$name.$punctuation;
}
hello("Sammy");
The variable is has been “declared” so there will be no error when the body of the function attempts to access the variable, and yet, the variable is considered “not set” so no additional output will be added:
OutputHello Sammy
We may not always want to print the output of a function. To build more flexibility into a function, it is common practice to return the output value to the code that initially made the call to the function. Use the keyword return
in place of the echo
keyword:
<?php
function hello($name, $greeting = "Hello", $punctuation = null) {
return $greeting." ".$name.$punctuation;
}
hello("Sammy");
echo hello("Sammy");
In the first call to the hello
function, nothing is done with the returned value, making it look like nothing happened, even though the code in the function was executed. In the second call to the hello
function, the keyword echo
is used before the function call, telling PHP to print the results of the returned output:
OutputHello Sammy
We do not always have to print the result. These results could also be stored in a variable or used as the input for another function.
Functions are blocks of reusable code. Most code that can be written outside a function can also be written inside a function. Just because you can, though, doesn’t mean you should. You should never define a function within another function, except for certain very advanced cases. You will, however, often call one function from within another function. To specify the format of the outputted greeting, you can use some built-in functions:
<?php
function hello($name, $greeting = "Hello", $punctuation = null) {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello("SAMMY");
The first line within the hello
function now applies some formatting to the value of $name
. First, it converts the string to lower case. The strtolower
function returns a value which is then immediately used in a second function, ucwords
. This converts the first letter of each word to upper case. The hello
function now provides a consistently formatted output, regardless of the input received:
OutputHello Sammy
PHP is a loosely typed language, meaning it will try to convert the value it is given into the appropriate type for each situation. This can create surprising and sometimes undesirable results. A name should be a string
, but what happens if we pass an integer
instead:
<?php
function hello($name, $greeting = "Hello", $punctuation = null) {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello(123);
PHP doesn’t have any problem type juggling, which converts the integer 123
into the string "123"
, then proceeding with the function as before:
OutputHello 123
That’s valid output, but this is probably not the desired result either. Let’s look at what happens when we pass an array. The stringtolower
function will throw a different error; for the purpose of this demonstration the double slashes will be added to the beginning of that line to make it a comment, which skips execution of that code:
<?php
function hello($name, $greeting = "Hello", $punctuation = null) {
// $name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello(["Sammy", "World"]);
This time the code provides a notice, but it’s not very clear. Also, depending on how the system is set up, notices could be turned off or ignored because they don’t stop the execution of the code. We may not realize there is a problem, or we may have trouble figuring out where the problem occurs. The output looks as if the string “Array” had been passed to the function:
OutputNotice: Array to string conversion in php shell code on line 2
Hello Array
Developers spend far more time reading and debugging than actually writing code, so making any code we write as easy as possible to both read and debug will save time in the long run. Starting in PHP 5 and with more robust support in PHP 7, we can now declare or specify the acceptable value types of both the input and output of a function.
Parameter Type Declarations ensure that the value of the argument, used when the function is called, matches the specified type; otherwise, a TypeError is thrown. Let’s add the string type to our $name
parameter and try the array
arguments again. This time we can keep the stringtolower
line because that code will not be reached:
<?php
function hello(string $name, $greeting = "Hello", $punctuation = null) {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello(["Sammy", "World"]);
This time the function call causes an exception, which stops the execution of the code, so we don’t get valid looking output. The message is also more clear as to what the error actually exists:
OutputWarning: Uncaught TypeError: Argument 1 passed to hello() must be of the type string, array given
If the argument is changed back to an integer instead of an array, it is reasonable to expect an error since the parameter should only accept string values. Let’s give it a try:
<?php
function hello(string $name, $greeting = "Hello", $punctuation = null) {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello(123);
Instead of throwing an error, once again the integer
is converted to a string
without a problem, and the function is executed:
OutputHello 123
This has to do with PHP being a loosely typed language. PHP still performs type juggling on the integer to convert it to a string, then continues execution.
Preventing PHP from performing “type juggling” can be helpful in making the code easier to read and debug. PHP provides a way for us to declare strict types on a per-file basis. Add the declaration to the top of any file that should enforce strict typing:
<?php
declare(strict_types=1);
function hello(string $name, $greeting = "Hello", $punctuation = null) {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello(123);
After adding the declaration line to the top of the file, PHP will produce an error when an integer
is passed instead of a string
:
OutputPHP Fatal error: Uncaught TypeError: Argument 1 passed to hello() must be of the type string, int given.
Note: The interactive shell does not allow you to declare strict_types
.
We can declare the second parameter of the hello
function as a string as well. Because it has a default value, an argument is not required, but if an argument is passed, it must be of the type string
. The third parameter is also a string with a default value. A null
value would also work in this function:
<?php
declare(strict_types=1);
function hello(string $name, string $greeting = "Hello", string $punctuation = null) {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello("SAMMY", "Hola", null);
Because the default value of the third parameter is set to null
, an argument of null
is allowed, as well as a string:
OutputHola Sammy
However, when the default value of a parameter is declared as a string, it will no longer accept a null
value:
<?php
declare(strict_types=1);
function hello(string $name, string $greeting = "Hello", string $punctuation = "!") {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello("SAMMY", "Hola", null);
Instead a TypeError is given:
OutputWarning: Uncaught TypeError: hello(): Argument #3 ($punctuation) must be of type string, null given
When the default value of a parameter is something besides null
but null
is also a valid input, PHP provides additional syntax to define that relationship by adding a question mark before the type declaration:
<?php
declare(strict_types=1);
function hello(string $name, string $greeting = "Hello", ?string $punctuation = null) {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello("SAMMY", "Hola", null);
Now the function will accept a third argument of null
and continue as previously processed:
OutputHola Sammy!
The question mark prefix works with all type declarations, not just strings, and does not require a default value (such as ?int $age
).
In the same way that a parameter type can be declared, the function’s return type can also be declared. To declare the return type of a function, add a colon along with the type after the closing parenthesis of the function definition:
<?php
declare(strict_types=1);
function hello(string $name, string $greeting = "Hello", ?string $punctuation = "!"): ?int {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello("SAMMY");
Notice that the return type declared is either an integer or a null
value. This declared type will not match their return value, and an exception is thrown:
OutputWarning: Uncaught TypeError: hello(): Return value must be of type ?int, string returned
Let’s update the return type to match the string
type returned:
<?php
declare(strict_types=1);
function hello(string $name, string $greeting = "Hello", ?string $punctuation = "!"): string {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello("SAMMY");
This gives us that output we have seen before:
OutputHello Sammy!
Sometimes a function performs an action that does not result in a return value; the var_dump
function is one example. If we go back to our very first version of the hello
function, we can add a void
return type:
<?php
function hello(): void {
echo "Hello World!";
}
$greeting = hello();
var_dump($greeting);
This declares that the function does not return a value. When the function is called and applied to a variable, the output is displayed because the echo
keyword is used within the function itself. By using var_dump
on the $greeting
variable, we can see that no value was returned, and the $greeting
variable is set to a null
value:
OutputHello World!
NULL
The void
type is used more often when manipulating the variable itself and not just a value. For that, we need to understand scope.
Scope is not just a technical term; it applies to anything in which we measure the “extent of the area or subject matter that something deals with or to which it is relevant.” For example, the scope of the Atlantic Ocean extends from the Americas on the west to Africa and Europe on the east, and from the Arctic Ocean in the north to the Indian Ocean in the south. If water is taken from the Atlantic Ocean and dumped into the Indian Ocean, the water becomes part of the Indian Ocean and no longer has any affect on the Atlantic Ocean. The scope of the Atlantic Ocean has not changed.
In programming, the scope refers to how different pieces of code have access to each other. There are several different areas of scope in an application. For the purpose of functions, we are going to examine two of them—the global scope of the file and the function scope.
By default, code in the global scope may call any function that the file knows about, but code in the global scope cannot reach directly into a function to access data. It must rely on the value returned by the function. By default, code in the function scope accepts input from the global scope and returns output to the global scope. Code in the function scope does not reach directly out to the global scope.
Because PHP treats these two scopes as separate entities, when a variable is passed into a function, the “value” of the variable is passed and not the variable itself. Let’s set a global variable to see what this means for the code:
<?php
declare(strict_types=1);
$name = "SAMMY";
function hello(string $name, string $greeting = "Hello", ?string $punctuation = "!"): string {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello($name);
echo $name;
Instead of passing a string directly, we now pass a string variable. The variable works in the same way as passing a string directly because only the variable’s value is used. When the value of the $name
variable is changed within the function, only the $name
variable within the function is affected. The $name
variable in the global scope is a separate variable:
OutputHello Sammy!
SAMMY
The output of echo hello($name)
is “Hello Sammy”, because that is the value that is returned by the function, which was supplied with the $name
argument. Even after the function is called, the value of the $name
variable is not changed in the global scope, so the output of echo $name;
is “SAMMY”, because the global variable remains set to “SAMMY”.
One big difference between the global scope and the function scope is that you can allow a function to reach out to the global scope to retrieve data, but you cannot retrieve data from a function directly in the global scope. The global
keyword reaches out to the global scope:
<?php
declare(strict_types=1);
$name = "SAMMY";
function hello(string $greeting = "Hello", ?string $punctuation = "!"): string {
global $name;
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello();
echo $name;
The variable $name
is set in the global scope, but instead of passing the value to the function, the first line of the function now reaches out to grab the variable from the global scope. The global
keyword grabs the variable itself and not just its value. The formatting done within the function is applied to the global scope of the variable, which also changes the value of the variable outside the function:
OutputHello Sammy!
Sammy
Using global variables is considered bad practice because it is hard to track down where the value of a variable may be modified. When modifying the variable is desired, a better approach is to pass a reference to the variable.
PHP reads arguments as values. It doesn’t care if we pass a variable containing the string “SAMMY” or the string itself. Only the value is used. Passing by value keeps the global scope and the function scope from overlapping. Because a function may return a value, we can use that value to set any variable in a separate scope, even one also used as an argument. By writing code in this way, the handling of a variable is kept clearly in the original scope. The previous examples have changed the value of the $name
variable by overriding the value of the variable to match the return value of some built-in functions. Let’s take a closer look at the ucwords
and strtolower
formatting:
$name = "SAMMY";
$name = ucwords(strtolower($name));
echo $name;
This new value of $name
now has the new formatting:
OutputSammy
While a function can only return a single value, a function may affect more then one variable. In the examples we have been writing, the hello
function uses multiple variables but it does not change the value of those variables in the global scope. PHP provides a way to interact directly with a variable from another scope, by passing a reference to the variable itself, rather than just its value. Arguments are passed by reference by adding an ampersand (&
) to any parameter:
<?php
declare(strict_types=1);
$name = "SAMMY";
function hello(string &$name, string $greeting = "Hello", ?string $punctuation = "!"): void {
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello($name);
echo $name;
When the $name
variable is passed by reference it changes the value of that variable in the global scope. After the function is called the $name
retains the formatting:
OutputHello Sammy!
Sammy
When passing by reference, the argument must be a variable. A value is no longer accepted:
<?php
declare(strict_types=1);
function hello(string &$name, string $greeting = "Hello", ?string $punctuation = "!"): string {
global $name;
$name = ucwords(strtolower($name));
return $greeting." ".$name.$punctuation;
}
echo hello("SAMMY");
Since there is no variable to reference, an error is given:
OutputWarning: Uncaught Error: hello(): Argument #1 ($name) cannot be passed by reference
PHP also includes many built-in functions which may be used to perform any number of common tasks in your application. These are actual functions that come pre-packaged with the language. Like all functions, these built-in functions do require the use of parenthesis.
When you read the documentation for the function definition of a built-in function, it will look very much like the user-defined functions we wrote, which included Type Declarations.
Variable handling functions provide details about a variable such as its type or value. One of the most common variable handling functions is var_dump
:
<?php
var_dump("Hello World!");
This function gives the details of the values it is given, including their type and size. The string “Hello World!” contains 12 characters:
Outputstring(12) "Hello World!"
PHP also provides additional functions for interacting with the value of specific types variables.
PHP includes specific functions for manipulating strings. Some of these functions give details about the string, such as strlen
:
echo strlen("How many characters are contained in this string?");
This function takes a string argument and returns an integer with the number of characters with the given string:
Output49
Other string functions perform an action on the string itself. They may add, remove, change characters, or convert the entire string. Such is the case with the explode
function, which splits apart a string based on the dividing characters supplied to the function, and returns an array. To turn a comma-separated list of items into an array, the explode
function could be used as follows:
<?php
$groceries = explode(",", "Milk, bread, eggs,fish.");
var_dump($groceries);
This first argument, a comma ","
, tells the function to add a new array element when it reaches the comma character. The second argument is the string to use when creating the array. The space is used as the dividing character and is not returned in the resulting array. All other characters are returned as an array element grouped by this dividing character. In this case, it creates an array of each individual grocery item:
Outputarray(4) {
[0]=>
string(4) "Milk"
[1]=>
string(6) " bread"
[2]=>
string(5) " eggs"
[3]=>
string(5) "fish."
}
Each character is kept precisely as it was in the original string, including case, spaces, and special characters, such as the period. Since the function splits a string by a string and not just a single character, we can use a longer string, such as a comma followed by a space ", "
:
<?php
$groceries = explode(", ", "Milk, bread, eggs,fish.");
var_dump($groceries);
This kept our array items from having a space at the beginning, but it didn’t separate the eggs from the fish because there is only a comma and not a comma followed by a space:
Outputarray(3) {
[0]=>
string(4) "Milk"
[1]=>
string(5) "bread"
[2]=>
string(10) "eggs,fish."
}
Like most challenges, there are many solutions to the above space issue. We’ll take a look at one while we start to see the power of functions with array functions.
Array functions perform different actions on an array, such as sorting the elements, finding an element, returning the keys, combining arrays, performing calculations on each item, and many more.
There is a built-in count
function to get the number of items in an array:
<?php
$groceries = explode(", ", "Milk, bread, eggs,fish.");
var_dump($groceries);
echo count($groceries);
The count
function takes an array as an argument and returns an integer containing the number of items:
Outputarray(3) {
[0]=>
string(4) "Milk"
[1]=>
string(5) "bread"
[2]=>
string(10) "eggs,fish."
}
3
This shows that the “count” of 3 matches up with the number of items in the array: array(3)
. Now let’s take a look at how we can use an array function to build our grocery list with all four items and no beginning spaces:
<?php
$groceries = explode(",", "Milk, bread, eggs,fish.");
$groceries = array_map("trim", $groceries);
var_dump($groceries);
echo count($groceries);
First, the explode
function on line 1 uses a comma without the space. Then a second line is added to use an array function called array_map
. This function applies the function that is passed as the first argument to each item in the array, which is passed as the second argument. The passed function can be a user-defined function or a built-in function. In this case, it uses the built-in trim
function, which “trims” whitespace from the beginning and end of a variable:
Outputarray(4) {
[0]=>
string(4) "Milk"
[1]=>
string(5) "bread"
[2]=>
string(4) "eggs"
[3]=>
string(5) "fish."
}
4
The explode
function converted the string of grocery items into an array, but what if the grocery list started as an array that we want to print as a string or a list? The implode
function can be used to convert an array into a string:
<?php
$groceries = array("milk", "bread", "eggs", "fish");
echo "Grocery List: " . implode(", ", $groceries);
The $groceries
variable is set (using the array
language construct) to build an array of grocery items. The next line concatenates the string "Grocery List: " with a string of grocery items, separated by a comma and space ", "
. The comma and space only separate the items themselves; they are not added to the beginning or end of the string:
OutputGrocery List: milk, bread, eggs, fish
Some functions perform the action on the original array itself by accepting the argument by reference. These functions do not return the resulting value. To sort the grocery list, we can add a line with the sort
function:
<?php
$groceries = array("milk", "bread", "eggs", "fish");
$sorted = sort($groceries);
var_dump($sorted);
var_dump($groceries);
The sort
is applied to the original array itself and is not reassigned to the new $sorted
variable because the sort
function does not return a result of the newly sorted array; instead, it always returns the boolean true
. So the $sorted
variable has a boolean value of true
while the original $groceries
array has been sorted alphabetically by value:
Outputbool(true)
array(4) {
[0]=>
string(5) "bread"
[1]=>
string(4) "eggs"
[2]=>
string(4) "fish"
[3]=>
string(4) "milk"
}
Since the assignment did not benefit us, we can change the line to only the sort itself:
<?php
$groceries = array("milk", "bread", "eggs", "fish");
sort($groceries);
echo "Grocery List: " . implode(", ", $groceries);
This will then allow us to return the array as a sorted list:
OutputGrocery List: bread, eggs, fish, milk
With more than 1,000 built-in functions within the standard PHP distribution, you can find a function for many common programming tasks. Besides the function types we looked at previously, there are other types, such as Math Functions, Internationalization Functions, and other Miscellaneous Functions. Optionally, additional functions require specific PHP extensions or modules compiled with PHP. Before writing a user-defined function, take a look to see if PHP has a built-in function that meets your requirements.
Language constructs are similar to built-in functions, but they are hard-coded into the PHP language. While they may look like a function, they may not always follow the same syntax or rules. As an example, the echo
keyword is a language construct that does not require parentheses:
<?php
echo "Hello World!";
echo("Hello World!");
Either one works and provides the same result:
OutputHello World!
Hello World!
Language constructs make up how the language is built and provide essential functionality. For example, whenever you try to access a variable that doesn’t exist, you get an error. To test whether a variable exists before you access it, you need to consult isset
or empty
:
<?php
if (empty($username)) {
echo "Guest";
}
If empty
were a regular function, you’d get a warning since you are attempting to access the $username
variable to pass it into empty
. However, since empty
is a language construct, this works without throwing a warning.
Other examples of language constructs that could be mistaken for functions are array()
, list()
and each()
. These language constructs, along with built-in functions, provide solutions to many common programming tasks. When these components are not enough, user defined functions provide a tool to create reusable blocks of code for a project.
Functions provide a powerful way to write code that is modular and reusable. Functions may come from the standard PHP distribution or additional extensions compiled with PHP. Individual developers may also write functions for a single project or share them across multiple projects. When you need an action performed against more than one item or location, functions are the tool.
Now that you have a good base for understanding functions, take a look at the many built-in functions provided by PHP. Not only will it give you an idea of what the language can do, but it will also show you some of the common challenges that developers have faced in the past and how they have been solved.
To learn more about PHP, check out the rest of our How To Code in PHP series.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
PHP is a popular server scripting language known for creating dynamic and interactive web pages.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.