Skip to content

Instantly share code, notes, and snippets.

@Dracovian Dracovian/00-about.txt
Last active Nov 30, 2019

Embed
What would you like to do?
Use-case specific PHP tutorials.
Let's assume that we're developing on a Linux server and we have the following project layout:
- /var/www/html
- /private
- /classes
- /a
- class_a.php
- /b
- class_b.php
- /c
- class_c.php
- autoloader.php
- /public
- /css
- /img
- index.php
So in this case we are storing our backend files in the "private" subdirectory, while we are storing our frontend files in the "public" subdirectory.
Let's also assume that we're running a MariaDB server with the following credentials:
username: root
password: root
hostname: localhost
database: rootdb
We will be delving into the following subject matters in this Gist:
- Class autoloaders
- Using PDO to make database connections
- Filtering user inputs
- Learning about OOP (Object-Oriented Programming) with PHP
<?php
/* Object-Oriented Programming Paradigm
*
* OOP is a paradigm that's based around objects and inheritance.
* This means that you're grouping code into objects (classes) and determining the scope (visibility) of the objects' code.
*
* Public: This object function/variable is visible to every other object in the code.
* - You can access, modify, and delete the value of public variables from anywhere in the code.
* - Magic methods in classes are always treated as public functions.
* - You can only have public functions in class interfaces.
*
* Protected: This object function/variable is visible to objects that inherit the parent object (the object that holds the function/variable).
* - You can't access, modify, or delete the value of a protected variable without extending the parent object (class).
*
* Private: This object function/variable is visible only to the functions/variables in the parent object.
* - No other object can access, modify, or delete the value of a private variable, even if the parent object is extended.
*/
// Namespaces are containers for classes, you can create multiple namespaces within a single PHP file if desired.
namespace NS_01 {
// Insert your classes here...
}
namespace NS_02 {
// Insert your classes here...
}
// However you're most likely going to only need one namespace per PHP file either way.
namespace NS {
// Now it's time to create some classes that will belong to our namespace "NS".
class CLASS_ONE {
/* Time to test out some variable scopes within this class.
*
* $str will be a public class variable.
* $int will be a protected class variable.
* $arr will be a private class variable.
*/
public $str;
protected $int;
private $arr;
/* PHP classes have access to something called "magic methods"
* https://www.php.net/manual/en/language.oop5.magic.php
*
* The most important magic methods we will be covering in these tutorials are:
* __construct
* __destruct
* __isset
* __unset
* __set
* __get
* __toString
*/
/* This is the class constructor magic method. You can't return anything from the class constructor.
* Whenever this class is called, the class constructor will be the first function called.
*/
function __construct(string $str, int $int, array $arr) {
/* Class-variables can be created anywhere within the class and can be accessed anywhere from within the class.
* We use the format `$this->varname` when accessing a class variable.
*/
$this->str = $str;
$this->int = $int;
$this->arr = $arr;
}
/* This is the class destructor magic method. You can't return anything from the class destructor.
* Whenever an object is created, you can use `unset($obj)` to call the destructor.
*/
function __destruct() {
/* We can clear the values of our class variables whenever we call the destructor.
* This isn't necessary since the class variables will be cleared when the object is unset.
*/
unset($this->str);
unset($this->int);
unset($this->arr);
}
/* This magic method will determine if a class variable is initialized and has a value associated with it.
* __isset should always return a boolean (true or false) value.
*/
function __isset(string $name) : bool {
return isset($this->$name);
}
/* This magic method will clear a class variable if one is initialized. */
function __unset(string $name) {
if (isset($this->$name))
unset($this->$name);
}
/* This magic method will create a class variable if one doesn't already exist. */
function __set(string $name, $value) {
if (!isset($this->$name))
$this->$name = $value;
}
/* This magic method will return the value of a class variable if one exists.
* __get should always return something, but we don't need to specify the type of the data returned.
*/
function __get(string $name) {
return isset($this->$name) ? $this->$name : null;
}
/* This magic method will return a string whenever a class object is echoed out like a string.
* __toString should always return a string.
*/
function __toString() : string {
return __CLASS__;
}
}
// We can create a class object when setting a variable to an initialized class.
$obj1 = new CLASS_ONE("str", 123, []);
// We can access public, protected, and private class variables/functions because $obj1 is a class object.
echo '<pre>';
print_r([
'str' => $obj1->str,
'int' => $obj1->int,
'arr' => $obj1->arr
]);
echo '</pre>';
}
<?php
// Reuse our namespace from the previous tutorial
namespace NS;
// Because the exception class belongs to the global namespace, we must call it in order to use it in this namespace.
use Exception;
/* Class autoloaders are used to dynamically call classes as opposed to manually calling classes one-after-another.
* We must consider the layout of our project when creating autoloaders to prevent errors when using them.
*/
/* Register a new autoloader with an anonymous function passed as an argument
* You can learn more about anonymous functions here: https://www.php.net/manual/en/functions.anonymous.php
*/
spl_autoload_register(function(string $class) {
// Split our class into an array.
$class_array = explode('\\', $class);
// Replace the first value of our array (which is the namespace in this case) with the classes directory.
$class_array[0] = "classes";
// Combine our array values into a single string.
$class_file = implode(DIRECTORY_SEPARATOR, $class_array);
// Attempt to import our new file.
try {
include_once $class_file . '.php';
} catch (Exception $ex) {
// We're going to do nothing if our autoloader fails to import the given class file.
}
});
// Test out our autoloader with respect to the project layout.
$a = new a\class_a();
$b = new b\class_b();
$c = new c\class_c();
<?php
/* It is always important to filter user inputs since we can't trust user inputs.
* Trusting user inputs can result in your server being hacked depending on what kind of users your site/application gets used by.
* Here's a list of filter types that you can use in PHP: https://www.php.net/manual/en/filter.filters.php
*/
// Determine if someone accesses the site at `03-filtering-inputs.php?test=`
if (filter_has_var(INPUT_GET, 'test')) {
// Gather the value of 'test' and strip it of anything that can be harmful.
$test = filter_input(INPUT_GET, 'test', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
// Echo out the value of $test.
echo $test;
}
// Determine if someone submitted a form with a button named `submit`
if (filter_has_var(INPUT_POST, 'submit')) {
// Gather the email and password data from our imaginary form.
// Make sure that the email is in the correct format of `email@domain.extension`
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
// We should never echo/print out the user's password nor should we ever filter it out.
// We will never store any passwords in the clear (as is) in the database.
$password = password_hash(filter_input(INPUT_POST, 'password'), PASSWORD_BCRYPT);
}
<?php
/* Modern PHP development brings us PDO which makes the process of accessing a database to be easier.
* So instead of having to learn the functions for each specific database engine, we can simply opt for PDO which makes things streamlined.
* You can find out more on PDO here: https://www.php.net/manual/en/book.pdo.php
*/
// We will be simulating a MySQL/MariaDB server for this example since it's the most popular database engine on the market.
// We'll start by creating a class to contain our PDO connection.
class Database {
// Create a private class variable for our PDO connection.
private $connection;
// Create a class constructor to set our PDO connection.
function __construct(string $hostname, string $username, string $password, string $database, int $port = 3306) {
$dsn = sprintf('mysql:host=%s;dbname=%s;port=%d', $hostname, $username, $port);
$this->connection = new PDO($dsn, $username, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // Ensure that PDO returns an exception upon error.
PDO::ATTR_PERSISTENT => TRUE, // Keep the database connection open until we close it manually.
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Return the data in the form of an array.
PDO::ATTR_EMULATE_PREPARES => FALSE // Force the database engine to prepare our statements.
]);
}
// Create a class destructor to destroy our PDO connection.
function __destruct() {
if (isset($this->connection))
unset($this->connection);
}
}
// Now it's time to create our class object.
$database = new Database('localhost', 'root', 'root', 'rootdb');
// Now it's time to execute a simple query.
$statement = $database->connection->prepare('SELECT table_name FROM information_schema.tables WHERE table_schema = ?');
$statement->execute(['testdb']); // We must put all of our prepared statement values in an array.
// Now it's time to return the results of our simple query.
$results = $statement->fetchAll();
// Now it's time to fetch each value from $results.
foreach ($results as $result)
echo $result['table_name'] . '<br />';
// Now we can kill our database connection.
unset($database);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.