PHP代码-快速参考
此页面包含基本PHP语法以及如何使用FastSitePHP的示例.假定一些编程知识,例如[if]语句或[loop]是什么;但是,您不必了解PHP就可以使用它.
通过设计示例,您可以简单地复制并粘贴所需的内容.因为存在标准PHP代码的示例,所以即使您不使用FastSitePHP,此页面也可以作为通用PHP参考.
显示63个示例代码段
PHP语法-概述
<?php
// PHP与C样式语法相似,因此[if]语句,[for]和[while]循环,[函数],
// [注释]等也与其他广泛使用的语言(例如JavaScript,C#和Java)相似。
// 如果您有一些JavaScript经验,那么很容易上手PHP。
// PHP脚本以[<?php]开头,并且各行必须以分号[;]结尾。
// [echo]语句输出内容。 如果将本示例另存为文件,
// 则只输出“你好,世界!”。
echo '你好,世界!';
PHP Syntax - Variables
// In PHP all variables start with a dollar sign character [$] followed by
// the variable name. Variables are created when they are first used and
// not declared ahead of time.
$value = 'Test';
// Variables are dynamically typed in PHP; the data type is determined
// by the value of the variable and the type can be changed.
$value = (10 * 20);
$string = 'String';
$number = 123;
$decimal = 123.456;
$bool = true;
$null = null;
// Arrays can be defined using [] characters like JavaScript and other languages
// when using any recent version of PHP. If using an old version of PHP
// (5.3 or below) Arrays need to be defined using the [array()] function.
$cities = ['Tokyo', 'São Paulo', 'Jakarta', 'Seoul', 'Manila', 'New York City'];
// An extra comma can be included after the last item
$numbers = array(123, 456, 789,);
// PHP Array's is actually an ordered map so they are often used like
// Dictionaries, Hashes, or Associative Arrays from other languages.
$months_days = [
'January' => 31,
'February' => 28,
'March' => 31,
'April' => 30,
];
// Objects can be dynamically created in PHP using the built-in [stdClass].
$object = new \stdClass;
$object->name = 'FastSitePHP';
$object->type = 'PHP Framework';
// Object can also be created dynamically when casting from an array.
$object2 = (object)[
'name' => 'FastSitePHP',
'type' => 'PHP Framework',
];
// To check if variable is defined use the [isset()] function
$defined1 = isset($object); // true
$defined2 = isset($object3); // false
// Additional types include Resources such as a file, and callback functions.
PHP Syntax - Strings
// Similar to other languages such as JavaScript, Python, and Ruby,
// strings in PHP can be either single-quoted or double-quoted.
$value = 'Single Quoted String';
$value = "Double Quoted String";
// To combine or concatenate strings use the dot character [.]:
$greeting = 'Hello ' . 'World';
// Spaces are not required between the [.] and the other variables:
$greeting = 'Hello '.'World';
// You can append to a string using the [.=] operator:
$greeting = 'Hello';
$greeting .= ' World';
// Similar to Python and Ruby, double-quote strings expand variables
// for string interpolation. This prints 'Hello World':
$name = 'World';
$greeting = "Hello ${name}";
// Multi-line strings use [<<<] followed by a programmer-defined identifier
// and end the string the same identifier starting on the code line followed
// by [;]. In this example the identifier is [EOD] for end-of-data. Multiline
// strings using this syntax support string interpolation.
$multiline1 = <<<EOD
Multi-line
String
${name}
EOD;
// When using ['] characters is similar to using single-quotated strings
// so there is no string interpolation. The above example prints 'World'
// instead of '${name}' while this version prints '${name}'.
$multiline2 = <<<'EOD'
Multi-line
String
${name}
EOD;
// Common String Functions using this string:
$value = ' abcdefgh ';
// String Length and Trim
$len = strlen($value); // 10
$len2 = strlen(trim($value)); // 8
// String Search, often PHP functions return mixed data types.
// [strpos()] and [stripos()] are good examples. If the string
// is found an integer with the position is returned otherwise
// a boolean value of false is returned.
$pos = stripos($value, 'DEF'); // Case-insenstive = 4
$pos2 = strpos($value, 'DEF'); // Case-Senstive = false
// Split to an Array and Join an Array to a String.
// Rather than using [split()/join()] PHP uses [explode/implode()].
$value = '123,456,789';
$array = explode(',', $value);
$string = implode('_', $array);
// Replace
$text = 'Blue and Red';
$search = 'Red';
$replace = 'Green';
$new_value = str_replace($search, $replace, $text);
// Internally in PHP strings are implemented as an array of bytes so if you
// work with binary files or data you use the string data type. This can
// present a problem though if you need to calculate the length of a Unicode
// String for a user, find the character position, etc. To support different
// encodings PHP includes Multibyte String Functions. In general they have the
// same name and params as other string functions but are prefixed with [mb_*].
$unicode = '测试';
$ulen = strlen($unicode); // 6
$ulen2 = mb_strlen($unicode); // 2
PHP Syntax - Logic Statements
// This example outputs data to the client as it is evaluated:
// var_dump() = PHP function to print a variable and the data type.
// echo "\n" = Output a new line for formatting.
$number = 5;
// Basic [if] Statement
// Prints: '[Number equals 5]'
if ($number === 5) {
echo '[Number equals 5]';
} else {
echo '[Number does not equal 5]';
}
// [if ... else]. The example aslo shows using the not operator [!].
// Prints: '[Number is positive]'
if (!is_int($number)) {
echo '[Number is not a integer]';
} elseif ($number < 0) {
echo '[Number is negative]';
} else {
echo '[Number is positive]';
}
// Ternary Expression: (expression ? true : false)
// Prints: [Number is even: no]
$is_even = ($number % 2 === 0 ? 'yes' : 'no');
echo "[Number is even: ${is_even}]";
// The [if] statement can be used to evaluate "truthy" values for
// other data types. The 3 statements below all evaluate to false
// because the values are empty or zero.
$empty_array = array();
if ($empty_array) {
echo '[Array has data]';
} else {
echo '[Array is empty]';
}
$empty_string = '';
if ($empty_array) {
echo '[String has data]';
} else {
echo '[String is empty]';
}
$zero = 0;
if ($zero) {
echo '[Number is not Zero]';
} else {
echo '[Number is Zero]';
}
// It's possible to exclude the middle expression when using the ternary
// operator '?:' (expression ?: default). This is known as the Elvis operator
// and returns either the result of the first expression if it evaluates
// as a "truthy" value or the 2nd expression (default value).
// Prints:
// [Elvis Operator: Default]
$value = ($empty_string ?: 'Default');
echo "[Elvis Operator: ${value}]";
// Prints:
// [Elvis Operator: 3]
$value = ((1 + 2) ?: 'Enter Value');
echo "[Elvis Operator: ${value}]";
// Equal [==] vs. Identical (Strict Mode) [===]
// PHP is similar to JavaScript when comparing on data types.
// These expressions all evalute to [true] because
// the data type does not have to match exactly.
echo "\n";
var_dump(1 == true);
var_dump(0 == '');
var_dump(0 == 'a');
var_dump('1' == '01');
// These expressions all evalute to [false] because
// the data type has to match exactly.
echo "\n";
var_dump(1 === true);
var_dump(0 === '');
var_dump(0 === 'a');
var_dump('1' === '01');
// Not Equal [!=] vs. Not Identical [!==]:
echo "\n";
var_dump(0 != ''); // false
var_dump(0 !== ''); // true
// Logical Operators:
echo "\n";
var_dump(true && true); // true
var_dump(false && false); // false
var_dump(false || true); // true
// Arrays can be easily compared in PHP
echo "\n";
$array1 = [1, 2, 3];
$array2 = [1, 2, 3];
$array3 = [1, 2, 3, 4];
var_dump($array1 === $array2); // true
var_dump($array1 === $array3); // false
// Switch Statement
// Just like the [if] statement the syntax for [switch] is similar
// to C style languages such as C and JavaScript so the same
// fallthrough rules apply.
//
// This example prints the season name from 4-season calendar
// in the Northern Hemisphere based on the current month.
echo "\n";
$month = date('F');
switch ($month) {
case 'March':
case 'April':
case 'May':
echo 'Spring';
break;
case 'June':
case 'July':
case 'August':
echo 'Summer';
break;
case 'September':
case 'October':
case 'November':
echo 'Fall (Autumn)';
break;
case 'December':
case 'January':
case 'February':
echo 'Winter';
break;
default:
echo 'Error';
}
PHP Syntax - Loops
// Just like the logic demo this example also outputs data as it is evaluated.
// Define arrays with the largest cities in the world (by urban area)
$cities = [
'Tokyo', 'São Paulo', 'Jakarta', 'Seoul', 'Manila',
'New York City', 'Shanghai', 'Cairo', 'Delhi',
];
$cities_population = [
'Tokyo' => '36,923,000',
'São Paulo' => '36,842,102',
'Jakarta' => '30,075,310',
'Seoul' => '25,520,000',
'Manila' => '24,123,000',
'New York' => '23,689,255',
'Shanghai' => '23,416,000',
'Cairo' => '22,439,541',
'Delhi' => '21,753,486',
];
echo 'Largest Cities in the World' . "\n";
echo str_repeat('-', 40) . "\n";
// Loop through the list of cities using the [foreach] loop.
// foreach (array as item)
foreach ($cities as $city) {
echo $city;
echo "\n";
}
echo "\n";
// Loop through a Dictionary or Associative Array using [foreach]
// foreach (array as key => value)
foreach ($cities_population as $city => $population) {
echo $city . ' = ' . $population;
echo "\n";
}
echo "\n";
// [for] loop using C style syntax, this prints 0...9 on separate lines
for ($n = 0; $n < 10; $n++) {
echo $n;
echo "\n";
}
echo "\n";
// [while] and [do-while] loops also use C style syntax so they will be familiar
// to JavaScript developers. [continue] and [break] also work as expected.
// Prints even numbers between 0 and 8
$n = 0;
while ($n < 10) {
if ($n % 2 !== 0) {
$n++;
continue;
}
echo $n;
echo "\n";
$n++;
}
echo "\n";
// Prints 0...4
$n = 0;
do {
if ($n === 5) {
break;
}
echo $n;
echo "\n";
$n++;
} while ($n < 10);
PHP Syntax - Functions
// Defining and calling functions in PHP is similar to other C style languages.
// While functions are easily defined in PHP most often popular Frameworks and
// PHP projects define classes instead of functions. PHP however has many
// built-in functions that are used in development.
function add($x, $y) {
return $x + $y;
}
// Optional parameters an be by specifing a default value after the variable.
function increment($x, $y = 1) {
return $x += $y;
}
// Callback functions can be defined and set to a variable just as you would
// use in JavaScript.
$subtract = function($x, $y) {
return $x - $y;
};
// This code calls the above functions and prints "2"
$x = 1;
$y = 2;
$z = add($x, $y); // returns 3
$z = increment($z); // returns 4
$z = increment($z, 2); // returns 6
$z = $subtract($z, 4); // returns 2
echo $z;
echo '<br>';
// Unlike JavaScript PHP functions do not have access to variables in the
// parent scope. The [use] keyword can be used to pass variables from the
// parent scope. When using this syntax and setting [$x] in the called function
// [$x] does not get set from the parent scope so this code prints "1".
$scope_test = function() use ($x) {
$x = 123;
};
$scope_test();
echo $x;
echo '<br>';
// This version is similar to the above version however the variable [$x] is
// passed by-reference using the [&] operator. This version will print "123"
// because [$x] gets modified.
$scope_test = function() use (&$x) {
$x = 123;
};
$scope_test();
echo $x;
echo '<br>';
PHP Syntax - Classes and Objects
// Full details of how to define and use classes is beyond the scope of this
// quick reference page however the basic syntax is shown below which can
// help you get started.
class Math
{
// Define a Member Variable
public $value = 0;
// Define a Class Constructor with an Optional Parameter.
// Defining [__construct] is optional.
public function __construct($number = 0)
{
$this->value = $number;
echo 'Classed Created with Value: ' . $number . '<br>';
}
// Define a Class Destructor
public function __destruct()
{
echo 'Classed Destroyed<br><br>';
}
// Public function that returns the object instance [$this]
public function add($number) {
$this->value += $number;
return $this;
}
// Function with no parameter or return value
public function show()
{
echo 'Value: ' . $this->value . '<br>';
}
}
// Prints:
/*
Classed Created with Value: 0
Value: 3
Classed Destroyed
*/
$math = new Math();
$math->add(1)->add(2)->show();
$math = null;
// Prints:
/*
Classed Created with Value: 10
Value: 15
...
*/
$math = new Math(10);
$math->add(5)->show();
// Read from a member variable:
$value = $math->value;
echo $value . '<br>';
PHP Syntax - Encoding - JSON, Base64, Base64-URL
// Create a Basic Object and Array for Encoding
$object = new \stdClass;
$object->string = 'Test';
$object->number = 123;
$object->bool = true;
$array = [
'string' => 'Test',
'number' => 123,
'bool' => true,
];
// -------------------------------------------
// Encode and Decode JSON
// -------------------------------------------
// Since PHP Array's are used like a Dictionary or Hash, both examples print:
// {"string":"Test","number":123,"bool":true}
$json = json_encode($object);
echo $json;
echo "\n";
$json = json_encode($array);
echo $json;
echo "\n\n";
// Use the 2nd Parameter for formatted JSON
$json = json_encode($object, JSON_PRETTY_PRINT);
echo $json;
echo "\n";
// Decode and print the object with details using [print_r()]:
$decoded = json_decode($json);
print_r($decoded);
echo "\n";
// By default an objects are decoded as [stdClass] objects. To return
// an array instead pass [true] as the 2nd parameter.
$decoded = json_decode($json, true);
print_r($decoded);
echo "\n";
// If there is an error decoding JSON data null will be returned.
// If you need to handle invalid JSON you can do so like this:
if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) {
throw new \Exception('Error decoding JSON Data: ' . json_last_error_msg());
}
// FastSitePHP includes a JSON helper class which throws exceptions on
// JSON errors instead of the default behavior of returning [false] or [null].
$json = \FastSitePHP\Encoding\Json::encode($object);
$decoded = \FastSitePHP\Encoding\Json::decode($json);
// Often though in most code simply calling [json_encode()] or [json_decode()]
// will be enough. By default PHP decodes large numbers as floats. If you
// want stricter decoding so they come in a strings then you can use additional
// options. This is how FastSitePHP's JSON class decodes as it is used in the
// JWT, Encryption, and SignedData classes. [JSON_BIGINT_AS_STRING] is not
// avaiable on PHP 5.3 so FastSitePHP uses compatible code.
$decoded = json_decode($json, true, 512, JSON_BIGINT_AS_STRING);
// -------------------------------------------
// Encode and Decode Base64
// -------------------------------------------
// Prints: "VGhpcyBpcyBhIHRlc3Q="
$data = 'This is a test';
$base64 = base64_encode($data);
echo $base64;
echo "\n";
// When decoding if there is an error then [false] is returned
$decoded = base64_decode($base64);
print_r($decoded);
echo "\n\n";
// -------------------------------------------
// Encode and Decode Base64-URL Format
// -------------------------------------------
// PHP does not include built-in functions for Base64-URL format so
// FastSitePHP includes a helper class with static methods. They behave
// similar to the built-in functions [base64_encode()] and [base64_decode()]
// so if there is an error then [false] is returned.
$base64url = \FastSitePHP\Encoding\Base64Url::encode($data);
echo $base64;
echo "\n";
$decoded = \FastSitePHP\Encoding\Base64Url::decode($base64url);
print_r($decoded);
echo "\n";
PHP Syntax - Errors and Exceptions
// PHP uses both Errors that are triggered and Exceptions that are thrown.
// PHP handles errors a differently than many languages. For example in many
// languages a "divide by zero" error would either throw an Exception or be
// fatal and halt the program and in compiled languages an undefined variable
// would not allow the program to run. However when using PHP unless error
// reporting is set both of these errors would simply be ignored and the
// script could continue with unexpected results. This can make programming
// with PHP difficult at first if you are coming from another language.
// FastSitePHP makes things easy because it runs code in strict mode and
// converts errors to exceptions once [app->setup()] is called.
// To handle all errors and exceptions globally in PHP, four different functions
// have to be first set. These are automatically handled from [app->setup()]:
// error_reporting()
// set_exception_handler()
// set_error_handler()
// register_shutdown_function()
// In PHP [try...catch] logic is similar to many languages such as JavaScript:
try {
throw new \Exception('Test');
} catch (\Exception $e) {
echo $e->getMessage();
echo '<br>';
}
// Variables can be checked if they might not exist.
// This code works and no error is triggered.
if (isset($x) === false) {
echo 'Variable [$x] is not defined<br>';
}
// Uncommenting the lines below will trigger different types of errors.
// When using PHP default development settings the errors will often cause
// an error message in the middle of the code and code after will still
// be executed.
//
// echo $x; // [E_NOTICE] = "Undefined variable: x"
// echo 1 / 0; // [E_WARNING] = "Division by zero"
// FastSitePHP converts Errors to Exceptions so they can be caught.
try {
echo $x;
} catch (\Exception $e) {
echo $e->getMessage();
echo '<br>';
}
try {
echo 1 / 0;
} catch (\Exception $e) {
echo $e->getMessage();
echo '<br>';
}
Hello World with FastSitePHP
<?php
// Only two files are required to run FastSitePHP and they can
// be in the same directory as [index.php] or the contents can
// be embedded in the main php page.
require 'Application.php';
require 'Route.php';
// Create the Application Object and optionally setup
// Error Handling and a Timezone.
$app = new FastSitePHP\Application();
$app->setup('UTC');
// Define the 'Hello World' default route
$app->get('/', function() {
return 'Hello World!';
});
// Return a JSON Response by returning an Object or an Array
$app->get('/json', function() {
return ['Hello' => 'World'];
});
// For all other requests, return the URL as a plain text response.
// The [use] keyword makes the [$app] variable available to the function.
$app->get('/*', function() use ($app) {
$app->header('Content-Type', 'text/plain');
return $app->requestedPath();
});
// Run the App
$app->run();
Application Object - Defining Basic Routes
// The Application Object is the key Object in FastSitePHP. It is used to
// define routes, provide request info, render templates, send the response,
// and more. If you are using a copy of this site or a starter site the
// Application Object will be available as the variable [$app] and routes
// are defined in the page [app.php].
// Basic Route
// Send an HTML Response when either '/about' or '/about/' is requested
$app->get('/about', function() {
return '<h1>About Page</h1>';
});
// By default URL's are case-sensitive however this can be
// turned off and then '/ABOUT' would match the above route.
$app->case_sensitive_urls = false;
// If setting URL strict mode then the above URL would only match
// to '/about' and '/about/' would have to be explicitly defined.
$app->strict_url_mode = true;
$app->get('/about/', function() {
return '<h1>About Directory</h1>';
});
// The about call using [get()] matches only 'GET' requests. If you would like
// to handle both 'GET' and 'POST' or other methods with the same route you
// can define the route using the [route()] function then check the if there is
// data sent with the request as shown below. The [route()] function will accept
// all request methods.
$app->route('/form', function() {
if ($_POST) {
// Handle posted form data
}
// Handle GET request, return rendered template, etc
});
// In addition to GET Requests you can handle [ POST, PUT, PATH, and DELETE]
// Requests using named functions.
$app->get('/method', function() { return 'get()'; });
$app->post('/method', function() { return 'post()'; });
$app->put('/method', function() { return 'put()'; });
$app->patch('/method', function() { return 'patch()'; });
$app->delete('/method', function() { return 'delete()'; });
// The same URL can be defined multiple times and the first matching response
// will stop additional routes from being evaluated. In this example the route
// '/example' will return the text 'Example 2'.
$app->get('/example', function() { return null; });
$app->get('/example', function() { return 'Example 2'; });
$app->get('/example', function() { return 'Example 3'; });
// In addition to returning a response you can also simply output a response
// using [echo] or other functions.
$app->get('/echo-response', function() {
echo 'Output';
});
Define a Route with a Parameter
// Send a response 'Hello FastSitePHP!' for the URL '/hello/FastSitePHP'.
// The ':name' text in the route pattern defines a parameter for the route
// because it starts with the ':' character.
$app->get('/hello/:name', function($name) {
return 'Hello ' . $name;
});
Define a Route with an Optional Parameter
// Send a response 'Hello World!' for the URL '/hello' or in the case of the
// optional [name] variable safely escape and return a message with the name.
// The [use] keyword makes the [$app] variable available to the function
// and the question mark in the URL pattern ':name?' makes the variable optional.
$app->get('/hello/:name?', function($name = 'World') use ($app) {
return 'Hello ' . $app->escape($name) . '!';
});
// In addition to optional parameters a wildcard character '*' can be used at
// the end of the URL to handle all requests that match the start of the URL.
// In this example the following two URL's would both be matched.
// '/hello/world'
// '/hello/page1/page2/page3'
$app->get('/hello/*', function() use ($app) {
$app->header('Content-Type', 'text/plain');
return $app->requestedPath();
});
Define a Route that maps to a Controller Class
// Defining routes with callback functions allows for fast prototyping
// and works well when minimal logic is used. As code grows in size it
// can be organized into controller classes.
// Optionally specify the Controller Class Root Namespace. When using this if a
// class 'Examples' is created then it will map to 'App\Controllers\Examples'.
$app->controller_root = 'App\Controllers';
// Similar to [controller_root] is [middleware_root] which applies to
// [Route->filter()] and [$app->mount()] functions.
$app->middleware_root = 'App\Middleware';
// The two format options are 'class' and 'class.method'. When using only
// class name then the route function [route(), get(), post(), put(), etc]
// will be used for the method name of the matching controller.
$app->get('/:lang/examples', 'Examples');
$app->get('/:lang/examples/:page', 'Examples.getExample');
// Controller Class Example
class Examples
{
public function get(Application $app, $lang) { }
public function getExample(Application $app, $lang, $page) { }
}
// In addition to organizing code into controller classes you can also separate
// routes into separate files using the [mount()] function. The mount function
// will load a file in the same directory only if the starting part of the
// Requested URL matches the Mount URL. An optional 3rd parameter accepts a
// callback function or string of 'Class.method' and if false is returned
// then the file won't be loaded.
$app->mount('/data/', 'routes-data.php');
$app->mount('/secure/', 'routes-secure.php', function() {
// Logic ...
return false;
});
$app->mount('/sysinfo/', 'routes-secure.php', 'Env.isLocalhost');
Route Parameter Validation
// The Application Object has a [param()] function which can be used to
// validate and convert URL parameters to a specific format such as a number.
// The function is defined as:
// param($name, $validation, $converter = null)
// Parameters:
// Validation = ['any', 'int', 'float', 'bool'], a valid regular expression,
// or a Closure/Callback function. When using 'int|float|bool' the data
// type will automatically be converted.
// Convertor = ['int', 'float', 'bool'] or a Closure/Callback function.
// Basic Example
// '/product/123' = Match and [$product_id] will be an integer
// '/product/abc' = 404 Page Not Found
$app->param(':product_id', 'int');
$app->get('/product/:product_id', function($product_id) {
var_dump($product_id);
});
// Additional Examples of Defining Parameter Rules. For more see full
// documentation and other examples.
$range_param = function($value) {
$num = (int)$value;
if ($num >= 5 && $num <= 10) {
return true;
} else {
return false;
}
};
$app
->param(':range1', $range_param)
->param(':range2', $range_param, 'int')
->param(':range3', $range_param, function($value) {
return (int)$value;
});
$app->param(':float', 'float');
$app->param(':bool', 'any', 'bool');
$app->param(':regex1', '/^\d+$/');
$app->param(':regex2', '/^[a-zA-Z]*$/');
Use Route Filters
// Routes can have custom filter functions assigned to them to run specific
// code if a route is matched, perform validation, or another task required
// by your site. Filter functions only run if the route is matched to the
// requested URL.
// Define some callback/closure functions
$text_response = function() use ($app) {
$app->header('Content-Type', 'text/plain');
};
$is_authenticated = function() {
// Check User Permissions ...
return true;
};
// When routes are created [get(), route(), post(), etc] the created route
// is returned so you can call [filter()] after defining the route.
// This page will be returned a Plain Text page because the filter function
// sets the Response Header and returned no value.
$app->get('/text-page', function($name) {
return 'Hello';
})->filter($text_response);
// A route can have multiple filters and for clarity you may want to put
// filter functions on separate lines. This page will only be called if
// [$is_authenticated] returns [true] and it will also be a text response.
$app->get('/secure-text-page', function($name) {
return 'Hello ' . $name;
})
->filter($is_authenticated)
->filter($text_response);
// The [filter()] function also accepts a string representing
// a class and method in the format of 'Class.method'.
$app->get('/phpinfo', function($name) {
phpinfo();
})
->filter('Env.isLocalhost');
// When using string filters you can specify a root namespace
// for the classes using the App property [middleware_root].
$app->middleware_root = 'App\Middleware';
Application Object - Basic Request Info
// Many frameworks require special configuration values in order to handle
// requests. FastSitePHP figures this out automatically and provides several
// functions in the Application Object to return basic request info.
// If your site does not use a proxy server such as load balancer then these
// functions can be used for building URL's or other app needs. If your site
// uses a load balancer with custom host headers then you would want to use
// the request object to obtain the root url.
// Root or Base URL for the Site. This is often needed to build full path
// URL's on web pages.
//
// Examples:
// # [index.php] specified in the URL
// Request: https://www.example.com/index.php/page
// https://www.example.com/index.php/page/page2
// Returns: https://www.example.com/index.php/
//
// # [index.php] Located in Root Folder
// Request: https://www.example.com/page
// https://www.example.com/page/page2
// Returns: https://www.example.com/
//
// # [index.php] Located under [site1]
// Request: https://www.example.com/site1/page
// https://www.example.com/site1/page/page2
// Returns: https://www.example.com/site1/
//
$root_url = $app->rootUrl();
// Root Directory for the Site. Often needed to build URL's for Static
// Resources such as CSS or JavaScript files.
//
// Request: https://www.example.com/index.php/page
// https://www.example.com/index.php/page/page2
// https://www.example.com/page
// Returns: https://www.example.com/
//
$root_dir = $app->rootDir();
// Get the Requested URL which exits after the Root URL. This will be
// based on where the [index.php] or entry PHP file is located.
//
// Request: https://www.example.com/index.php/test/test?test=test
// https://www.example.com/index.php/test/test
// https://www.example.com/test/test/
// https://www.example.com/test/test
// https://www.example.com/site1/index.php/test/test
// Returns: '/test/test'
//
// In the above example both '/test/test/' and '/test/test' return
// '/test/test' when using the default property [$app->strict_url_mode = false]
// otherwise the exact URL would be returned.
//
$requested_path = $app->requestedPath();
// Example usage for building URL's:
$site_css = $app->rootDir() . 'css/site.css';
$docs_link = $app->rootUrl() . '/documents';
//
// <link href="{{ $site_css }}" rel="stylesheet" />
// <a href="{{ $docs_link }}">Documents</a>
Dynamic Functions and Lazy Loading Properties
// FastSitePHP allows for the Application Object to be assigned dynamic
// functions and lazy loading properties. This allows for custom functions
// and resources shared by many routes to be organized under a global object
// and can allow for simple and clear dependancy injection.
// JavaScript Example - This works to add a function dynamically to an object:
//
// var obj = {};
// obj.test = function() { alert('test'); };
// obj.test();
// PHP Example - The function can be assigned to a property however if called
// an error is triggered - 'Call to undefined method ...'.
$obj = new \stdClass;
$obj->test = function() { echo 'test'; };
// $obj->test();
// When using FastSitePHP's Application object you can simply assign an use
// functions just like in JavaScript or Ruby.
$app->test = function() { echo 'test'; };
$app->test();
// The native PHP function [method_exists()] will not work for custom functions
// so to check if either a built-in or custom App method exists use this.
$exists = $app->methodExists('test');
// The [lazyLoad()] function accepts a property name and callback function.
// It creates the object as a property of the app only if used. This is ideal
// for working with sites where some pages use a resource and some do not.
$app->lazyLoad('db', function() {
return $pdo = new \PDO('sqlite::memory:');
});
// [$app->db] gets set here on first use.
$sql = 'CREATE TABLE test (id INTEGER PRIMARY KEY, test)';
$app->db->query($sql);
// [$app->db] now works as a standard property as it was previously called.
$sql = 'SELECT * FROM sqlite_master';
$records = $app->db->query($sql)->fetchAll(\PDO::FETCH_ASSOC);
Application Events
<?php
// Just like the Hello World Demo this code can be copied to a separate
// [index.php] or other file and then tested.
// There are 5 Application callback events:
// before(), beforeSend(), after(), notFound(), and error()
// They can be used to handle custom logic while the application is running.
// Load Files
require 'Application.php';
require 'Route.php';
// Or use an Autoloader:
// require '../../vendor/autoload.php';
// Create and Setup App Object
$app = new FastSitePHP\Application();
$app->setup('UTC');
// ------------------------------------------------------------------
// Define Events
// ------------------------------------------------------------------
// [Before] Events will be called from the [run()] function prior to any routes
// being matched. All Event functions can be called multiple times and will
// run in order that they are defined.
$app->before(function() use ($app) {
$app->content = '[before1]';
});
$app->before(function() use ($app) {
$app->content .= '[before2]'; // Append
});
// [Before Send] Events will be called from the [run()] function after
// a route has been matched to the requested resource. Functions passed
// to the [beforeSend()] function should be defined as [function($content)]
// and they must return a response otherwise a 404 'Not found' response will
// be sent to the client.
$app->beforeSend(function($content) {
return $content . '[beforeSend]';
});
// [Not Found] Events will be called from the [run()] function after all
// routes have been checked with no routes matching the requested resource.
// Functions passed to the [notFound()] function take no parameters and
// if they return a response then it be handled as a standard route and
// will call any defined [beforeSend()] functions afterwards.
$app->notFound(function() use ($app) {
return $app->content . '[notFound]';
});
// [Error] Events will be triggered if an unhandled Exception is thrown,
// an error is triggered, or a route is not matched and would trigger a
// 404 or 405 response. This function can be used to log errors or handle
// the response with a custom error. If [exit()] is not called then the
// specified or standard FastSitePHP error template will be rendered.
$app->error(function($response_code, $e) use ($app) {
// $response_code = [null, 404, 405, or 500]
// $e = [null, Exception, or Throwable]
if ($app->requestedPath() === '/error-test-1') {
echo $app->content . '[Custom Error]';
exit();
}
});
// [After] Events will be called from the [run()] function after the response
// has been sent to the client. Functions passed to the [after()] function
// should be defined as [function($content)]; the [$content] parameter defined
// in the callback is the contents of the response that was sent to the client
// and it cannot be modified from here. The only way that [after()] functions
// will not get called is if there script is terminated early from PHP's
// [exit()] statement or if error handling is not setup and an error occurs.
$app->after(function($content) {
echo '[after]';
});
// ------------------------------------------------------------------
// Define Routes
// ------------------------------------------------------------------
// This response will output the following:
// [before1][before2][page][beforeSend][after]
$app->get('/', function() use ($app) {
return $app->content . '[page]';
});
// Call URL '/test' and see the following:
// [before1][before2][notFound][beforeSend][after]
// This response will output the following:
// [before1][before2][Custom Error]
$app->get('/error-test-1', function() {
throw new \Exception('Error Test 1');
});
// Display's Standard Error Page with [after] showing at very bottom
$app->get('/error-test-2', function() {
throw new \Exception('Error Test 2');
});
// ------------------------------------------------------------------
// Run the App
// ------------------------------------------------------------------
$app->run();
PHP Template Example
<!--
// This is the contents of the file [template.php] which is shown as an example
// on this page. When calling [render()] the Application Object is passed as
// [$app] which allows for [escape()] and other functions to be used. In addition
// to the standard [if (expression) { code }] sytnax PHP provides an alternative
// syntax for control structures when using templates [if (expr): (code) endif].
//
// PHP templates are high performance and use very little memory however the
// syntax can be considered more verbose than many modern template formats. If
// you prefer to use a different template format there are many widely used and
// high quality template engines for PHP that can be intergrated with FastSitePHP.
-->
<h1><?= $app->escape($page_title) ?></h1>
<?php if (count($list) === 0): ?>
<p>No Records found</p>
<?php else: ?>
<ol>
<?php foreach ($list as $item): ?>
<li><?= $app->escape($item) ?></li>
<?php endforeach ?>
</ol>
<?php endif ?>
<p><?= $app->escape($year) ?></p>
Application - Render Server-Side Template Files
// Set the Template Root Directory and Specific Core Files
$app->template_dir = __DIR__ . '/views/';
// $app->header_templates = '_header.php';
// $app->footer_templates = '_footer.php';
// $app->error_template = 'error.php'; // For 500 Responses
// $app->not_found_template = '404.php'; // For 404 and 405 Responses
// Optionally show detailed errors when using the default error
// template and set custom error messages. With the default template
// detail errors will be displayed when running from localhost.
$app->show_detailed_errors = true;
// $app->error_page_title = 'Custom Error Page';
// $app->error_page_message = 'Custom Error Message';
// $app->not_found_page_title = 'Custom 404 Page';
// $app->not_found_page_message = 'Custom 404 Message';
// $app->method_not_allowed_title = 'Custom 405 Page';
// $app->method_not_allowed_message = 'Custom 405 Message';
// Define Data for the Template. Variables can be defined in the App's
// [locals] property and they can be passed on the render function.
$app->locals['year'] = date('Y');
$data = [
'page_title' => 'PHP Template Example',
'list' => ['Item 1', 'Item 2', 'Item 3', 'Item 4'],
];
// Render the PHP Template and return a string.
// The template source is shown in the above example code section.
$html = $app->render('template.php', $data);
Application - Render with a Custom Template Engine
// Define a Custom Template Engine that uses the
// popular Mustache Template System.
$app->engine(function($file, array $data = null) {
$dir = __DIR__ . '/views/';
$options = [
'cache' => dirname(__FILE__).'/tmp/cache/mustache',
'loader' => new Mustache_Loader_FilesystemLoader($dir, ['extension' => '.htm']),
];
$mustache = new Mustache_Engine($options);
$tmpl = $mustache->loadTemplate($file);
$html = $tmpl->render($data);
return $html;
});
// Define Data for the Template
$app->locals['year'] = date('Y');
$data = [
'page_title' => 'Mustache Template Example',
'list' => ['Item 1', 'Item 2', 'Item 3', 'Item 4'],
'has_list' => true,
];
// Render the Template
$html = $app->render('template.mustache', $data);
// When using Custom Templates you can define Custom Error and Not Found Pages:
// $app->error_template = 'error'; // For 500 Responses
// $app->not_found_template = '404'; // For 404 and 405 Responses
HTTP Request Object - Reading Query Strings, Form Fields, and Cookies
// The request object can be used obtain info from the client for an
// HTTP request. This includes query strings, form fields, cookies,
// headers, and more. The request object also contains functions to
// sanitize “clean” and safely read client info.
// Without using a Framework, Query Strings, Form Variables and other
// User Input can be read through PHP Superglobals [$_GET, $_POST, etc].
// Example, read the Query String Field [number]:
$number = $_GET['number'];
// If the query string [type] does not exist then the above code
// would throw an exception so to safely get the value you can first
// check if it is set.
$number = (isset($_GET['number']) ? $_GET['number'] : null);
// An additional line of PHP code can be used to force a numeric value:
$number = (int)$number;
// The Request object can be used instead to safely read the values, convert
// data types, etc. To use the Request object simply create one:
$req = new \FastSitePHP\Web\Request();
// You can then read query strings by name without including safety logic:
$number = $req->queryString('number');
// An optional 2nd parameter can be used to convert to a specific data type.
// In this example the value will be converted to an interger if it is valid
// otherwise null will be returned.
$number = $req->queryString('number', 'int?');
// In addition to [queryString()] functions [form()] and [cookie()] can be
// used in the same manner.
$value = $req->form('field');
$cookie = $req->cookie('name');
// The Request object also contains a helper function to handle user input
// or objects where a value may or may not exist. This can be used to prevent
// errors when reading complex JSON object and to to sanitize “clean” data from
// any object or array.
//
// Function Definititon:
// value($data, $key, $format = 'value?', $max_length = null)
//
// Data Example:
// $_POST['input1'] = 'test';
// $_POST['input2'] = '123.456';
// $_POST['checkbox1'] = 'on';
// $json = [
// 'app' => 'FastSitePHP',
// 'strProp' => 'abc',
// 'numProp' => '123',
// 'items' => [ ['name' => 'item1'], ['name' => 'item2'] ],'
// ];
//
// Function Examples:
// 'test' = $req->value($_POST, 'input1');
// // Truncate the string to 2 characters:
// 'te' = $req->value($_POST, 'input1', 'string', 2);
// 123.456 = $req->value($_POST, 'input2', 'float');
// '' = $req->value($_POST, 'missing', 'string'); // Missing
// 1 = $req->value($_POST, 'checkbox1', 'checkbox');
// 0 = $req->value($_POST, 'checkbox2', 'checkbox'); // Missing
// true = $req->value($_POST, 'checkbox1', 'bool');
// 'FastSitePHP' = $req->value($json, 'app');
// 'abc' = $req->value($json, 'strProp', 'string?');
// 0 = $req->value($json, 'strProp', 'int'); // Invalid Int
// null = $req->value($json, 'strProp', 'int?'); // Invalid Int
// 123 = $req->value($json, 'numProp', 'int');
// 'item1' = $req->value($json, ['items', 0, 'name']);
// 'item2' = $req->value($json, ['items', 1, 'name']);
// null = $req->value($json, ['items', 2, 'name']); // Missing
//
// See full documentation for more. If you need full validation rather than
// data cleaning see the [\FastSitePHP\Data\Validator] class.
HTTP Request Object - Request JSON and Content
// Create the Request Object
$req = new \FastSitePHP\Web\Request();
// Get the Request Content Type. This is a helper field that returns
// a simple value based on the 'Content-Type' header:
// 'json' = 'application/json'
// 'form' = 'application/x-www-form-urlencoded'
// 'xml' = 'text/xml' or 'application/xml'
// 'text' = 'text/plain'
// 'form-data' = 'multipart/form-data'
// If different the raw header value will be returned and if the header
// is not defined then [null] will be returned.
$type = $req->contentType();
// The Request body/content can be read from [content()]. If the Request Type
// is JSON then the object will be parsed and an object/array will be returned.
// If [contentType() === 'form'] then an array will be returned otherwise the
// body/content is returned as a string. In PHP a string can also be used for
// binary data as a string is simply array of bytes.
$body = $req->content();
// The [value()] function can be used to safely read nested values from a
// submitted JSON object. See other examples and docs for more on using the
// [value() function.
$value = $req->value($body, ['items', 0, 'name']);
HTTP Request Object - Header Fields
// Create the Request Object
$req = new \FastSitePHP\Web\Request();
// Reading Common Header Fields can be done through functions:
$origin = $req->origin();
$userAgent = $req->userAgent();
$referrer = $req->referrer();
$client_ip = $req->clientIp();
$protocol = $req->protocol();
$host = $req->host();
$port = $req->port();
// When using functions with 'Accept' Headers an array of data is returned,
// and an optional parameter can be passed to return true or false.
$accept_encoding = $req->acceptEncoding();
$accept_language = $req->acceptLanguage();
// Example:
// 'Accept-Language' Header Value = 'ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4'
// Returns:
// [
// ['value' => 'ru-RU', 'quality' => null],
// ['value' => 'ru', 'quality' => 0.8],
// ['value' => 'en-US', 'quality' => 0.6],
// ['value' => 'en', 'quality' => 0.4],
// ];
$accept_en = $req->acceptLanguage('en'); // true
$accept_de = $req->acceptLanguage('de'); // false
// Any header can be read when using the [header()] function:
$content_type = $req->header('Content-Type');
$user_agent = $req->header('User-Agent');
// Header Keys are Case-insensitive so the following all return the same value:
$content_type = $req->header('content-type');
$content_type = $req->header('CONTENT-TYPE');
$content_type = $req->header('Content-Type');
// All headers can be read from the [headers()] function:
$headers = $req->headers();
HTTP Request Object - Proxy Header Fields
// Create the Request Object
$req = new \FastSitePHP\Web\Request();
// Request Proxy Headers are used for key fields such as client IP when a
// web server sites behind a “proxy” server on a local network, for example
// a load balancer. Reading the values correctly is important for security,
// however in general with any programming language or framework reading proxy
// headers if often difficult and requires extra config. FastSitePHP makes
// the task easy with no config required.
// For example, simply reading the Client IP of the request can be done
// by reading the value of REMOTE_ADDR.
$client_ip = $_SERVER['REMOTE_ADDR'];
// If the load balancer is configured to provide the Client IP it will
// usually be one of the following Request Headers [X-Forwarded-For,
// Client-Ip, or Forwarded]. However since the end user can send data with
// the Request Header it must be read correctly. The standardized header
// [Forwarded] has a format like this:
// 'for=192.0.2.43, for="[2001:db8:cafe::17]";proto=http;by=203.0.113.43'
// While non-standard but widely used headers such as [X-Forwarded-For] use
// this format:
// 'client-ip1, client-ip2, proxy1, proxy2'
// FastSitePHP handles both formats.
// For example assume the load balancer is at '10.0.0.1', '10.0.0.2' is used
// for additional content filtering, and [X-Forwarded-For] came in with the
// the following value:
// [REMOTE_ADDR] = '10.0.0.1'
// [X-Forwarded-For] = "' OR '1'='1 --, 127.0.0.1, 54.231.1.5, 10.0.0.2"
// In this example, the following was submitted:
// - Client - A SQL Injection String of "' OR '1'='1 --"
// - Client - A localhost IP [127.0.0.1]
// - Client - Actual IP [54.231.1.5]
// - Server - 10.0.0.2
// When simply reading Client IP without any parameters the IP of the load
// balancer is returned for this example which is '10.0.0.1'.
$client_ip = $req->clientIp();
// Then when using the default 'from proxy' setting the correct User IP
// value of '54.231.1.5' is returned. If no proxy server is used then the
// default settings of 'from proxy' are safe to call.
$user_ip = $req->clientIp('from proxy');
// When using proxies an optional 2nd parameter of [$trusted_proxies] is
// avaiable. This defaults to the string 'trust local', however an array
// of specific IP or IP Ranges (CIDR format) can be used for more specific
// filtering. Additionally the first parameter [$option] can also be
// be modified to read from different Request Headers.
$user_ip = $req->clientIp('from proxy', 'trust local');
// In addition to Client IP, proxy values can also be read for
// [Protocol, Host, and Port]:
$portocal = $req->protocol('from proxy'); // 'http' or 'https'
$host = $req->host('from proxy');
$port = $req->port('from proxy');
Request Object - Server Info
// The Request Object can return the Server IP and has a helper function
// [isLocal()] that returns true only if both the requesting client and
// the web server are on localhost ['127.0.0.1' or '::1']. In certain apps
// you may want to enable certain features for development or local work
// and these functions help with that.
$req = new \FastSitePHP\Web\Request();
$server_ip = $req->serverIp();
$is_local = $req->isLocal();
// NOTE - the Web Server IP is often different than than the actual
// Network IP. To obtain the network IP (location of the server) use
// the Networking Config Object instead:
$config = new \FastSitePHP\Net\Config();
$net_ip = $config->networkIp();
Response - Content, Status Codes, Headers, Cookies, and Files
// By default when a string is returned in a route the server returns an
// HTML response. Without creating a Response Object, the Application Object
// can be used to specify a different 'Content-Type' Header which is what
// Browsers and HTTP Clients use to determine how to handle the response.
$app->get('/app-text-response', function() use ($app) {
$app->header('Content-Type', 'text/plain');
return 'Response using the Application Object';
});
// When using the Response Object [contentType()] and [content()]
// are the main functions to specify different content types.
$app->get('/text-response', function() {
$res = new \FastSitePHP\Web\Response();
return $res->contentType('text')->content('Text Response');
});
// When using the Response Object, properties are set through getter/setter
// functions and are chainable so they can be used on one line as shown
// above or separated to multiple lines as shown here.
$app->get('/text-response2', function() {
return (new \FastSitePHP\Web\Response())
->contentType('text')
->content('Text Response 2');
});
// Using the Response Object
$res = new \FastSitePHP\Web\Response();
// Set the 'Content-Type' Header.
// The following 3 function calls all set the same value.
// The difference is that [contentType()] is a helper function which allows
// for short-hand values of [html, json, jsonp, text, css, javascript, xml].
$res->contentType('text');
$res->contentType('text/plain');
$res->header('Content-Type', 'text/plain');
// Set Content
// For most content types use a string when setting [content()].
$res->content('<h1>FastSitePHP</h1>');
// For JSON Content either Objects and Arrays are used
$object = [
'title' => 'Demo',
'number' => '123',
];
$res
->contentType('json')
->content($object);
// The helper [json()] function sets both [contentType()] and [content()]
$res->json($object);
// For formatted JSON set the option [JSON_PRETTY_PRINT] before sending
// the Response. By default [JSON_UNESCAPED_UNICODE] is used and JSON
// is minimized. Any constant used by [json_encode()] can be set here.
$app->json_options = (JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$res->jsonOptions(JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// Status Codes
// [$app] only supports [200, 201, 202, 204, 205, 404, and 500]
// and the Response Object allows and handles 304 Responses along
// with any other valid or custom status codes.
$app->statusCode(201);
$res->statusCode(500);
// A helper function [pageNotFound()] exists on the Application Object that
// can be used to send a 404 response along with default or custom 404 page.
$app->get('/document/:name', function($name) use ($app) {
if ($name !== 'test') {
return $app->pageNotFound();
}
return 'Test';
});
// Specify a file for the response; the file specified will be streamed to the
// client and sent in a memory efficient manner so this function can be called
// on very large files with minimal performance impact for the server.
$file_path = __FILE__;
$res->file($file_path);
// Include specific Mime-Type along with Headers for Caching.
// Another topic on this page covers caching in more detail.
$res->file($file_path, 'text', 'etag:md5', 'private');
// Example File Usage
$app->get('/view-source-code', function() {
$file_path = __FILE__;
$res = new \FastSitePHP\Web\Response();
return $res->file($file_path, 'download');
});
// Convert a file name or file type to a mime-type.
//
// File extensions that map to a Mime type with the function are:
// Text: htm, html, txt, css, csv, md, markdown, jsx
// Image: jpg, jpeg, png, gif, webp, svg, ico
// Application: js, json, xml, pdf, woff
// Video: mp4, webm, ogv, flv
// Audio: mp3, weba, ogg, m4a, aac
//
// If a file type is not associated with a mime-type then a file
// download type of 'application/octet-stream' will be returned.
$mime_type = $res->fileTypeToMimeType('video.mp4');
$mime_type = $res->fileTypeToMimeType('mp4');
// Set Response Headers and Cookies
// Using the Application Object
$app->header('X-API-Key', 'App_1234');
$app->cookie('X-API-Key', 'App_1234');
// Or using the Response Object
$res->header('X-API-Key', 'Res_1234');
$res->cookie('X-API-Key', 'Res_1234');
// When creating a Response Object the Application Object can be
// passed and all App settings from [statusCode(), cors(), noCache(), headers(),
// cookies(), and json_options] will be passed to the Response Object.
$res = new \FastSitePHP\Web\Response($app);
HTTP Redirects
// HTTP Requests can be redirected using either the App or Response Object.
// When using the App Object and calling [redirect()] the PHP script ends
// immediately however any events defined from [after()] will be called.
// If your site uses Server-side Unit Testing you may want to use the response
// object which behaves as a regular route and doesn’t end script execution.
// User makes this request
$app->get('/page1', function() use ($app) {
$app->redirect('page3');
});
// Or User makes this request
$app->get('/page2', function() {
$res = new \FastSitePHP\Web\Response();
return $res->redirect('page3');
});
// User will then see this URL and Response
$app->get('/page3', function() {
return 'page3';
});
// The default Response Status Code is [302 'Found'] (Temporary Redirect),
// and an optional 2nd parameter for both App and Response allow for
// additional redirect response status codes:
// 301 Moved Permanently
// 302 Found
// 303 See Other
// 307 Temporary Redirect
// 308 Permanent Redirect
$app->get('/old-page', function() use ($app) {
$app->redirect('new-page', 301);
});
Response - Cache Headers and Client-Side Caching
// Examples below show how to use Response Headers to control how a Browser
// or HTTP Client caches a Page or Resource.
// Prevent a Browser or Client from Caching a Page or File.
// Both the Application and the Response Objects have a [noCache()] function.
// Calling these functions will send 3 Response Headers to the client:
// Cache-Control: no-cache, no-store, must-revalidate
// Pragma: no-cache
// Expires: -1
$app->noCache();
$res = new \FastSitePHP\Web\Response();
$res->noCache();
// If using certain Response Headers the Response Object will send a 304
// "Not Modified" Response depending on the Request Headers. 304 Responses
// are used by Browsers and other Clients to re-use previously fetched resources
// from their cached copy. This allows the user to see static resources more
// quickly and reduces the amount of traffic sent from the server.
// 'Cache-Control' Response Header. This header has different options to tell
// clients how they can cache a page. In this example only end users and not
// proxy servers can cache the response and they must re-validate it each time.
$res->cacheControl('private, must-revalidate');
// 'Expires' Response Header. This header is used to tell a client how long the
// content is valid for, however depending on 'Cache-Control' options this value
// may be ignored. Setting this value though does not trigger a 304 response and
// it's up to the browser or client how to handle it.
$res->expires('+1 month');
// 'ETag' Response Header (ETag is short for Entity Tag). An ETag represents a
// unique value for the content (often using a Hash). Browsers and Clients will
// send back an 'If-None-Match' Request Header with the version that they have
// cached and if it matches then the Response Object will send a 304 Response
// without the content since the browser can use the local copy.
$res->etag('hash:md5');
// The [etag()] function also accepts the hash itself or a closure function.
$res->etag('0132456789abcdef');
$res->etag(function($content) {
return sha256($content);
});
// The optional 2nd parameter accepts the ETag Type of either 'strong' or 'weak'.
// The default is 'weak' and that is recommended to avoid complex caching errors.
// If you need to use 'strong' ETags you would likey want to do extra testing.
$res->etag('hash:sha256', 'weak');
// 'Last-Modified' Response Header. If set and if the client sends back an
// 'If-Modified-Since' Request Header that matches then a 304 Response will
// be sent. When setting the value use a Unix Timestamp or String that can be
// parsed by the PHP Function [strtotime()].
$res->lastModified('2019-01-01 13:01:30');
// 'Vary' Response Header. The 'Vary' Response Header can be used to
// specify rules for HTTP Caching and also to provide content hints to
// Google and other Search Engines.
$res->vary('User-Agent, Referer');
// When sending a file as the response you can specify optional parameters
// [$cache_type and $cache_control]. Cache Type has 3 valid options shown
// below and Cache Control sets the [cacheControl()] function.
$file_path = __FILE__;
$content_type = 'text';
$res->file($file_path, $content_type, 'etag:md5');
$res->file($file_path, $content_type, 'etag:sha1', 'private');
$res->file($file_path, $content_type, 'last-modified', 'public');
// When sending etags with [file()] and using either 'etag:md5' or 'etag:sha1'
// the hash is calculated each time. If you use ETags and have large files
// or frequently accessed files it would be a good idea to save the hash
// when the file is first created and set it through the [etag()] function.
$saved_hash = '0132456789abcdef';
$res->file($file_path)->etag($saved_hash);
Cross-Origin Resource Sharing (CORS)
// CORS is commonly used in Web API's to share data from one site or
// domain with another domain (cross-orign resource). To include the
// 'Access-Control-Allow-Origin' Header in your response use the [cors()]
// function. First make sure to set CORS headers from the App Object.
$app->cors('*');
// If using the Response Object either pass the App Object to either
// the Response when it's created or it's [cors()] function.
$res = new \FastSitePHP\Web\Response($app);
$res->cors($app);
// When passing a string the 'Access-Control-Allow-Origin' is validated
// and set, however if you need to pass additional CORS use an array
// with the named headers instead.
$app->cors([
'Access-Control-Allow-Origin' => 'https://www.example.com',
'Access-Control-Allow-Headers' => 'Authorization, Content-Type',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Max-Age' => 86400,
]);
// If calling a POST, PUT, DELETE or other Request Method you may need
// to handle OPTIONS requests. When using CORS and an OPTIONS request is
// processed, FastSitePHP will automatically set the header
// 'Access-Control-Allow-Methods' based on how routes are defined.
// To make sure OPTIONS requests are handled first create a function
// that sets the CORS value.
$cors = function () use ($app) {
$app->cors('*');
};
// Assign the Filter Function to the routes that use CORS:
$app->post('/api-data', function() {
return [ 'example' => 'POST' ];
})
->filter($cors);
$app->put('/api-data', function() {
return [ 'example' => 'PUT' ];
})
->filter($cors);
// If you do not want to allow FastSitePHP to handle OPTIONS
// requests you can turn it off using this option:
$app->allow_options_requests = false;
Secure Cookies
// FastSitePHP allows for easy handling of Secure Cookies (Encrypted, Signed,
// or JWT). To use generate a secure key and save it with app config values.
// For more on config and crypto settings see other docs on this site.
// Strong keys are important for security and are required by default.
// $app->config['ENCRYPTION_KEY'] = 'eada343fc415625494bfd1b065ba...';
// $app->config['SIGNING_KEY'] = 'ab2403a36467b59b20cc314bb211e18...';
// $app->config['JWT_KEY'] = 'fkeVxeElykoCBzRTIUjxwTD9MIg71nXxOEQ...';
// The Request object has three functions that use the config keys to read
// and verify the secure cookies. If the cookies don't exist, are invalid,
// expired, etc then [null] will be returned.
$req = new \FastSitePHP\Web\Request();
$decrypted = $req->decryptedCookie('encrypted');
$verified = $req->verifiedCookie('signed');
$jwt = $req->jwtCookie('jwt');
// Encrypted and Signed Data can be of any basic type [Strings, Numbers,
// Objects, etc], while JWT's require an Object or an Array/Dictionary.
$text = 'Request Time: ' . date(DATE_RFC2822);
$user = new \stdClass;
$user->id = 123;
$user->name = 'Admin';
$user->role = 'Admin';
// To send with the Response pass data to the corresponding response method.
// An optional 3rd parameter exits for an expiration time for both
// [signedCookie()] and [jwtCookie()] that defaults to 1 hour. This
// applies to the signed data or JWT and not the cookie itself.
$res = new \FastSitePHP\Web\Response();
$res->encryptedCookie('encrypted', $text);
$res->signedCookie('signed', $user, '+20 minutes');
$res->jwtCookie('jwt', $user, '+20 minutes');
Connect to a Database and run SQL Statements
// FastSitePHP provides a Database class which is a thin wrapper for PDO to
// reduce the amount of code needed when querying a database. An additional
// example on this page shows how to use PDO.
// Connect to a Database, this example uses SQLite with a temp in-memory db.
$dsn = 'sqlite::memory:';
$db = new \FastSitePHP\Data\Database($dsn);
// Depending on the connection 4 additional parameters can also be used:
/*
$user = null;
$password = null;
$persistent = false;
$options = [];
$db = new Database($dsn, $user, $password, $persistent, $options);
*/
// Create tables and test records. The function [execute()] is used for
// action queries (INSERT, UPDATE, DELETE, CREATE, etc) and returns the
// number of affected rows.
$db->execute('CREATE TABLE page_types (id INTEGER PRIMARY KEY, page_type)');
$sql = 'CREATE TABLE pages (id INTEGER PRIMARY KEY AUTOINCREMENT,';
$sql .= ' type_id, title, content)';
$db->execute($sql);
// This example uses a double-quotes for the string ["] because SQL strings
// include the single-quote character ['] for text.
$sql = "INSERT INTO page_types (id, page_type) VALUES (1, 'text/plain')";
$rows_added = $db->execute($sql);
// An optional 2nd parameter for parameters can be used. This is recommended
// when for user input to prevent SQL Injection Attacks. The Question Mark [?]
// is the placeholder character to use in the SQL statement.
$sql = 'INSERT INTO page_types (id, page_type) VALUES (?, ?)';
$params = [2, 'text/html'];
$rows_added += $db->execute($sql, $params);
// Multiple records can be added (or updated, etc) when using [executeMany()]
$sql = 'INSERT INTO pages (type_id, title, content) VALUES (?, ?, ?)';
$records = [
[1, 'Text Test Page', 'This is a test.'],
[2, 'HTML Test Page', '<h1>Test<h1><p>This is a test.</p>'],
];
$rows_added += $db->executeMany($sql, $records);
// In addition to using [?] you can also used named parameters in the
// format of ":name". Named parameters can make the code easier to read.
$sql = 'INSERT INTO pages (type_id, title, content)';
$sql .= ' VALUES (:type_id, :title, :content)';
$params = [
'type_id' => 1,
'title' => 'Named Parameters',
'content' => 'Test with Named Parameters.',
];
$rows_added += $db->execute($sql, $params);
// Get the id of the last inserted row or sequence value
$last_id = $db->lastInsertId();
// Query for Multiple Records
// Returns an Array of Records (Associative Array for each Record).
$sql = 'SELECT * FROM pages';
$records = $db->query($sql);
// Query for one record. Returns an Associative Array or [null] if not found.
// Both [query()] and [queryOne()] support optional parameters when querying.
$sql = 'SELECT * FROM pages WHERE id = ?';
$params = [1];
$record = $db->queryOne($sql, $params);
// The [Database] class also contains additional functions such as
// [queryValue(), queryList(), querySets()] to simplify and reduce
// the amount code needed when working with databases.
Connect to a Database and run SQL Statements using PDO
// Connect to a Database using PHP Data Objects (PDO),
// this example uses SQLite with a temp in-memory db.
$dsn = 'sqlite::memory:';
$user = null;
$password = null;
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
];
$pdo = new \PDO($dsn, $user, $password, $options);
// Create tables and test records.
$pdo->query('CREATE TABLE page_types (id INTEGER PRIMARY KEY, page_type)');
$sql = 'CREATE TABLE pages (id INTEGER PRIMARY KEY AUTOINCREMENT,';
$sql .= ' type_id, title, content)';
$pdo->query($sql);
// This example uses a double-quotes for the string ["] because SQL strings
// include the single-quote character ['] for text.
$sql = "INSERT INTO page_types (id, page_type) VALUES (1, 'text/plain')";
$stmt = $pdo->query($sql);
$rows_added = $stmt->rowCount();
// This example uses a prepare statement with an array of parameters. This is
// recommended when for user input to prevent SQL Injection Attacks. The
// Question Mark [?] is the placeholder character to use in the SQL statement.
$sql = 'INSERT INTO page_types (id, page_type) VALUES (?, ?)';
$params = [2, 'text/html'];
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$rows_added += $stmt->rowCount();
// Multiple records can be added (or updated, etc) in a loop
// using a prepared statement.
$sql = 'INSERT INTO pages (type_id, title, content) VALUES (?, ?, ?)';
$records = [
[1, 'Text Test Page', 'This is a test.'],
[2, 'HTML Test Page', '<h1>Test<h1><p>This is a test.</p>'],
];
$stmt = $pdo->prepare($sql);
foreach ($records as $record) {
$stmt->execute($record);
$rows_added += $stmt->rowCount();
}
// In addition to using [?] you can also used named parameters in the
// format of ":name". Named parameters can make the code easier to read.
$sql = 'INSERT INTO pages (type_id, title, content)';
$sql .= ' VALUES (:type_id, :title, :content)';
$params = [
'type_id' => 1,
'title' => 'Named Parameters',
'content' => 'Test with Named Parameters.',
];
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$rows_added += $stmt->rowCount();
// Get the id of the last inserted row or sequence value
$last_id = $pdo->lastInsertId();
// Query for Multiple Records
// Returns an Array of Records (Associative Array for each Record).
$sql = 'SELECT * FROM pages';
$stmt = $pdo->query($sql);
$records = $stmt->fetchAll(\PDO::FETCH_ASSOC);
// Query for one record using parameters. Returns an Associative Array
// or [false] if not found.
$sql = 'SELECT * FROM pages WHERE id = ?';
$params = [1];
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$record = $stmt->fetch(\PDO::FETCH_ASSOC);
// Functions [fetchAll()] and [fetch()] also support a number of options
// for the return format including Indexed-Arrays using [PDO::FETCH_NUM],
// Anonymous Objects using [PDO::FETCH_OBJ] and custom classes using
// [PDO::FETCH_CLASS].
Connect to a Database
// FastSitePHP’s Database class or PHP's built-in PDO class can connect to
// different databases. FastSitePHP’s Database class provides a thin wrapper
// over PDO to reduce the amount of code needed when querying a database.
// Examples below shows how to build connection strings and run a query for
// a number of different databases. If you download this site, the code below
// can be modified and tested for your environment; or simply copy what you
// need to your site or app.
// When specifying the hostname (Server Name), you can often specify just the
// server name (example: 'db-server') or the fully-qualified domain name (FQDN)
// (example 'db-server.example.com') based on how your network is setup.
// For example on an internal network simply using the server name will work
// but through VPN using the FQDN is often required.
// ----------------------------------------------------------------------------
// MySQL
// Basic Format:
// "mysql:host={hostname};dbname={database}";
//
// This example also shows using the [MYSQL_ATTR_INIT_COMMAND]
// option to se the timezone to UTC when the connection is created.
//
// If you have a site or application that has users in multiple timezones or
// countries an application design that works well is to save all dates and
// times in UTC and then format based on the users selected timezone.
//
$dsn = 'mysql:host=localhost;dbname=wordpress;charset=utf8';
$user = 'root';
$password = 'wordpress';
$options = [
\PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '+00:00'",
];
$sql = 'SELECT table_schema, table_name';
$sql .= ' FROM information_schema.tables';
$sql .= " WHERE table_type = 'BASE TABLE'";
// ----------------------------------------------------------------------------
// Oracle
// Format:
// "oci:dbname=//{hostname}:{port-number}/{database}"
$dsn = 'oci:dbname=//server:1521/hr';
$user = 'sys';
$password = 'password';
$options = [];
$sql = 'SELECT OWNER, TABLE_NAME FROM ALL_TABLES ORDER BY OWNER, TABLE_NAME';
// In addition to the standard format you can also specify a full TNS string
$tns = '(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)';
$tns .= '(HOST=server.example.com)(PORT=1521)))';
$tns .= '(CONNECT_DATA=(SERVICE_NAME=dbname)))';
$dsn = 'oci:dbname=' . $tns;
// ----------------------------------------------------------------------------
// SQL Server
$dsn = 'sqlsrv:Server=db-server;Database=DbName';
$user = 'sa';
$password = 'password';
$options = [];
$sql = 'SELECT SCHEMA_NAME(schema_id) AS schema_name, name FROM sys.tables';
// SQL Server (using ODBC)
// If the native SQL Server PDO driver is not installed and the
// PDO ODBC Driver is installed and a ODBC Connection is setup
// you could use this:
$dsn = 'odbc:DRIVER={SQL Server};SERVER=db-server;DATABASE=DbName;';
// ----------------------------------------------------------------------------
// IBM (using ODBC)
// This example shows a connection to and IBM DB2 or AS/400 through iSeries.
// ODBC Options will vary based on the driver installed and used.
$dsn = 'odbc:DRIVER={iSeries Access ODBC Driver};';
$dsn .= 'HOSTNAME=AS400.EXAMPLE.COM;';
$dsn .= 'PORT=56789;';
$dsn .= 'SYSTEM=SYSTEM;';
$dsn .= 'PROTOCOL=TCPIP;';
$dsn .= 'UID=USER;';
$dsn .= 'PWD=PASSWORD;';
$user = null;
$password = null;
$options = [];
$sql = 'SELECT SYSTEM_TABLE_SCHEMA, TABLE_NAME, TABLE_TEXT';
$sql .= ' FROM QSYS2.SYSTABLES';
$sql .= " WHERE SYSTEM_TABLE_SCHEMA IN 'QSYS'";
$sql .= ' ORDER BY SYSTEM_TABLE_SCHEMA, TABLE_NAME';
$sql .= ' FETCH FIRST 100 ROWS ONLY';
// ----------------------------------------------------------------------------
// PostgreSQL
$dsn = 'pgsql:host=localhost;port=5432;dbname=dbname;';
$user = 'postgres';
$password = 'password';
$options = [];
$sql = 'SELECT table_schema, table_name';
$sql .= ' FROM information_schema.tables';
$sql .= " WHERE table_type = 'BASE TABLE'";
// ----------------------------------------------------------------------------
// SQLite
// Example using a file path:
// 'sqlite:/var/www/app_data/db.sqlite'
// 'sqlite:C:\inetpub\wwwroot\db.sqlite'
// In-Memory Database:
// 'sqlite::memory:'
$dsn = 'sqlite:' . $file_path;
$user = null;
$password = null;
$options = [];
$sql = 'SELECT * FROM sqlite_master';
// ----------------------------------------------------------------------------
// Persistent Connection Option
//
// Many PHP Database drivers supports persistent connections
// which can allow for better performance.
$persistent = false;
// ============================================================================
// Connect using PHP Data Objects (PDO)
$options[\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_EXCEPTION;
if ($persistent) {
$options[\PDO::ATTR_PERSISTENT] = true;
}
$pdo = new \PDO($dsn, $user, $password, $options);
// Query using PDO
$stmt = $pdo->query($sql);
$records = $stmt->fetchAll(\PDO::FETCH_ASSOC);
// =================================================================================
// Connect and Query using FastSitePHP's Database class.
// Only the DSN (Data Source Name) is a required parameter.
$db = new \FastSitePHP\Data\Database($dsn, $user, $password, $persistent, $options);
$records = $db->query($sql);
// =================================================================================
// In addition to FastSitePHP's Database class [OdbcDatabase] and [Db2Database]
// can also be used for supported enviroments, and especially for IBM Databases.
//
// When using the class [OdbcDatabase] the DSN will be the same as the PDO DSN
// excluding the 'odbc:' prefix.
/*
$odbc = new OdbcDatabase($dsn, $user, $password, $persistent, $options);
$db2 = new Db2Database($dsn, $user, $password, $persistent, $options);
*/
// ============================================================================
// Lazy Loading with FastSitePHP
//
// FastSitePHP’s Application object has a function [lazyLoad()] which accepts
// a property name and callback function. It creates the object as a property
// of the app only if used. This is ideal for working with sites where some
// pages connect to a database and some pages do not, or if you have a site
// that connects to multiple databases but not all pages use each database.
$app->lazyLoad('db', function() use ($dsn, $user, $password) {
return new \FastSitePHP\Data\Database($dsn, $user, $password);
});
// Query for records. The database gets connected to here only when first used.
$records = $app->db->query($sql);
// ============================================================================
// To obtain a list of available drivers on the computer call [phpinfo()]
// and view the result or call the following function to get an array of
// driver names. A full list of PDO Drivers can be found at:
// http://php.net/manual/en/pdo.drivers.php
// If you need a driver and it is not available or enabled on your server
// they are generally easy to install and enable.
$drivers = \PDO::getAvailableDrivers();
Validating User Input
// For many apps validating client side (webpage or app) provides instant
// feedback to users and limits need for extra web request, however users
// can bypass validation by using DevTools or other methods so for data
// that needs to be validated using server-side validation is important.
// FastSitePHP provides a class that allows for many rules to be easily
// defined and run against an object (or Associative Array/Dictionary).
// Common rules can simply be copied from HTML Input controls.
// HTML Example:
/*
<input name="name" title="Name" required>
<input name="age" title="Age" required min="13" max="99">
<input name="phone" title="Phone" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}">
*/
// FastSitePHP Code to Validate Form Post using the above HTML.
// Form Post Fields come in the PHP Superglobal array [$_POST]
// and it simply be passed to the [Validator] class.
$v = new \FastSitePHP\Data\Validator();
$v->addRules([
// Field, Title, Rules
['name', 'Name', 'required'],
['age', 'Age', 'required min="13" max="99"'],
['phone', 'Phone', 'pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"'],
]);
list($errors, $fields) = $v->validate($_POST);
if ($errors) {
// Error Logic
// [$errors] returns an array of error messages for the end user
// [$fields] returns an array of unique fields that had an error
// along with an array of error messages for each field.
// Fields can be used by a client app to highlight form fields, etc.
}
// In addition to using strings for the rules you can also use arrays.
// This can provide better performance if you have a high traffic site,
// however it runs very fast either way.
$v = new \FastSitePHP\Data\Validator();
$v->addRules([
['name', 'Name', ['required' => true]],
['age', 'Age', [
'required' => true,
'min' => '13',
'max' => '99',
]],
['phone', 'Phone', ['pattern' => '[0-9]{3}-[0-9]{3}-[0-9]{4}']],
]);
// The validator class supports a number of HTML5 rules along
// with some custom rules:
// 'exists', 'required', 'type', 'minlength', 'maxlength',
// 'length', 'min', 'max', 'pattern', 'list',
// The [type] rule supports a number of HTML5 data types along
// with many custom data types:
// 'text', 'password', 'tel', 'number', 'range', 'date',
// 'time', 'datetime', 'datetime-local', 'email', 'url',
// 'unicode-email', 'int', 'float', 'json', 'base64',
// 'base64url', 'xml', 'bool', 'timezone', 'ip', 'ipv4',
// 'ipv6', 'cidr', 'cidr-ipv4', 'cidr-ipv6',
// In addition to standard rules custom rules can be defined using
// callback functions that return true/false or a custom error
// message string:
$v
->addRules([
['site_user', 'Site User', 'check-user required'],
['site_password', 'Password', 'check-password required'],
])
->customRule('check-user', function($value) {
return ($value === 'admin');
})
->customRule('check-password', function($value) {
return ($value === 'secret' ? true : 'Invalid Password');
});
list($errors, $fields) = $v->validate($_POST);
Using the HTTP Client
// The HttpClient can be used to simplify communication with other Web Services,
// HTTP API’s, and works great for calling and returning the result of local
// services – for example an AI/ML (Artificial Intelligence / Machine Learning)
// Service written in Python with TensorFlow or scikit-learn.
// Perform a simple HTTP GET Request and check the result
$res = \FastSitePHP\Net\HttpClient::get($url);
if ($res->error) {
// An error would be returned in the event of a major failure such as
// a timeout or SSL Cert Error. A 404 or 500 Response from the server
// would be handled by checking the [status_code].
$error = $res->error;
} else {
$status_code = $res->status_code; // 200, 404, 500, etc
$headers = $res->headers; // Array of Response Headers
$content = $res->content; // Response Content as a String - HTML, Text, etc
$info = $res->info; // Array of Info such as Time Stats
}
// Perform an HTTP GET Request and read the JSON Result. If the Response
// Content-Type is 'application/json' then [$res->json] will contain an array
// otherwise null. Request Headers can be passed an optional paramater.
$headers = [
'X-API-Key' => 'ab82050cf5907934fa1d0f6f66284642a01d1ba2280656870c',
'X-Custom-Header' => 'Test',
];
$res_json = \FastSitePHP\Net\HttpClient::get($url, $headers);
$json = $res->json;
$text = $res->content;
// Submit a HTTP POST Request as JSON and also as a Form.
// Data can be either an Array or Object and Headers are optional.
$data = [
'text' => 'test',
'num' => 123,
];
$res_post = \FastSitePHP\Net\HttpClient::postJson($url, $data, $headers);
$res_form = \FastSitePHP\Net\HttpClient::postForm($url, $data);
// When using PHP 5.5 or later 'multipart/form-data' Form Posts are supported
// with the PHP built-in class [CURLFile]:
/*
$data = [
'field1' => 'test',
'file' => new \CURLFile($file_path),
];
*/
// Save the Response Content as a File Download
// Just like [postJson()] and [postForm()] Request Headers are optional.
$res_file = \FastSitePHP\Net\HttpClient::downloadFile($url, $save_path, $headers);
$saved_path = $res_file->content;
// The above code demo shows the 4 helper static functions [get(), postJson(),
// postForm(), and downloadFile()], additional options are available when using
// the HttpClient as an object with the [request()] method.
// Submit a PUT Request with a file as the Request Body
$http = new \FastSitePHP\Net\HttpClient();
$res_put = $http->request($url, [
'method' => 'PUT',
'headers' => $headers,
'send_file' => $file_path,
]);
GraphQL Service using HttpClient
// GraphQL is a popular technology for developing API's. It has been ported to
// many languages including PHP, however the reference implementation, the most
// commonly used version, and also high in performance is GraphQL with NodeJS
// and Express. This route can be copied or modified to allow GraphQL from PHP
// using any GraphQL service on localhost or from another URL.
$app->route('/graphql', function() {
try {
$url = 'http://localhost:4000/graphql';
// If an 'Authorization' Request Header was
// sent then pass it to the GraphQL Service.
$req = new \FastSitePHP\Web\Request();
$auth = $req->header('Authorization');
$headers = ($auth === null ? null : ['Authorization' => $auth]);
// Submit GraphQL Request
if ($req->method() === 'GET') {
$url .= '?query=' . urlencode($req->queryString('query'));
$url .= '&variables=' . urlencode($req->queryString('variables'));
$url .= '&operationName=' . urlencode($req->queryString('operationName'));
$res = \FastSitePHP\Net\HttpClient::get($url, $headers);
} else {
$res = \FastSitePHP\Net\HttpClient::postJson(
$url,
$req->content(),
$headers
);
}
// Check Response, an error typically would occur not for data
// errors but rather HTTP errors (i.e.: If the service is down).
if ($res->error) {
throw new \Exception($res->error);
}
// Return Object for JSON Response
return $res->json;
} catch (\Exception $e) {
// Return unexpected error as a 200 response
// using standard error format used by GraphQL.
return [
'errors' => [
['message' => $e->getMessage()]
],
];
}
})->filter(function() use ($app) {
// Use CORS to allow web pages to access this service from any host (URL)
if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN'] !== 'null') {
$app->cors([
'Access-Control-Allow-Origin' => $_SERVER['HTTP_ORIGIN'],
'Access-Control-Allow-Headers' => 'Authorization, Content-Type',
'Access-Control-Allow-Credentials' => 'true',
]);
} else {
$app->cors('*');
}
});
Send an Email through an SMTP Server
// Define Email Settings
$from = 'noreply@example.com';
$to = 'user.name@example.com';
$subject = 'Email Test from FastSitePHP at ' . date(DATE_RFC2822);
$body = '<h1>Email Title</h1><p style="color:blue;">This is a test.</p>';
// Create an Email Object
$email = new \FastSitePHP\Net\Email($from, $to, $subject, $body);
// The Email Class also has many additional settings and can be created
// without specifying any parameters. When setting [From] or [Reply-To]
// email addresses on of the following formats can be used:
// String: 'Email Address'
// Array: ['Email', 'Name']
// And when specifying who to send email to any of the formats can be used:
// String 'Email Address'
// Array: ['Email', 'Name']
// Array: ['Email Address 1', 'Email Address 2', '...']
/*
$email = new \FastSitePHP\Net\Email();
$email
->from(['noreply@example.com', 'No Reply'])
->replyTo('test@example.com')
->to(['email1@example.com', 'email2@example.com'])
->cc('email3@example.com')
->bcc('email4@example.com')
->priority('High')
->header('X-Transaction-ID', '123abc');
*/
// File attachements are also supported:
//
// $email->attachFile($file_path);
// SMTP Servers that support Unicode Emails can use [allowUnicodeEmails(true)].
// When used the SMTP Client sends a SMTPUTF8 option if the server supports it.
//
// $email->allowUnicodeEmails(true)->from('无回复@example.com');
// SMTP Settings
$host = 'smtp.example.com';
$port = 25;
$auth_user = null;
$auth_pass = null;
// Create SMTP Client and Send Email.
// Once the variable for the SMTP Client is not longer used (or set to null)
// then it automatically sends a 'QUIT' command to the SMTP Server and closes
// the connection.
$smtp = new \FastSitePHP\Net\SmtpClient($host, $port);
if ($auth_user !== null) {
$smtp->auth($auth_user, $auth_pass);
}
$smtp->send($email);
$smtp = null;
// Additional options can be specified for timeout (in seconds) and for logging
$timeout = 2;
$debug_callback = function($message) {
echo '[' . date('H:i:s') . '] ' . trim($message) . "\n";
};
// The [SmtpClient] Class also supports an easy to use API for communicating
// with SMTP Servers. In this example Gmail is used and several commands are
// performed. Messages are logged to the [$debug_callback] function.
$host = 'smtp.gmail.com';
$port = 587;
$smtp2 = new \FastSitePHP\Net\SmtpClient($host, $port, $timeout, $debug_callback);
$smtp2->help();
$smtp2->noop();
$smtp2->quit();
$smtp2->close();
// One or more emails can also be sent using App Config Values or System
// Enviroment Variables. This type of setup can be used to prevent sensitive
// authentication info from being saved with the main code logic.
/*
$app->config['SMTP_HOST'] = $host;
$app->config['SMTP_PORT'] = $port;
$app->config['SMTP_TIMEOUT'] = $timeout;
$app->config['SMTP_USER'] = $auth_user;
$app->config['SMTP_PASSWORD'] = $auth_pass;
\FastSitePHP\Net\SmtpClient::sendEmails([$email]);
*/
Search for Files and Directories (Folders)
// Create a FileSystem Search Object
$search = new \FastSitePHP\FileSystem\Search();
// For basic usage specify a root directory with the [dir()] command and then
// call either [files()] or [dirs()]. An array of matching names will be returned.
$files = $search->dir($dir_path)->files();
// [all()] can be used to return both directories and files
list($dirs, $files) = $search->dir($dir_path)->all();
// Functions are chainable so breaking them up
// one per line can make the code easier to read.
$dirs = $search
->dir($dir_path)
->dirs();
// URL lists can also be generated from matching files.
$url_root = 'http://www.example.com/';
$urls = $search
->dir($dir_path)
->urlFiles($url_root);
// A number of different criteria functions exist and can be used to filter
// the results. In this example a recursive search is used to find PHP files
// that contain the text 'FileSystem'. When a recursive search is used the
// full file paths are returned unless [includeRoot(false)] is set.
// See documentation and examples for all functions.
$files = $search
->dir($dir_path)
->recursive(true)
->fileTypes(['php'])
->includeText(['FileSystem'])
->files();
File System Sync
// Create a FileSystem Sync Object
$sync = new FastSitePHP\FileSystem\Sync();
// Sync files and directories (folders) from [dirFrom(path)] to [dirTo(path)].
// The sync is recursive so all files and directories are synced in all
// sub-directories. Required functions are [dirFrom, dirTo, and sync].
// To view the results call [printResults()] after calling [sync()].
// All options with defaults are shown below.
$sync
->dirFrom($dir_from)
->dirTo($dir_to)
->excludeNames(['package-lock.json'])
->excludeRegExPaths(['/node_modules/'])
->summaryTitle('File System Sync Results')
->hashAlgo('sha256')
->dryRun(false) // Set to [true] for testing
->sync()
->printResults();
Convert Markdown to HTML using PHP
// FastSitePHP includes the high performance library Parsedown for
// converting Markdown format to HTML.
// Make sure to load the vendor autoloader
require '../../../vendor/autoload.php';
// Create Parsedown Object
$Parsedown = new Parsedown();
// Convert to HTML from a Text String
$html = $Parsedown->text('Hello **FastSitePHP**!');
// Read a File and convert to HTML
$file_path = __DIR__ . '/views/example.md';
$md = file_get_contents($file_path);
$html = $Parsedown->text($md);
Logging
// FastSitePHP includes two logging classes that implement the widely used
// [Psr\Log] Interface.
// Create a file logger. Log messages are appended and the file is created
// the when the first message is added.
$file = __DIR__ . '/log.txt';
$file_logger = new \FastSitePHP\Data\Log\FileLogger($file);
// Create an HTML Logger
// This class can be used for temporary development logs because it outputs an
// HTML table of logged messages after the response is sent or depending on
// options can be used to replace the original response. The parameter
// [$replace_response] is optional.
$replace_response = false;
$html_logger = new \FastSitePHP\Data\Log\HtmlLogger($app, $replace_response);
// Log messages using one of the following functions:
// emergency(), alert(), critical(), error(),
// warning(), notice(), info(), debug()
$file_logger->info('This is a Test.');
$html_logger->error('Application Test');
// Additionally data can be passed to the message with placeholders
$html_logger->info('User {name} created', [
'name' => 'Admin'
]);
// The date format can be any valid value for the PHP function [date()].
// Default is [\DateTime::ISO8601].
$file_logger->date_format = 'Y-m-d H:i:s';
// For the file logger the output format can be controlled by properties.
//
// Default Format:
// '{date} {level} - {message}{line_break}';
//
// Line Breaks default based on the OS:
// "\r\n" - Windows
// "\n" - Other OS's
$file_logger->log_format = '[{level}] {message}{line_break}';
$file_logger->line_break = '^^';
// You can also customize the HTML Logger with your own template:
// $html_logger->temlate_file = 'YOUR_TEMPLATE.php';
Get Network and Server Info
// Create a Networking Config Object
$config = new \FastSitePHP\Net\Config();
// Get a (fqdn) 'fully-qualified domain name' for the server ['server.example.com']
$host = $config->fqdn();
// Get the Network IPv4 Address for the computer or server
$ip = $config->networkIp();
// Get a list of all IPv4 Addresses for the computer or server
$ip_list = $config->networkIpList();
// Get a text string of info from the server using one of the following commands:
// - Linux / Unix = [ip addr] or [ifconfig]
// - Mac = [ifconfig]
// - Windows = [ipconfig]
$info = $config->networkInfo();
// Convert the Network Info String to an Object
$info = $config->parseNetworkInfo($info);
Get Environment and System Info
// Create an Environment System Object
$sys = new \FastSitePHP\Environment\System();
// Get an array of basic information related to the Operating System
// [ 'OS Type', 'Version Info', 'Release Version', 'Host Name', 'CPU Type' ]
$os_info = $sys->osVersionInfo();
// Get a text string of detailed system info using one of the following commands:
// - Linux = File: '/etc/os-release'
// - FreeBSD = uname -mrs
// - IBM AIX = uname -a
// - Mac = system_profiler SPSoftwareDataType SPHardwareDataType
// - Windows = ver
$info = $sys->systemInfo();
// Get an array of information related to free, used, and total space for
// a filesystem drive or disk partition. This function allows for specific
// drives or partitions to be specified.
// - *nix = $sys->diskSpace('/dev/disk0')
// - Windows = $sys->diskSpace('C:')
$disk_space = $sys->diskSpace();
// Windows only function that returns an array of drive letters
// mapped to the server. Returns an empty array for other OS's.
$mapped_drives = $sys->mappedDrives();
Use a [.env] File
// Loads environment variables from a [.env] file into [getenv()] and [$_ENV].
// FastSitePHP's DotEnv is a port of the Node package [dotenv] so the same
// syntax used by node projects is supported.
$vars = \FastSitePHP\Environment\DotEnv::load($dir);
// Use variables from the file after reading it. Variables are only set
// from the file if they do not already exist.
$value = getenv('DB_CONNECTION');
$value = $_ENV['DB_CONNECTION'];
// Load a file using [.env] file format. The full path of the file is
// specified so it can be named anything.
$vars = \FastSitePHP\Environment\DotEnv::loadFile($file_path);
// Optionally require keys to exist in the file.
$required_vars = ['DB_ORACLE', 'DB_SQL_SERVER'];
$vars = \FastSitePHP\Environment\DotEnv::load($dir, $required_vars);
Security - Encrypt and Decrypt Data
// Generate a Key for Encryption.
// The key is a long hex string of secure random bytes.
// The key would typically be saved with your app or in config.
$crypto = new \FastSitePHP\Security\Crypto\Encryption();
$key = $crypto->generateKey();
// Encrypt and Decrypt using the Crypto Helper Class with Config Settings.
// Data of different data types can be encrypted and returned in the
// same format (string, int, object, etc).
$app->config['ENCRYPTION_KEY'] = $key;
$encrypted_text = \FastSitePHP\Security\Crypto::encrypt($data);
$decrypted_data = \FastSitePHP\Security\Crypto::decrypt($encrypted_text);
// Encrypt and Decrypt using the Encryption Class. This class
// provides many additional options that are not in the helper class.
$encrypted_text = $crypto->encrypt($data, $key);
$decrypted_data = $crypto->decrypt($encrypted_text, $key);
Security - Encrypt and Decrypt a File
// FastSitePHP allows for fast authenticated encryption of any size file
// (even large files that are many gigs in size). The code used for encryption
// is compatible with shell commands and a Bash Script [encrypt.sh] that works
// on Linux and Unix Computers. The Bash Script can be downloaded from this site,
// and will work on most Linux OS's without having to install anything.
// Generate a Key for Encryption
$crypto = new \FastSitePHP\Security\Crypto\FileEncryption();
$key = $crypto->generateKey();
// Build file paths of files to save based on the original name
$enc_file = $file_path . '.enc';
$output_file = $enc_file . '.decrypted';
// Encrypt and Decrypt using the Crypto Helper Class with Config Settings.
// A [FileEncryption] class also exists with additional options.
$app->config['ENCRYPTION_KEY'] = $key;
\FastSitePHP\Security\Crypto::encryptFile($file_path, $enc_file);
\FastSitePHP\Security\Crypto::decryptFile($enc_file, $output_file);
Security - Encode and Decode a JSON Web Token (JWT)
// The JWT Payload can be either an Object or an Array (Dictionary).
$payload = [
'User' => 'John Doe',
'Roles' => ['Admin', 'SQL Editor']
];
// Generate a Key for Encoding (Signing).
// The key is a long hex string of secure random bytes.
// The key would typically be saved with your app or in config.
$jwt = new \FastSitePHP\Security\Crypto\JWT();
$key = $jwt->generateKey();
// Encode and Decode JWT with the Crypto Helper Class with Config Settings.
// When using the default parameters with the helper class the data has a
// 1-hour timeout.
$app->config['JWT_KEY'] = $key;
$token = \FastSitePHP\Security\Crypto::encodeJWT($payload);
$data = \FastSitePHP\Security\Crypto::decodeJWT($token);
// Encode (Sign) and Decode (Verify) using the JWT Class. When using
// default settings with the JWT Class, not timeout is specified, all
// claims are validated, and a secure key is required.
$token = $jwt->encode($payload, $key);
$data = $jwt->decode($token, $key);
// Add Claims to the Payload and use an Insecure Key for Compatibility
// with other sites (Often online demos of JWT are shown using simple
// passwords for the key). By default keys are required to be secure
// with proper length and in either Base64 or Hex format.
$payload = $jwt->addClaim($payload, 'exp', '+10 minutes');
$payload = $jwt->addClaim($payload, 'iss', 'example.com');
$jwt
->useInsecureKey(true)
->allowedIssuers(['example.com']);
$insecure_key = 'password123';
$token = $jwt->encode($payload, $insecure_key);
$data = $jwt->decode($token, $insecure_key);
Security - Encode and Decode JWT using RSA
// The JWT Payload can be either an Object or an Array (Dictionary).
$payload = new \stdClass;
$payload->User = 'John Doe';
$payload->Roles = ['Admin', 'SQL Editor'];
// Create JWT Class, specify 'RS256' Algoritm, and generate Key Pair
$jwt = new \FastSitePHP\Security\Crypto\JWT();
$jwt
->algo('RS256')
->allowedAlgos(['RS256']);
list($private_key, $public_key) = $jwt->generateKey();
// Encode (Sign) and Decode (Verify)
$token = $jwt->encode($payload, $private_key);
$data = $jwt->decode($token, $public_key);
Security - Sign and Verify Data
// Using the [SignedData] is similar in concept to using JWT.
// A client can read the data but not modify it.
// Generate a Key for Signing.
// The key is a long hex string of secure random bytes.
// The key would typically be saved with your app or in config.
$csd = new \FastSitePHP\Security\Crypto\SignedData();
$key = $csd->generateKey();
// Sign and Verify using the Crypto Helper Class with Config Settings.
// When using the default parameters with the helper class the data has
// a 1-hour timeout. Data of different data types can be signed and
// verified to the original format (string, int, object, etc).
$app->config['SIGNING_KEY'] = $key;
$signed_text = \FastSitePHP\Security\Crypto::sign($data);
$verified_data = \FastSitePHP\Security\Crypto::verify($signed_text);
// Sign and Verify using the SignedData Class. The SignedData Class
// allows for additional options and doesn't use config settings.
// The parameter [$expire_time] is optional.
$expire_time = '+20 minutes';
$signed_text = $csd->sign($data, $key, $expire_time);
$verified_data = $csd->verify($signed_text, $key);
Security - Hash and Verify Passwords
// Saving User Passwords using a one-way hashing function is important for
// secure applications. FastSitePHP’s Password class provides support for
// bcrypt (default) and Argon2.
// Example of a User Password, this value should not be saved to a database
$password = 'Password123';
// Create a Password Object
$pw = new \FastSitePHP\Security\Password();
// Hash the Password, this will create hash text that looks like this:
// '$2y$10$cDpu8TnONBhpBFPEKTTccu/mYhSppqNLDNCfOYLfBWI3K/FzFgC2y'
// The value will change everytime and is safe to save to a database.
$hash = $pw->hash($password);
// Verify a Password - returns [true] or [false]
$verified = $pw->verify($password, $hash);
// Create a randomly generated password that is 12 characters in length
// and contains the following:
// 4 Uppercase Letters (A - Z)
// 4 Lowercase Letters (a - z)
// 2 Digits (0 - 9)
// 2 Special Characters (~, !, @, #, $, %, ^, &, *, ?, -, _)
$strong_password = $pw->generate();
// Specify a different BCrypt Cost of 12 instead of the default value 10
$pw->cost(12);
$hash2 = $pw->hash($password);
$verified2 = $pw->verify($password, $hash2);
// When using PHP 7.2 or later Argon2 can be used
if (PHP_VERSION_ID >= 70200) {
$pw->algo('Argon2');
$argon_hash = $pw->hash($password);
$argon_verified = $pw->verify($password, $argon_hash);
}
Security - Generate a new RSA Key Pair
// Generate a new RSA Key Pair
$key_pair = \FastSitePHP\Security\Crypto\PublicKey::generateRsaKeyPair();
list($private_key, $public_key) = $key_pair;
// Generate a new 3072-Bit RSA Key
$bits = 3072;
$key_pair = \FastSitePHP\Security\Crypto\PublicKey::generateRsaKeyPair($bits);
list($private_key2, $public_key2) = $key_pair;
Generate a string of random bytes
// Generate cryptographically secure pseudo-random bytes that
// are suitable for cryptographic use and secure applications.
$bytes = \FastSitePHP\Security\Crypto\Random::bytes(32);
// Convert the bytes to another format:
$hex_bytes = bin2hex($bytes);
$base64_bytes = base64_encode($bytes);
// When using PHP 7 or newer you can simply call [random_bytes()]
$bytes = random_bytes(32);
Security - CSRF using Session
// One call to a static function creates a token on GET Requests
// and validates it with Requests POST, PUT, DELETE, etc.
// If there is an error with the token then an exception is
// thrown which will cause 500 response with the error page.
\FastSitePHP\Security\Web\CsrfSession::setup($app);
// The token is assigned a locals value in the Application Object
$token = $app->locals['csrf_token'];
// This allows it to be used with templating code.
// Tokens are validated from [setup()] but not automatically added
// to forms so they must be added through templating or by code.
//
// <meta name="X-CSRF-Token" content="{{ $csrf_token }}">
// <input name="X-CSRF-Token" value="{{ $csrf_token }}">
// A good place to call this function is on route filters
// of pages that use authentication. Example:
// Create a filter function to assign to multiple routes
$csrf_session = function() use ($app) {
\FastSitePHP\Security\Web\CsrfSession::setup($app);
};
// Use the function when defining a route
$app->get('/form', function() use ($app) {
return $app->render('form.php');
})
->filter($csrf_session);
Security - Stateless CSRF
// Stateless CSRF Tokens are not stored in Session but rather use a crypto
// keyed-hash message authentication code (HMAC) to create and verify the token.
// A secure secret key is requird.
// The key would typically be saved with your app or in config.
$key = \FastSitePHP\Security\Web\CsrfStateless::generateKey();
// To use the Key it must be saved to either a config value or
// an environment variable before calling [setup()].
$app->config['CSRF_KEY'] = $key;
// putenv("CSRF_KEY=${key}");
// A unique identifier for the user is also required. This doesn't have
// to be secret and can be a simple as an numeric field in a database.
$user_id = 1;
// Setup and validate stateless CSRF Tokens
\FastSitePHP\Security\Web\CsrfStateless::setup($app, $user_id);
// Optionally add a timeout, this CSRF token will expire after 5 minutes
$expire_time = '+5 minutes';
\FastSitePHP\Security\Web\CsrfStateless::setup($app, $user_id, $expire_time);
// The same logic is used when using the [CsrfSession] class so
// the token is assigned a locals value in the Application Object
// which allows for it to be used with templating code.
$token = $app->locals['csrf_token'];
//
// <meta name="X-CSRF-Token" content="{{ $csrf_token }}">
// <input name="X-CSRF-Token" value="{{ $csrf_token }}">
// Also just like [CsrfSession] a good place to call [setup()]
// is on route filter functions.
$csrf = function() use ($app, $user_id) {
\FastSitePHP\Security\Web\CsrfStateless::setup($app, $user_id);
};
$app->get('/form', function() use ($app) {
return $app->render('form.php');
})
->filter($csrf);
IP Addresses and Validation
// With FastSitePHP you can easily compare an IP Address to an accepted range
// of IP’s using CIDR Notation. CIDR Notation (Classless Inter-Domain Routing)
// is a compact representation of an IP address and its associated routing
// prefix. It is used regularly when working with digital networks and often
// needed for websites when handling IP Addresses for security.
// Check if IP Address '10.10.120.12' is in the '10.0.0.0/8' range
// Returns [true]
$matches = \FastSitePHP\Net\IP::cidr('10.0.0.0/8', '10.10.120.12');
// Check if IP Address '10.10.120.12' is in the '172.16.0.0/12' range
// Returns [false]
$matches2 = \FastSitePHP\Net\IP::cidr('172.16.0.0/12', '10.10.120.12');
// IPv6 is also supported
$matches3 = \FastSitePHP\Net\IP::cidr('fe80::/10', 'fe80::b091:1117:497a:9dc1');
// Get an array of Private Network Addresses in CIDR Notation
// [
// '127.0.0.0/8', // IPv4 localhost
// '10.0.0.0/8', // IPv4 Private Network, RFC1918 24-bit block
// '172.16.0.0/12', // IPv4 Private Network, RFC1918 20-bit block
// '192.168.0.0/16', // IPv4 Private Network, RFC1918 16-bit block
// '169.254.0.0/16', // IPv4 local-link
// '::1/128', // IPv6 localhost
// 'fc00::/7', // IPv6 Unique local address (Private Network)
// 'fe80::/10', // IPv6 local-link
// ]
$private_addr = \FastSitePHP\Net\IP::privateNetworkAddresses();
// The array from [privateNetworkAddresses()] can be used with the [cidr()]
// function to check if an IP address is from a private network or from the
// public internet. The [cidr()] function accepts the CIDR Parameter as
// either an array or a string.
$matches4 = \FastSitePHP\Net\IP::cidr($private_addr, '10.10.120.12');
// Get Info about a CIDR string when calling [cidr()] with only 1 parameter.
// This example returns the following:
// [
// 'CIDR_Notation' => '10.63.5.183/24',
// 'Address_Type' => 'IPv4',
// 'IP_Address' => '10.63.5.183',
// 'Subnet_Mask' => '255.255.255.0',
// 'Subnet_Mask_Bits' => 24,
// 'Cisco_Wildcard' => '0.0.0.255',
// 'Network_Address' => '10.63.5.0',
// 'Broadcast' => '10.63.5.255',
// 'Network_Range_First_IP' => '10.63.5.0',
// 'Network_Range_Last_IP' => '10.63.5.255',
// 'Usable_Range_First_IP' => '10.63.5.1',
// 'Usable_Range_Last_IP' => '10.63.5.254',
// 'Addresses_in_Network' => 256,
// 'Usable_Addresses_in_Network' => 254,
// ]
$info = \FastSitePHP\Net\IP::cidr('10.63.5.183/24');
// Example of CIDR Info when using IPv6:
// [
// 'CIDR_Notation' => 'fe80::b091:1117:497a:9dc1/48',
// 'Address_Type' => 'IPv6',
// 'IP_Address' => 'fe80::b091:1117:497a:9dc1',
// 'Subnet_Mask' => 'ffff:ffff:ffff::',
// 'Subnet_Mask_Bits' => 48,
// 'Network_Address' => 'fe80::',
// 'Network_Range_First_IP' => 'fe80::',
// 'Network_Range_Last_IP' => 'fe80::ffff:ffff:ffff:ffff:ffff',
// 'Addresses_in_Network' => '1208925819614629174706176',
// ]
$info_ip6 = \FastSitePHP\Net\IP::cidr('fe80::b091:1117:497a:9dc1/48');
File System Security
// The FileSystem Security Class contains functions for validating files.
// Prevent Path Traversal Attacks by verifying if a file name exists in a
// specified directory. Path Traversal Attacks can happen if a user is
// allowed to specify a file on a file system through input and uses a
// pattern such as '/../' to obtain files from another directory.
// Examples:
// Assume both files exist and would return [true] from built-in function
// [is_file()]. [false] would be returned for the 2nd file when using
// [Security::dirContainsFile()].
$file1 = 'user_image.jpg';
$file2 = '../../index.php';
$file_exists_1 = \FastSitePHP\FileSystem\Security::dirContainsFile($dir, $file1);
$file_exists_2 = \FastSitePHP\FileSystem\Security::dirContainsFile($dir, $file2);
// The function [dirContainsFile()] only allows for files directly under the root
// folder so another function exists to search sub-directories [dirContainsPath()].
$path1 = 'icons/clipboard.svg'; // Returns [true]
$path2 = '../../app/index.php'; // Returns [false]
$path_exists_1 = \FastSitePHP\FileSystem\Security::dirContainsPath($dir, $path1);
$path_exists_2 = \FastSitePHP\FileSystem\Security::dirContainsPath($dir, $path2);
// [dirContainsPath()] contains an optional 3rd parameter [$type] which defaults
// to 'file' and allows for one of the following options ['file', 'dir', 'all'].
$path3 = 'icons';
$exists = \FastSitePHP\FileSystem\Security::dirContainsPath($dir, $path3, 'dir');
// [dirContainsDir()] can be used to check directories/folders.
$dir1 = 'icons';
$dir2 = '../../app';
$dir_exists_1 = \FastSitePHP\FileSystem\Security::dirContainsDir($dir, $file1);
$dir_exists_2 = \FastSitePHP\FileSystem\Security::dirContainsDir($dir, $file2);
// Validate Image Files
// The [fileIsValidImage()] function can be used to verify if image files
// created from user input are valid. For example a malicious user may try
// to rename a PHP script or executable file as an image and upload it to
// a site. Returns [true] if an image file [jpg, gif, png, webp, svg]
// is valid and the file's extension matches the image type.
$is_image = \FastSitePHP\FileSystem\Security::fileIsValidImage($image_file);
Security - Rate Limiting
// Rate Limit Class
$rate_limit = new \FastSitePHP\Security\Web\RateLimit();
// Using the RateLimit class requires and instance of
// [\FastSitePHP\Data\KeyValue\StorageInterface].
// In this example SQLite is used. When multiple servers are used behind
// a load balancer an in-memory cache db such as Redis can be used.
$file_path = sys_get_temp_dir() . '/ratelimit-cache.sqlite';
$storage = new \FastSitePHP\Data\KeyValue\SqliteStorage($file_path);
// There are 2 required options [storage] and [id].
// [id] represents the user - IP Address, User ID, etc.
//
// [max_allowed] and [duration] will commonly be used and represent
// the rate at which the event is allowed. If not specified then a
// default of 1 is used which allows for 1 request per second.
$options = [
'max_allowed' => 1, // Requests, Events, etc
'duration' => 1, // In seconds
'storage' => $storage,
'id' => $_SERVER['REMOTE_ADDR'],
];
// Check the Request
list($allowed, $headers) = $rate_limit->allow($options);
// $allowed = bool
// $headers = Array of headers that can be used for logic
// or sent with the response
// One thing to be aware of when filtering by IP is that many users can have
// the same IP if they are accessing your site from the same office or location.
// Option examples:
// Limit to 10 requests every 20 seconds
$options = [ 'max_allowed' => 10, 'duration' => 20, ];
// Limit to 2 requests per minute
$options = [ 'max_allowed' => 2, 'duration' => 60, ];
// Limit to 2 requests per day
$options = [ 'max_allowed' => 10, 'duration' => (60 * 60 * 24), ];
// If using the [RateLimit] class for multiple
// uses then you need to specify an optional key.
$options = [ 'key' => 'messages-sent' ];
$options = [ 'key' => 'acounts-created' ];
// The [RateLimit] class allows for different rate limiting algorithms;
// the default is 'fixed-window-counter' which puts a fixed amount on
// the number of requests for the given duration but allows for bursts.
// The 'token-bucket' allows for rate limiting at a timed rate however
// it can allow for a higher number requests than the specified [max_allowed].
//
// For basic usage with a small number of [max_allowed] such as
// "1 request per second" they will behave the same, however if specifying
// a larger number such as "10 requests per 20 seconds" then there will
// be a difference so if you are using rate limiting for web requests with
// a larger number you may want to compare the differences using example code
// and see related links in the API docs.
//
$options = [ 'algo' => 'fixed-window-counter' ];
$options = [ 'algo' => 'token-bucket' ];
// The [filterRequest()] function can be used to filter the request.
// When used if the user's rate limit is reached then a 429 [Too Many Requests]
// response is sent and [exit()] is called to stop the script execution.
$filter_request = function() use ($app, $storage) {
// Get User IP (example if using a load-balancer)
$req = new \FastSitePHP\Web\Request();
$user_ip = $req->clientIp('from proxy');
// Check rate
$rate_limit = new \FastSitePHP\Security\Web\RateLimit();
$rate_limit->filterRequest($app, [
'storage' => $storage,
'id' => $user_ip,
]);
};
$app->get('/api', function() {})->filter($filter_request);
// When using [filterRequest()] the following Response Headers can sent
// to the client depending on which options are used:
// Retry-After Standard Header
// X-RateLimit-Limit Human readable description of the rate limit
// X-RateLimit-Remaining Requests allowed for the given time frame
// X-RateLimit-Reset Unix Timestamp for the limit to reset
Open and Edit Images Files
// Use the Media Image Class to open an image. If the image is invalid or the
// file extension doesn't match the file type then an exception will be thrown.
// Supported file extensions = [jpg, jpeg, gif, png, webp]
$img = new \FastSitePHP\Media\Image();
$img->open($file_path);
// Generate a Thumbnail or Resize the Image to a specified max width and height.
//
// When both width and height are specified the image will be sized to the
// smaller of the two values so it fits. If only width or only height are
// specified then image will be sized proportionally to the value.
$max_width = 200; // Pixels
$max_height = 200;
$img->resize($max_width, $max_height);
// Images can also be cropped to a specific dimensions.
// This can be used with JavaScript or App cropping libraries to allow users
// to generate thumbnails from a full uploaded image. For example allow
// a user to crop an uploaded image to a profile thumbnail.
$left = 50;
$top = 40;
$width = 120;
$height = 80;
$target_width = $width * 2; // Optional
$target_height = $height * 2; // Optional
$img->crop($left, $top, $width, $height, $target_width, $target_height);
// Images can be rotated which is useful for sites that allow users to upload
// images because images can often upload with incorrect rotation depending on
// the mobile device or a user may simply want to change the rotation.
$degrees = 180;
$img->rotateLeft();
$img->rotateRight();
$img->rotate($degrees);
// Save Quality (0 to 100) can be specified when saving JPG or WEBP images.
// And Compression-Level (0 to 9) can specified when saving PNG files.
$img->saveQuality(90); // Default Quality
$img->pngCompression(6); // Default Compression-Level
// Overwrite an existing image by simply calling [save()] without
// a path or save to a new file by specifying a full file path.
$img->save($save_path);
// Optionally close the image to free memory when finished working with it.
// This happens automatically when the variable is no longer used.
$img->close();
Handle Language Translations for a Site or App
// FastSitePHP provides an easy to use Internationalization (i18n) API for
// sites and apps that need to support multiple languages. The code is
// structured but minimal in size so if you have different translation needs
// you can simply copy and modify the class.
// Translations are saved as JSON files in the one directory using the name
// format of “{name}.{lang}.json”. An optional main file named “_.{lang}.json”
// if found will loaded first. The main file “_” is useful for storing key
// translations such as menus, page headers, page footers, etc.
// An optional fallback language can be specified so that missing translations
// default to another language. This allows partially translated sites to use
// this API.
// Since the API is simple and easy to use there are only two functions to call:
// [langFile()] and [textFile()].
// Example Files:
// _.en.json
// _.es.json
// header.en.json
// header.es.json
// about.en.json
// Using this code the above files will be loaded in the order listed.
$app->config['I18N_DIR'] = __DIR__ . '/i18n';
$app->config['I18N_FALLBACK_LANG'] = 'en';
\FastSitePHP\Lang\I18N::langFile('header', 'es');
\FastSitePHP\Lang\I18N::langFile('about', 'es');
// Typical usage is allow for an app to load a language
// file based on the Requested URL:
$app->get('/:lang/about', function($lang) {
\FastSitePHP\Lang\I18N::langFile('about', $lang);
});
// [setup()] can be called for each request to make sure
// that a language file is always loaded for template rendering when
// [$app->render()] is called.
//
// This is useful if your site uses PHP or other templates for rendering
// and expects the [i18n] default file to always be available. For example
// an unexpected error or call to [$app->pageNotFound()] can trigger a
// template to be rendered.
\FastSitePHP\Lang\I18N::setup($app);
// Loaded translations are set to the app property ($app->locals['i18n'])
// so that they can be used with template rendering and the calling page.
// When using a URL format of [https://www.example.com/{lang}/{pages}]
// and a fallback language the user will be re-directed to the same page
// with the fallback language if the specified language doesn't exist.
// When [langFile()] is called and the language is verified as valid
// it is set to the app property ($app->lang).
// The other I18N function [textFile()] simply takes a full file path
// containing text '{lang}' along with the selected language and then loads
// the file or if it doesn't exist, the matching file for the fallback language.
$file_path = $app->config['I18N_DIR'] . '/test-{lang}.txt';
$content = \FastSitePHP\Lang\I18N::textFile($file_path, $app->lang);
// Use [getUserDefaultLang()] to get the default language for the user based
// on the 'Accept-Language' Request Header and available languages for the site.
//
// This is useful to provide custom content for the user or to redirect to the
// user's language when they access the default URL.
//
// Requires config values I18N_DIR and I18N_FALLBACK_LANG.
$default_lang = \FastSitePHP\Lang\I18N::getUserDefaultLang();
Formatting Dates, Times, and Numbers
// FastSitePHP provides an easy to use Localization (l10n) API to allow date
// and number formatting with a user’s local language and regional settings.
// Create a new Lang L10N Object
$l10n = new \FastSitePHP\Lang\L10N();
// Settings can optionally be passed when the class is first created.
/*
$locale = 'en-US';
$timezone = 'America/Los_Angeles';
$l10n = new \FastSitePHP\Lang\L10N($locale, $timezone);
*/
// Use the [timezone()] function to get or set the timezone that will be used
// when formatting dates and times.
//
// If you have a site or application that has users in multiple timezones or
// countries an application design that works well is to save all dates and
// times in UTC and then format based on the users selected timezone.
//
// This example prints:
/*
UTC = 2030-01-01 00:00
Asia/Tokyo = 2030-01-01 09:00
America/Los_Angeles = 2029-12-31 16:00
*/
$date_time = '2030-01-01 00:00:00';
$timezones = ['UTC', 'Asia/Tokyo', 'America/Los_Angeles'];
foreach ($timezones as $timezone) {
// Change Timezone
$l10n->timezone($timezone);
// Print the formated date and time
echo $l10n->timezone();
echo ' = ';
echo $l10n->formatDateTime($date_time);
echo '<br>';
}
echo '<br>';
// Change Timezone back to UTC for the next examples
$l10n->timezone('UTC');
// The [$date_time] parameter for functions [formatDateTime(), formatDate(),
// and formatTime()] is either a Unix Timestamp (int) or a string in format
// of 'YYYY-MM-DD HH:MM:SS' or 'YYYY-MM-DD'
$date_time = 1896181200;
$date_time = '2030-02-01 13:00:00';
// Print Date Time with different locales using [locale()] and
// [formatDateTime()] functions. This example prints:
/*
ko = 2030. 2. 1. 오후 1:00
bn = ১/২/২০৩০ ১:০০ PM
en-US = 2/1/2030, 1:00 PM
de-CH = 01.02.2030, 13:00
ar = ١/٢/٢٠٣٠ ١:٠٠ م
*/
$locales = ['ko-KR', 'bn-BD', 'en-US', 'de-CH', 'ar'];
foreach ($locales as $locale) {
// Change Locale
$l10n->locale($locale);
// Print the formated date and time
echo $l10n->locale();
echo ' = ';
echo $l10n->formatDateTime($date_time);
echo '<br>';
}
echo '<br>';
// In addition to [formatDateTime()] functions [formatDate()] and
// [formatTime()] can be used to show only a date or time. Prints:
/*
01/02/2030
13:00:00
*/
$l10n->locale('fr-FR');
echo $l10n->formatDate($date_time);
echo '<br>';
echo $l10n->formatTime($date_time);
echo '<br>';
echo '<br>';
// Print a formatted Number with different locales using [locale()] and
// [formatNumber()] functions. Decimal places are optional and default
// to 0. This example prints:
/*
en-US = 1,234,567,890.12345
en-IN = 1,23,45,67,890.12345
fr = 1 234 567 890,12345
fa = ۱٬۲۳۴٬۵۶۷٬۸۹۰٫۱۲۳۴۵
*/
$number = 1234567890.12345;
$decimals = 5;
$locales = ['en-US', 'en-IN', 'fr', 'fa'];
foreach ($locales as $locale) {
// [locale()] is a chainable getter and setter function
// so it can be set and read from the same line.
echo $l10n->locale($locale)->locale();
echo ' = ';
echo $l10n->formatNumber($number, $decimals);
echo '<br>';
}
// Get supported Locales, Languages, and Timezones
$locales = $l10n->supportedLocales();
$langugages = $l10n->supportedLanguages();
$timezones = $l10n->supportedTimezones();
Starter Site Middleware
// The FastSitePHP Starter Site includes several examples pages and provides
// a basic directory/file structure. The site is designed to provide structure
// for basic content (JavaScript, CSS, etc) while remaining small in size so
// that it is easy to remove files you don’t need and customize it for your site.
//
// https://github.com/fastsitephp/starter-site
//
// Core Middleware classes are provided and can be modified for your site.
//
// To use them specify the 'Class.method' on route filter functions or
// when mounting additional files.
// Require a user to be logged in in order to use a page
$app->get('/secure-page', 'SecureController')->filter('Auth.hasAccess');
// Require an authenticated user and use CORS
$app
->get('/api/:record_type', 'ApiController.getData')
->filter('Cors.acceptAuth')
->filter('Auth.hasAccess');
// Only run a route from localhost
$app->get('/server-info', function() {
phpinfo();
})
->filter('Env.isLocalhost');
// Only load a file if running from localhost
$app->mount('/sysinfo/', 'routes-sysinfo.php', 'Env.isLocalhost');