1. Go to this page and download the library: Download shayanderson/exo 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/ */
app()->cli()->output('First line');
app()->cli()->output()->prepend('Second');
app()->cli()->output(' line');
app()->cli()->output('Third line');
// outputs:
// First line
// Second line
// Third line
app()->cli()->output()->enableBuffering();
app()->cli()->output('one');
app()->cli()->output('two');
print_r( app()->cli()->output()->buffer() );
// Array ( [1] => one [2] => two )
// output "text" (in green color)
app()->cli()->output()->colorGreen('text');
// the prepend() method must always be called last, example:
app()->cli()->output()->colorGreen()->prepend('some');
app()->cli()->output()->colorGreen(' text');
// outputs: "some text" (in green color)
// using request: /?id=5&name=Shay
print_r([
'id' => app()->request()->query('id')->integer(),
// use "default" as default value if query "name" does not exist
'name' => app()->request()->query('name', 'default')->string()
]);
// Array ( [id] => 5 [name] => Shay )
app()->session()->flash()->set('loginError', 'Invalid username');
// redirect, then output message
echo app()->session()->flash()->get('loginError');
// message is no longer available on next request
// set: header, status code and content type:
app()->response()
->header('X-Test', 'abc')
->statusCode( app()->response()::HTTP_OK )
->contentType( app()->response()::CONTENT_TYPE_APPLICATION_JSON );
/**
* @property int $id
* @property string $name
* @property bool $isActive
*/
class UserEntity extends \Exo\Entity
{
// public $name; // not allowed because registered as property("name")
// constructor is optional
public function __construct(array $data = null)
{
parent::__construct($data); // must be called
}
// erEntity(['id' => 5]);
// can also use props as setters:
$entity->name = 'Shay';
// use props as getters:
$name = $entity->name;
print_r($entity->toArray());
// Array ( [id] => 5 [name] => Shay [isActive] => 1 )
// toArray() supports filters:
print_r($entity->toArray(['name' => 1]));
// Array ( [name] => Shay )
print_r($entity->toArray(['name' => 0, 'title' => 0]));
// Array ( [id] => 5 )
// assertion/validation example
$entity->id = null;
print_r($entity->toArray());
// throws exception: Assertion failed: "UserEntity.id" must be a number (value: [null])
// single property assertion example
$entity->assert('id', null);
// throws exception: Assertion failed: "UserEntity.id" must be a number (value: [null])
// single property validation example
var_dump($entity->validate('id', null)); // false
var_dump($entity->validate('id', 'Shay')); // true
$this->property('name')
->apply(function($name){
return strtoupper($name);
})
->validator() // validator() must be called after all other property() methods
->string();
// ...
$entity = new UserEntity(['name' => 'shay']);
echo $entity->name; // SHAY
class UserOptionsEntity extends \Exo\Entity
{
protected function register()
{
$this->property('theme')
->validator()
->string()
->allowed(['light', 'dark']);
}
}
// in the UserEntity class bind the reference
class UserEntity extends \Exo\Entity
{
protected function register()
{
// ...
$this->property('options')
->bind(new UserOptionsEntity)
->validator()
->object();
// ...
}
}
$this->property('name')->validator()->string();
$this->property('createdAt')
->voidable() // property can be missing when allowing voidables in toArray()
->validator() // validator() must be called after all other property() methods
->string();
// ...
$entity = new UserEntity(['name' => 'Shay']);
print_r($entity->toArray([], /* allow voidables */ true)); // no assert exception for "createdAt"
// Array ( [name] => Shay )
print_r($entity->toArray()); // voidable not allowed, exception thrown
// Assertion failed: "UserEntity.createdAt" must be a non-empty string (value: [null])
$this->voidable(); // set all properties as voidable (except for "id" below)
$this->property('id')->validator()->string()->notVoidable(); // cannot be missing
$this->property('name')->validator()->string(); // can be missing
$this->property('createdAt')->validator()->string(); // can be missing
$entity = new UserEntity(['id' => 5]);
print_r($entity->toArray([], /* allow voidables */ true)); // no assert exception
class User
{
use \Exo\Event;
// (): array
{
static $events = [];
return $events;
}
public function signIn(int $id)
{
// sign in code here
self::emitEvent('user.signIn', ['id' => $id]);
}
}
// bind event(s) before User use
User::onEvent('user.signIn', function($args){
echo 'User signed in, user ID: ' . $args['id'];
});
// usage
$user = new User;
$user->signIn(14);
// User signed in, user ID: 14
User::onEvent('user.signIn', function($args){
echo 'User signed in, user ID: ' . $args['id'];
});
User::onEvent('user.signIn', function(){
echo 'User sign in detected';
});
// on event trigger:
// User signed in, user ID: 14
// User sign in detected
User::onEvent('user.signIn', function($args){
echo 'User signed in, user ID: ' . $args['id'];
return true; // stop chain
});
User::onEvent('user.signIn', function(){
echo 'User sign in detected';
});
// on event trigger:
// User signed in, user ID: 14
use Exo\Exception;
// throw exception with context
throw new Exception('Error message', [
'id' => 5
]);
try
{
(new MyClass)->badMethod();
}
catch(Exception | Throwable $th)
{
\Exo\Exception::handle($th, function(array $info) use(&$th){
// add some more info (optional)
$info['file'] = $th->getFile();
$info['line'] = $th->getLine();
logRecord($info); // log the exception or something
// output and stop
print_r($info);
exit;
// --or-- continue to throw exception
throw $th;
});
}
/**
* @method Service service()
*/
class App extends \Exo\Factory
{
private static $classes = [
'service' => 'Service'
];
public static function &classes(): array
{
// merge with Exo classes (optional)
$classes = self::$classes + parent::classes();
return $classes;
}
}
// helper function (optional)
function app(): App
{
return App::getInstance();
}
// usage
app()->service()->doSomething();
function model(): Model
{
return Model::getInstance();
}
// usage
$user = model()->user->get($userId);
use Exo\Factory\Dynamic as DynamicFactory;
// example instantiate object using dynamic name for Factory\User
$user = (new DynamicFactory('User', 'Factory'))->newInstance($userId);
$user->doSomething(); // example call
// or instantiate object with array of constructor args
$user = (new DynamicFactory('User', 'Factory'))->newInstanceArgs([$userId, $sessId]);
// or static methods
$factory = new DynamicFactory('User', 'Factory');
($factory->getClass())::doSomething(); // example static call
// or use with Singleton (Exo\Factory\Singleton) subclass
$factory = new DynamicFactory('User', 'Factory');
$user = $factory->getInstanceSingleton(); // same as (singleton)::getInstance()
// or call static method
$user = ($factory->getClass())::getInstace();
$factory = new DynamicFactory('User', 'Factory');
try
{
$user = $factory->newInstance($userId);
}
catch(\Exo\Exception $ex)
{
logSomething('Factory class does not exist "' . $factory->getClass() . '"');
}
class User extends \Exo\Factory\Singleton {}
// now this call will return \Factory\User::getInstance()
$user = Factory::getInstance()->user($userId)->get();
class Session extends \Exo\Factory\Singleton {}
// usage
$sessId = Session::getInstance()->sessionId();
function session(): Session
{
return Session::getInstance();
}
$sessId = session()->sessionId();
use Exo\Logger;
// first setup log handler
// most basic handler, store log records in array:
$logHandler = new \Exo\Logger\ArrayHandler;
Logger::handler($logHandler); // register
// some code
app()->logger('user')->debug('User authenticated', ['id' => $userId]); // channel "user"
// more code
app()->logger('session')->debug('Session started'); // channel "session"
// more code
if($fatal)
{
app()->logger()->critical('Database connection failed', ['error' => $dbError]); // no channel
}
// get and output log
print_r( $logHandler->close() );
class MyLogHandler extends \Exo\Logger\Handler
{
protected $param;
public function __construct(string $param, int $level = \Exo\Logger::LEVEL_DEBUG,
array $channelFilter = null)
{
$this->param = $param;
parent::__construct($level, $channelFilter);
}
public function close()
{
// do something like output log or close connection
}
public function write(\Exo\Logger\Record $record)
{
if($this->isHandling($record))
{
// do something like write to file or DB table
}
}
}
namespace Test;
class MyClass
{
public function doAction()
{
app()->logger()->debug();
// message: "\Test\MyClass::doAction"
app()->logger()->debug(['key' => 'val']);
// message: "\Test\MyClass::doAction"
// context: [key => val]
}
}
class UserModel extends \Exo\Model
{
// example
public function create(array $document): bool
{
return app()->store->users->insert(
$this->entity($document)->toArray()
);
}
// array of entities example
public function createMany(array $documents): int
{
return app()->store->users->insertMany(
$this->entityArray($documents)
// possible to use filter like:
// $this->entityArray($documents, ['name' => 1])
);
}
}
namespace Canvas;
class RectangleOptions extends \Exo\Options
{
// valid option keys must be constants that begin with "KEY_"
const KEY_HEIGHT = 'height';
const KEY_WIDTH = 'width';
protected function __construct()
{
// all below is optional, set default value to: 300
$this->option(self::KEY_HEIGHT, 300)
// validation is optional
->number();
$this->option(self::KEY_WIDTH, 600)
}
// tions', $data);
}
return true;
}
}
class Rectangle
{
private $h;
private $w;
public function __construct()
{
$options = RectangleOptions::getInstance(); // singleton
$this->h = $options->get($options::KEY_HEIGHT);
$this->w = $options->get($options::KEY_WIDTH);
}
}
use Canvas\Rectangle;
use Canvas\RectangleOptions;
RectangleOptions::getInstance()->set('height', 200);
RectangleOptions::getInstance()->set('width', 400);
$rec = new Rectangle; // h:200, w:400
print_r(RectangleOptions::getInstance()->toArray());
// Array ( [height] => 200 [width] => 400 )
use Exo\Share;
Share::getInstance()->set('user', new User(14));
// more code
$userLevel = Share::getInstance()->get('user')->getLevel();
// can also use props:
Share::getInstance()->user = new User(14);
$userLevel = Store::getInstance()->user->getLevel();
use \Exo\Validator;
$userName = 'Bob';
$userUsername = 'bob';
$userAge = '';
(new Validator('user.name'))
->string()
->assert($userName);
(new Validator('user.username'))
->string()
->unique(function($username){
return true; // should be DB lookup or something
})
->assert($userUsername);
(new Validator('user.age'))
->number()
->assert($userAge);
// throws exception:
// Assertion failed: "user.age" must be a number (value: "")
// all values are considered )
->string()
->email()
->optional() // not optional:
(new Validator('user.name'))
->string()
->assert('');
// throws exception:
//Assertion failed: "user.name" must be a non-empty string (value: "")
(new Validator('user.age'))
->number()->message('Invalid age')
->between(21, 99)->message('Age must be between 21 and 99')
->assert('');
// throws exception:
// Assertion failed: "user.age" Invalid age (value: "")
(new Validator('user.age'))
->number()
->between(21, 99)
->groupMessage('Invalid age value')
->assert('');
// throws exception:
// Assertion failed: "user.age" 'Invalid age value (value: "")
(new Validator('age'))
->number()->message('Invalid age')
->assert('', function(array $validationMessages){
handleValidationErrors($validationMessages);
// return true to halt and not throw validation exception
return true;
});
class MyRule extends \Exo\Validator\Rule
{
protected $message = 'does not equal "validValue"';
public function validate($value): bool
{
return $value === 'validValue';
}
}
// usage
validator('myValue')
->string()
->rule(new MyRule)
->assert('badValue');
// throws exception:
// Assertion failed: "myValue" does not equal "validValue" (value: "badValue")
$isValid = (new Validator('user.age'))
->number()
->validate($userAge)
if(!$isValid) // do something
class MyAssertionException extends \Exception {}
// set as exception class for failed assertions:
\Exo\Validator\AbstractType::setAssertionExceptionClass(MyAssertionException::class);