Mini PHP framework example for web application.
Project requirements:
- PHP 7
- mySQL
- composer
- mySQL GUI. Ex: Sequel Pro (Optional)
How to run:
- Setup the database in
config.php
. Make sure to createusers
table withname
column - run
composer install
- run
php -S localhost:8888
Use Homebrew to install the PHP
To search all PHP related formulas
brew search php
To install the php (mine is php 7.1) formula
brew install homebrew/php/php71
I'm using Atom. If you need curated atom packages for your development environment, check out mehcode/awesome-atom
You can echo your variable along with strings using these 3 ways:
echo "Hello, $name"
echo "Hello, {$name}"
echo 'Hello, ' . $name
Shorthand for PHP echo:
<?php
<?= 'Hello, World' ?>
Sanitize HTML special characters:
<?= "Hello, " . htmlspecialchars($_GET['name']); ?>
So far we only have single file called index.php. For separation of concerns, Follow this steps:
- Create new file specifically to display HTML. Use proper name convention for the file like index.view.php
- Move all the HTML code to index.view.php
- Call index.view.php in index.php using
require index.view.php
Nice way to iterate array in HTML
<?php foreach ($names as $name) : ?>
<li><?= $name; ?></li>
<?php endforeach; ?>
Create associative array
<?php
$person = [
'age' => 23,
'hair' => 'black',
'career' => 'web developer'
];
Add an associative array item
<?php
$person['name'] = 'Adhya';
Delete an associative array item
<?php
unset($person['age']);
Print an associative array
<ul>
<?php foreach ($person as $key => $feature) : ?>
<li><strong><?= $key; ?></strong> <?= $feature; ?></li>
<?php endforeach; ?>
</ul>
Echo an array
<?php
echo '<pre>';
die(var_dump($person));
echo '</pre>';
Add a regular array item
<?php
$animals = ['dog', 'cat'];
$animals[] = 'fish'
Convert a string to uppercase
<?php
ucwords($heading)
Ternary operator
<li>
<strong>Status: </strong> <?= $task['completed'] ? 'Complete' : 'Incomplete' ; ?>
</li>
null
Die Dump Function
<?php
function dd($data){
echo "<pre>";
die(var_dump($data));
echo "</pre>";
}
If you don't have mysql installed yet
brew search mysql
brew install mysql
Adding user accounts. click here for more information
- Connect as root
mysql -u root -p
- Create users and grant access
mysql> CREATE USER 'finley'@'localhost' IDENTIFIED BY 'some_pass';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'finley'@'localhost'
-> WITH GRANT OPTION;
mysql> CREATE USER 'finley'@'%' IDENTIFIED BY 'some_pass';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'finley'@'%'
-> WITH GRANT OPTION;
mysql> CREATE USER 'admin'@'localhost' IDENTIFIED BY 'admin_pass';
mysql> GRANT RELOAD,PROCESS ON *.* TO 'admin'@'localhost';
mysql> CREATE USER 'dummy'@'localhost';
Some useful sql commands:
- To show all databases
mysql> show databases;
- To create a database
mysql> create database database_name;
- To use database
mysql> use database_name;
- To show all tables
mysql> show tables;
- To show table structure
mysql> describe table_name;
- To create a table
mysql> create table todos (id integer PRIMARY KEY AUTO_INCREMENT, description text NOT NULL, completed boolean NOT NULL);
- To delete a table
mysql> drop table_name;
- To insert a data row
mysql> insert into todos (description, completed) values('Go to the store', false);
- To select all data rows in a table
mysql> select * from table_name;
Simple example of PHP class:
<?php
class Task
{
public $description;
public $completed = false;
public function __construct($description)
{
# Automatically triggered on instantiation
$this->description = $description;
}
public function complete()
{
$this->completed = true;
}
public function isComplete()
{
return $this->completed;
}
}
Connect to database
<?php
// Always wrap PDO connection in try catch
try {
return new PDO('mysql:host=127.0.0.1;dbname=mytodo', 'root', '');
} catch (PDOException $e) {
die($e->getMessage());
}
Prepare and execute query
<?php
$statement = $pdo->prepare('select * from todos');
$statement->execute();
Fetch data from database
<?php
// return value in both associative and index array
var_dump($statement->fetchAll());
// return value in associative array
var_dump($statement->fetchAll(PDO::FETCH_OBJ));
// return value and assign it to a class
var_dump($statement->fetchAll(PDO::FETCH_CLASS, 'Task'))
Use static modifier so we can use this class as a facade without need of instantiation
<?php
public static function make()
{
try {
return new PDO('mysql:host=127.0.0.1;dbname=mytodo', 'root', '');
} catch (PDOException $e) {
die($e->getMessage());
}
}
Create a bootstrap file to accommodate the pre-setup. For example:
<?php
require 'database/Connection.php';
require 'database/QueryBuilder.php';
return new QueryBuilder(
Connection::make()
);
Create a config file to handle the general setting of your application (Database, Email, etc.)
<?php
return [
'database' => [
'name' => 'mytodo',
'username' => 'root',
'password' => '',
'connection' => 'mysql:host=127.0.0.1',
'options' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]
]
];
user trim
to cut white spaces or specific characters off from the outer sides of a string
<?php
trim($_SERVER['REQUEST_URI'], '/');
Can't use $this in a static function. use 'new static' or 'new self' instead to instantiate self class.
Simple Router class example:
<?php
<?php
class Router
{
protected $routes = [];
public static function load($file)
{
// Use 'new static' or 'new self' which equal to 'new Router'
$router = new static;
require $file;
return $router;
}
public function define($routes)
{
$this->routes = $routes;
}
public function direct($uri)
{
if (array_key_exists($uri, $this->routes)) {
return $this->routes[$uri];
}
throw new Exception("No route defined for this uri");
}
}
Put routes list somewhere else in the root directory
<?php
$router->define([
'' => 'controllers/index.php',
'about' => 'controllers/about.php',
'about/culture' => 'controllers/about-culture.php',
'contact' => 'controllers/contact.php',
]);
null
<?php
class Post
{
public $title;
public $author;
public $published;
public function __construct($title, $author, $published)
{
$this->title = $title;
$this->author = $author;
$this->published = $published;
}
$posts = [
new Post('My First Post', 'JW', true),
new Post('My First Post', 'JW', true),
new Post('My First Post', 'AW', true),
new Post('My First Post', 'TT', false)
]
//Array filter
$publishedPosts = array_filter($posts, function($post) {
return $post->published;
});
//Array map
$posts = array_map(function($post) {
return (array) $post;
}, $posts);
//Array column
$authors = array_column($posts, 'author', 'title');
}
Use parse_url
to filter out the query string
<?php
public static function uri()
{
return $uri = trim($_SERVER['REQUEST_URI'], '/');
return $uri = trim(
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/'
);
}
Group the requests into based on their action
<?php
$router->get('', 'controllers/index.php');
$router->get('about', 'controllers/about.php');
$router->get('about/culture', 'controllers/about-culture.php');
$router->get('contact', 'controllers/contact.php');
$router->post('names', 'controllers/add-name.php');
Put the input array as a parameter of execute method to bind the values
<?php
public function insert($table, $parameters)
{
$sql = sprintf(
'insert into %s (%s) values (%s)',
$table,
implode(', ', array_keys($parameters)),
':' . implode(', :', array_keys($parameters))
);
try {
$statement = $this->pdo->prepare($sql);
$statement->execute($parameters);
} catch (Exception $e) {
return $e->getMessage();
}
}
For more readable sql query template, use sprintf
<?php
$sql = sprintf(
'insert into %s (%s) values (%s)',
$table,
implode(', ', array_keys($parameters)),
':' . implode(', :', array_keys($parameters))
);
Use header
to redirect
<?php
header('Location: /');
Autoload classes with composer (in composer.json
file). Write ./
to load all the classes in the project
{
"autoload": {
"classmap": [
"./"
]
}
}
Class for Dependency Injection Container
<?php
class App
{
protected static $registry = [];
public static function bind($key, $value)
{
static::$registry[$key] = $value;
}
public static function get($key)
{
if (! array_key_exists($key, static::$registry)) {
throw new Exception("No {$key} is bound in the container");
}
return static::$registry[$key];Â
}
}
Note: Always run composer-autoload
when adding new class to update the autoload file
Create a helper
file only if you already have a handful of global functions.
Try to keep global function as little as possible.
use extract($data)
to extract data from compact('users')
function.
Use "Splat" function to break array into arguments
<?php
return $this->callAction(
...explode('@', $this->routes[$requestType][$uri])
);
To add namespace
<?php
namespace App\Core\Database;
To use class with namespace
<?php
use App\Core\Database\Connection
Note: When using use
, it should be written after namespace
null