1. Go to this page and download the library: Download forensic/handler library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
forensic / handler example snippets
/**
* file AuthHandler.php
*/
//make sure composer autoloader is loaded
namespace app\Handler;
use Forensic\Handler\Handler as BaseHandler;
use app\Model\UserModel; //our model
class AuthHandler extends BaseHandler
{
public function getDB()
{
//return db orm
}
/**
* executes signup
*@param array|string [$source = 'post'] - the source of the data. can also be an array
*/
public function executeSignup($source = 'post')
{
$rules = [
//email field rule.
'email' => [
'type' => 'email',
'err' => '{this} is not a valid email address'
],
'password1' => [
'type' => 'password',
],
'password2' => [
'type' => 'password',
'matchWith' => '{password1}'
],
];
$this->setSource($source)->setRules($rules);
if (!$this->execute())
return $this->succeeds(); //return immediately if there are errors
/*
* check if user exists, so that we dont create same user again
* we can check using the model, or execute a prepared separate query. we could also
* define this integrity check right in the rules above, only that we must implement
* the DBCheckerAbstract class which will be shown later
*/
$query = 'SELECT id FROM users WHERE email = ? AND password = ?';
$result = $this->getDB()->select($query, array($this->email, $this->password1));
if (count($result) > 0)
{
$this->setError('email', 'User already exists, please login instead');
return $this->succeeds(); //return immediately if there is error
}
//create user
$user = new UserModel();
/**
* do not copy password2 and rename password1 to just password when mapping processed
* data to our model
*/
$this->modelSkipField('password2')->modelRenameField('password1', 'password');
$this->mapDataToModel($user)->save(); // it returns the model
//set the new user id so that it can be accessed outside the class
$this->setData('id', $user->id);
return $this->succeeds();
}
}
/**
* file auth controller
*/
namespace app\Controller;
use SomeNamespace\Request;
use SomeNamespace\JsonResponse;
use SomeNamespace\Controller as BaseController;
use app\Handler\AuthHandler;
class AuthController extends BaseController
{
public function signup(Request $request, Response $response, AuthHandler $handler)
{
if ($handler->executeSignup())
return $response([
'status' => 'success',
'data' => [
'userId' => $handler->id
],
]);
else
return $response([
'status' => 'failed',
'data' => [
'errors' => $handler->getErrors()
],
]);
}
}
$rules = [
'first-name' => [
'type' => 'text',
'options' => [
'min' => 3,
'minErr' => '{_this} should be at least 3 charaters length',
//first-name should be at least 3 characters length
],
],
//we are expecting an array of favorite colors
'favorite-colors' => [
'type' => 'choice',
'filters' => [
'toLower' => true, //convert the colors to lowercase
],
'options' => [
//choices to choose from
'choices' => array('green', 'white', 'blue', 'red', 'violet', 'purple'),
'err' => 'color {_index} is not a valid color',
//color 1 is not a valid color'
],
],
'subscribe-newsletter' => [
'type' => 'boolean',
],
//email is
$rules = [
'first-name' => [
'type' => 'text',
'options' => [
'min' => 3,
'minErr' => 'first name should be at least 3 characters length',
'max' => 15,
]
],
'favorite-integer' => [
'type' => 'positiveInteger',
'options' => [
'lt' => 101, //should be less than 101, or max of 100.
]
],
'date-of-birth' => [
'type' => 'date',
'options' => [
'min' => '01-01-1990', //only interested in people born on or after 01-01-1990
'max' => '{CURRENT_DATE}'
]
],
];
$rules = [
'first-name' => [
'type' => 'text',
'regexAll' => [
//name must start with letter
[
'test' => '/^[a-z]/i',
'err' => 'name must start with an alphabet'
],
//only aphabets, dash and apostrophe is allowed in name
[
'test' => '/^[-a-z\']+$/',
'err' => 'only aphabets, dash, and apostrophe is allowed in name'
]
]
],
'country' => [
'type' => 'text',
'options' => [
'regex' => [
'test' => '/^[a-z]{2}$/',
'err' => '{this} is not a 2-letter country iso-code name'
]
],
],
'phone-number' => [
'type' => 'text',
'options' => [
'regexAny' => [
'tests' => [
//phone number can match nigeria mobile number format
'/^0[0-9]{3}[-\s]?[0-9]{3}[-\s]?[0-9]{4}$/',
//phone number can match uk mobile number format
'/^07[0-9]{3}[-\s]?[0-9]{6}$/'
],
'err' => 'only nigeria and uk number formats are accepted'
]
]
],
'favorite-colors' => [
'options' => [
'regexNone' => [
//we dont accept white as a color
[
'test' => '/^white$/i',
'err' => '{this} is not an acceptable color'
],
//we dont accept black either
[
'test' => '/^black$/i',
'err' => '{this} is not an acceptable color'
],
],
],
],
]
$rules = [
'date-of-birth' => [
'type' => 'date',
'options' => [
'min' => '01-01-1990', //only interested in people born on or after 01-01-1990
'max' => '{CURRENT_DATE}'
]
],
];
[
'min' => 8,
'max' => 28,
'regexAll' => [
//password should contain at least two alphabets
[
'test' => '/[a-z].*[a-z]/i',
'err' => 'Password must contain at least two letter alphabets'
],
//password should contain at least two non letter alphabets
[
'test' => '/[^a-z].*[^a-z]/i',
'err' => 'Password must contain at least two non letter alphabets'
],
],
];
namespace app\Handler;
use Forensic\Handler\Abstracts\DBCheckerAbstract;
use Illuminate\Support\Facades\DB;
class DBChecker extends DBCheckerAbstract
{
/**
* construct query from the given options
*
*@param array $options - array of options
* the options array contains the following fields.
* 'entity': is the database table
* 'params': which is array of parameters. defaults to empty array
* 'query': which is the query to run. defaults to empty string
* 'field': if the query parameter is empty string, then there is the field parameter
* that refers to the database table column to check
*/
protected function buildQuery(array $options): string
{
$query = $options['query'];
//if the query is empty string, we build it according to our orm
if ($query === '')
{
//build the query
$query = 'SELECT * FROM ' . $options['entity'] . ' WHERE ' .
$options['field'] . ' = ?';
}
return $query;
}
/**
* executes the query. the execute method should return array of result or empty
* array if there is no result
*/
protected function execute(string $query, array $params, array $options): array
{
return DB::select($query, $params);
}
}
//file BaseHandler
namespace app\Handler;
use Forensic\Handler\Handler as ParentHandler;
class BaseHandler extends ParentHandler
{
public function construct($source = null, array $rules = null)
{
parent::__construct($source, $rules, null, new DBChecker());
}
}
// file AuthHandler.php
namespace app\Handler;
use app\Model\UserModel; //our model
class AuthHandler extends BaseHandler
{
/**
* executes signup
*@param array|string [$source = 'post'] - the source of the data. can also be an array
*/
public function executeSignup($source = 'post')
{
$rules = [
//email field rule.
'email' => [
'type' => 'email',
'err' => '{this} is not a valid email address',
//db check rule goes here
'check' => [
'if' => 'exists', // note that it is error if it exists
'entity' => 'users',
'field' => 'email',
'err' => 'User with email {this} already exists, login instead',
]
],
'password1' => [
'type' => 'password',
],
'password2' => [
'type' => 'password',
'matchWith' => '{password1}'
],
];
$this->setSource($source)->setRules($rules);
if (!$this->execute())
return $this->succeeds(); //return immediately if there are errors
//create user
$user = new UserModel();
//do not copy password2 and rename password1 to just password
$this->modelSkipField('password2')->modelRenameField('password1', 'password');
$this->mapDataToModel($user)->save(); // it returns the model
//set the new user id
$this->setData('id', $user->id);
return $this->succeeds();
}
}
$rules = [
'userid' => [
'type' => 'positiveInt',
'check' => [
'if' => 'notExist',
'entity' => 'users'
//since no query is defined, the field option will default to id rather than
// userid because the field value is an integer,
//params options will default to array(current_field_value)
]
],
'email' => [
'type' => 'email',
'checks' => [
//first check
[
'if' => 'exists',
'entity' => 'users'
//since no field is defined, it will defualt to email
],
//more checks goes here
]
],
'country' => [
'type' => 'text',
'checks' => [
//first check
[
'if' => 'notExist',
'query' => 'SELECT * from countries WHERE value = ?',
'params' => array('{this}'),
'err' => '{this} is not recognised as a country in our database'
],
],
],
];
//file CustomValidator.php
namespace app\Handler;
use Forensic\Handler\Validator;
class CustomValidator extends Validator
{
protected function validateName(bool $dash and apostrophe is allowed in names
[
'test' => '/^[-a-z\']$/i',
'err' => 'only alphabets, hyphen and apostrophe allowed in names'
]
//name must start with at least two alphabets
[
'test' => '/^[a-z]{2,}/i',
'err' => 'name must start with at least two alphabets'
],
];
return $this->validateText($
//file BaseHandler
namespace app\Handler;
use Forensic\Handler\Handler as ParentHandler;
class BaseHandler extends ParentHandler
{
public function construct($source = null, array $rules = null)
{
parent::__construct($source, $rules, new CustomValidator(), new DBChecker());
}
/**
*@override the parent method.
*/
public function getRuleTypesMethodMap(): array
{
return array_merge(parent::getRuleTypesMethodMap(), [
'name' => 'validateName'
]);
}
}
// file ProfileHandler.php
namespace app\Handler;
use app\Model\UserModel; //our model
class ProfileHandler extends BaseHandler
{
/**
* updates user profile
*@param array|string [$source = 'post'] - the source of the data. can also be an array
*/
public function updateProfile($source = 'post')
{
$rules = [
//email field rule.
'id' => [
'type' => 'positiveInteger',
//db check rule goes here
'check' => [
'if' => 'doesNotExist',
'entity' => 'users',
'err' => 'No user found with id {this}',
]
],
'first-name' => [
'type' => 'name',
],
'last-name' => [
'type' => 'name',
],
'middle-name' => [
'type' => 'name',
'