Enclosing brackets. Anything not inside the brackets will be rendered as plain text.
<?php
//php here
?>
plain text here
You can have as many php sections in a file as you want.
You cannot nest php sections.
<?php
<?php
//invalid
?>
?>
All php sections in a file are interpreted as a single program. For instance, this is a valid if/else.
<?php if(x > 0) { ?>
text if x is positive
<?php } else { ?>
text otherwise
<?php } ?>
Single line
//comments
Multi-line
/*
comments
*/
Echo will output text to standard-out.
echo "Hey";
Use . (period) for string concatenation.
echo "Hey, " . $username;
Print outputs to standard-out.
print("text");
print_r($full_array);
All variable names must start with a "$" (dollar sign).
$x = 5;
That's it for declaring a variable, you don't need a "var" keyword or a type or anything.
Check if a variable has a value:
if(isset($x))
{
}
ignore all this, haven't gotten it working anyway
<?php namespace Widget;
class Dongle {
static function doThing() {
echo 'did it';
}
}
?>
<?php namespace MyProject\Utils;
function sum(a, b) {
return a + b;
}
?>
<?php
use Widget as W;
use MyProject\Utils; //defaults to "use MyProject\Utils as Utils;"
\W\Dongle::doThing(); //referencing a method in a class
$result = Utils\sum(1, 2); //referencing a function
?>
<?php
use function MyProject\Utils\sum;
$result = sum(1, 2); //referencing a function
?>
Include text of another file in this one, to use functions/etc.
Note that relative paths are relative to the place you run "php" from.
include("filename.php");
include("../filename.php");
include("/absolute/system/path/filename.php");
Includes are transitive, meaning that if A.php includes B.php, and B.php includes C.php, then elements of C can be used by A.
end of line character(s)
$output = str_replace(PHP_EOL, "<br/>", $input);
Magic constants are provided automatically by the php engine, provided the correct extensions are installed.
$current_line_number_in_file = __LINE__;
$current_path_and_filename = __FILE__;
$current_directory = __DIR__;
//or
$current_directory = dirname(__FILE__);
$current_function_name = __FUNCTION__;
$current_class_name_with_namespace = __CLASS__;
$current_trait_name_with_namespace = __TRAIT__;
$current_class_method_name = __METHOD__;
$current_namespace = __NAMESPACE__;
Variables that might be provided by the web server:
The full URL, including query string:
$_SERVER['REQUEST_URI']
Just the path part of the URL, excluding the home address (such as www.site.com) and excluding the query string.
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
. = string concatenation
$name = $first . $last;
strlen = string length
$characterCount = strlen("apple");
trim = remove leading and trailing white-space characters from a string
$trimmedString = trim($string);
contains
if(strpos($string, $substring) !== false)
{
echo "string contains substring";
}
explode
$array = explode("/", $filename);
split string into characters
$chars = str_split($string); //H e l l o W o r l d
$chars = str_split($string, 3); //Hel loW orl d
implode
$string = implode(",", $array);
replace
$output = str_replace("old", "new", $input);
strtoupper = convert entire string to upper-case
$upperCase = strtoupper($string);
replace
$output = str_replace("old", "new", "input string");
replace the first instance of "old"
function replace_first_instance($old, $new, $text)
{
$i = strpos($text, $old);
if ($i !== false) {
$text = substr_replace($text, $new, $i, strlen($old));
}
return $text;
}
to replace the last instance, use "strrpos" instead of "strpos"
ord = convert a character to its ascii code (integer)
$asciiCode = ord($char);
chr = convert an ascii code (integer) to a character
$char = chr($asciiCode);
convert string to integer
if(ctype_digit($string)) {
return intval($string);
}
Init an array
$a = array();
Print full array:
print_r($myArray);
An associative array can be called a hash or a dictionary in other languages. Each element is a key/value pair.
$array = array( "key1"=>"value1", "key2"=>"value2" );
$value1 = $array["key1"];
contains key
if (array_key_exists('key', $search_array)) {
//operation
}
append
$array[$key] = $value;
$array += array($key=>$value);
note that neither update is made in-place
if this array was already an element in a parent array, the parent array will still see the old version of the array
add to array
array_push($array, $element); //add to the end
aray_unshift($array, $element); //add to the beginning
remove from array
$element = array_pop($array); //get from the end
$element = aray_shift($array); //get from the beginning
count = array length
$elementCount = count($array);
contains
$isInArray = in_array($element, $array);
sort (as of php 5 at least)
sort($array); //sort arrays in ascending order
rsort($array); //sort arrays in descending order
natsort($array); //sort alphanumeric strings the way a human would
usort($array); //sort arrays with a user-defined function
asort($array); //sort associative arrays in ascending order, according to the value
ksort($array); //sort associative arrays in ascending order, according to the key
arsort($array); //sort associative arrays in descending order, according to the value
krsort($array); //sort associative arrays in descending order, according to the key
Note that php sorts considers underscore "_" character to be after alphabetic characters, which is not the usual directory sorting order. Possible solution here:
function cmp($a, $b) {
$aTemp = str_replace('_', '0', $a);
$bTemp = str_replace('_', '0', $b);
return strcmp($aTemp,$bTemp);
}
usort($arr, "cmp");
map: apply a function to each element and return an array of the results
$output_array = array_map('function_name', $input_array);
specify the function name by the first string parameter
explode = split a string into an array of string, based on the delimiter
$array = explode(",", $string);
sets array's internal pointer to the first element and returns it
$firstElement = reset($array);
sets array's internal pointer to the last element and returns it
$lastElement = end($array);
Basic:
class myClass
{
function myMethod()
{
echo "Test";
}
}
$x = new myClass;
$x->myMethod();
Store all constructor arguments as local fields:
class myClass
{
public function __construct(array $arguments = array())
{
if (!empty($arguments))
{
foreach ($arguments as $property => $argument)
{
$this->{$property} = $argument;
}
}
}
}
class myClass
{
public $myField = "default";
}
$x = new myClass;
$x->{'myField'} = "A";
$fieldName = 'myField';
$x->$fieldName = "B";
Function arguments are required, unless you set a default value.
class myClass
{
public static function myStaticMethod()
{
echo "Static";
}
}
myClass::myStaticMethod();
square root
$x = sqrt(36);
Rand will return a random integer, with the min and max inclusive.
$coinFlip = rand(0,1);
$result = myFunction($a, $b);
function myFunction($a, $b)
{
return $a * $b;
}
To access outer variable within a function, either pass them in as arguments, or declare their use within the function.
$x = 5;
function myFunction()
{
global $x;
$x = 6;
}
"continue" and "break" work like other languages.
In curly bracket format, "elseif" and "else if" can both be used.
if($x > 0)
{
}
else if($x < 0)
{
}
else
{
}
if($x > 0)
{
}
elseif($x < 0)
{
}
else
{
}
When using colon format, only "elseif" is valid.
if($x > 0):
//something
elseif($x < 0):
//something else
else:
//or this
endif;
for($x = 0; $x <= 10; $x++)
{
echo $x;
}
foreach($array as $element)
{
}
"argv" is an array of command line arguments.
//command line
php myScript.php a b c
//in script
$x = $argv[0]; //"myScript.php"
Xy = $argv[1]; //"a"
Get 0 or 1 for if there is at least one match in the string
$result = preg_match('/(foo)(bar)(baz)/', 'foobarbaz');
Get details of matches within string
$matches = ""
preg_match('/a(.*)d/', 'abcd', $matches);
print_r($matches);
//$matches is set to an array
//first element is the full substring that matched the pattern
//then each substring inside parentheses in the regex is returned
/*
Array
(
[0] => abcd
[1] => bc
)
*/
Get number of times a match is found in the string
echo preg_match_all('/(foo)(bar)(baz)/', 'foobarbaz');
Get details of matches within string
$matches = ""
preg_match_all('/a(.*)d/', 'abcd', $matches);
print_r($matches);
//$matches is set to an array
//first element is the full substring that matched the pattern
//then each substring inside parentheses in the regex is returned
/*
Array
(
[0] => Array( [0] => abcd )
[1] => Array( [0] => bc )
)
*/
Replace matches with new substring
$result = preg_replace('/search_text/i', 'replacement_text', 'original_text');
"//i" case-insensitive
"//m" the ^ and $ characters can match based on new-lines instead of just the entire string
"//s" dots (.) can match new-line characters
"//x" white-space (or anything between # symbols) in the pattern is ignored unless it is escaped or within a character class
"//A" will only match starting at the beginning of the string
"//U" quantifiers will not be greedy, unless they are immediately followed by a ?
"//sU" can match across new-lines, but will take the shortest matches possible - what you think of as "multi-line" behavior
"\" use backslash to escape special characters, so they are treated as normal characters
"\d" matches any digit character
"\w" matches any ASCII character (latin alphabet, digits, underscore)
"\s" matches any whitespace character (space, tab, new line, carriage return, etc)
"\D" is the inverse set of "\d"
"\W" is the inverse set of "\w"
"\S" is the inverse set of "\s"
"[abc]" for creating a custom character class (replace "abc" with whatever), matches a single char from the classes
"^" means beginning of line (this character is called "circumflex")
"$" means end of line
$file = fopen($fileName, 'w');
fwrite($file, $fullText);
fclose($file);
$pages = glob('../pages/*.txt');
//result is array of "../pages/a.txt", "../pages/b.txt" etc
Include other php or text files in the current script.
include "otherFile.php";
The path to the otherFile can be absolute, based on the "include_path", or relative based on the current script's directory.
The contents of included files can be considered to be copied directly into that line in the current script. That's true even if the "include" is inside a function definition.
Require is just like Include, except if the file is not found you'll get a fatal error.
Set script to output error details to standard-out.
ini_set('display_errors', 'On');
try
{
}
catch(Exception $exception)
{
echo $exception->getMessage();
}
Throwing exceptions:
throw new Exception("Error message.");
Accessing GET parameters:
$email = $_GET["email"];
Set a custom HTTP response code and message.
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
header($protocol . ' ' . $code . ' ' . $text);
This sets the status and text that are accessible through javascript:
//var request = new XMLHttpRequest();
//request.status = 500
//request.statusText = "Internal Server Error"
500 is the standard "Internal Server Error" code.
$headers = "From: noreply@website.com\r\n";
mail($to, $subject, $message, $headers);
Use these headers for HTML formatted messages.
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type:text/html;charset=UTF-8\r\n";
$headers .= "From: noreply@website.com\r\n";
Copied this regular expression from the web for verifying a email address is probably valid.
function isValidEmail($text)
{
$regex = "/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/";
return (preg_match($regex, $text));
}
A php database library which may need to be installed separately.
You can check if the correct library is installed:
if (!function_exists('mysqli_connect'))
{
throw new Exception("mySQLi not found.");
}
(servername will be "localhost" if running script and database on the same machine)
$connection = mysqli_connect($servername, $database_username, $database_password, $database_name);
if ($connection->connect_error)
{
throw new Exception("Connection failed: " . $connection->connect_error);
}
//query database
mysqli_close($connection);
Fast-and-dirty query without error checking.
$sql = "SELECT Id, Name FROM Customer";
$result = $connection->query($sql);
$id = null;
$name = null;
if ($result->num_rows > 0)
{
while($row = $result->fetch_assoc())
{
$id = $row["Id"];
$name = $row["Name"];
}
}
"fetch_assoc" is return a row of data as an associative array using the column names from the SELECT statement.
$sql = mysqli_prepare($connection, "SELECT Id, Name FROM Customer"); //note: no ending semi-colon in query
if(!$sql)
{
throw new Exception("Could not prepare query statement in ".__FUNCTION__."."); //see constants __FUNCTION__ and __METHOD__
}
//actually run the query and load any results
$sql->close();
You can fetch a row of results directly into a set of variables by binding them.
The arguments in "bind_result" must be in the same order as the columns in the SELECT statement.
if(!$sql->execute())
{
throw new Exception($sql->error);
}
$var1 = null;
$var2 = null;
$var3 = null;
$sql->bind_result($var1, $var2, $var3);
if(!$sql->fetch())
{
throw new Exception($sql->error . " in " . __FUNCTION__ . ".");
}
echo "Got ".$var1." and ".$var2." and ".$var3;
Result variables must be bound between "execute" and "fetch".
"store_result" loads all results from the database into local memory. It is required before you can count the resulting rows.
"num_rows" returns the number of rows in the result set.
$sql->bind_result($var1, $var2, $var3);
$sql->store_result();
if($sql->num_rows > 0)
{
if(!$sql->fetch())
{
throw new Exception($sql->error);
}
}
$sql = mysqli_prepare($connection, "SELECT SourceId FROM Source WHERE Name = ?");
if(!$sql)
{
throw new Exception("Could not prepare query statement in ".__FUNCTION__.".");
}
$sql->bind_param("s", $sourceName);
$sql->execute();
$sql->store_result();
if($sql->num_rows === 0)
{
//action: no results
}
$sourceId = null;
$sql->bind_result($sourceId);
$sql->fetch();
$sql->close();
Always use parameterized queries when including variable data in a query. This will protect you from SQL injection attacks and many formatting errors.
Replace each variable in the query with a "?" (question mark).
$sql = mysqli_prepare($connection, "SELECT Id, Name FROM Customer WHERE LocationId=? AND Category=?");
if(!$sql)
{
throw new Exception("Could not prepare query statement.");
}
$sql->bind_param("is", $locationId, $category);
if(!$sql->execute())
{
throw new Exception($sql->error);
}
The first argument in "bind_param" is the types of the variables, one character each, in the same order as the query.
i = integer
d = double
s = string
b = blob
The rest of the arguments in "bind_param" are the variables, in the same order as the query.
Fetch document as text
$content = file_get_contents($url);
echo $content;
//cannot handle badly formed HTML
$dom = new domDocument;
$dom->loadHTML($content);
$dom->preserveWhiteSpace = false;
$bodyTable = $dom->getelementsbytagname('body');
echo $bodyTable->item(0);
How to create URL routing rules in a PHP application.
The .htaccess file operates at the directory level. The file in the current directory will override any file in a higher/ancestor directory.
You can use .htaccess to route everything to an index.php instead of following the full URL path.
Install: sudo apt install phpunit
Verify Installtion: phpunit --version
Run a test file: phpunit myunittests.php
Test functions must be named "test...".
Will only run the first test class found in the file.
An "exit()" being run during a test will exit phpunit.
<?php
include("../file_under_test.php");
use PHPUnit\Framework\TestCase;
// use MyProject\MyNamespace; //haven't got namespacing working yet
class TestXYZ extends TestCase
{
public function testSomething() : void
{
//Arrange
$password_raw = "redyellowblue";
//Act
$hashed_a = create_password_hash($password_raw);
$hashed_b = create_password_hash($password_raw);
//Assert
self::assertNotSame($hashed_a, $hashed_b);
}
}
?>
You cannot override, overload, monkey patch, or mock global functions.
Here is an example of refactoring code so that unit testing is still possible (assuming you are not using Classes, which can be mocked).
Original file:
<?php
if(php_sapi_name() == "cli") {
http_response_code(404);
include('404_not_found.php');
die();
}
if($argv[0] == "create_admin") {
//stuff
}
else if($argv[1] == "remove_user") {
//stuff
}
?>
This is hard to unit test because we can't change the result of "if(php_sapi_name() == "cli")", it will always be false during testing.
Refactored file:
<?php
if(php_sapi_name() == "cli") {
http_response_code(404);
include('404_not_found.php');
die();
}
handle_command($argv);
function handle_command($arguments) {
if($arguments[0] == "create_admin") {
//stuff
}
else if($arguments[1] == "remove_user") {
//stuff
}
}
?>
Function "handle_command" can be fully unit tested. The glue putting it all together would need to be end-to-end tested.
//in the code under test
echo("abc");
//test case
public function testEchoMessage() : void
{
$this->expectOutputString("abc");
functionUnderTest();
}
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage("message");
For more explicit assertions on an exception, use "try/catch" within the test method to get the exception.