PHP code example of shayanderson / exo

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/ */

    

shayanderson / exo example snippets


// Example usage from CLI:
// php index.php myCommand debug x=123 y=abc
print_r( app()->cli()->getArgs() ); // print all args
// Array ( [SCRIPT] => index.php [COMMAND] => myCommand [debug] => 1 [x] => 123 [y] => abc )

// getters
var_dump( app()->cli()->get('COMMAND') ); // string(9) "myCommand"
var_dump( app()->cli()->get('x') ); // string(3) "123"

// check if keys exist
var_dump( app()->cli()->has('x') ); // bool(true)
var_dump( app()->cli()->has('bad') ); // bool(false)

// override default map: Array ( [0] => SCRIPT [1] => COMMAND )
app()->cli()->map([
	0 => '_self_'
]);
// ...
print_r( app()->cli()->getArgs() );
// Array ( [_self_] => index.php [myCommand] => 1 [debug] => 1 [x] => 123 [y] => abc )

app()->cli()->output('Console message');
// outputs:
// Console message

app()->cli()->output(['1', '2', '3']);
// outputs:
// 1
// 2
// 3

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');

app()->cli()->output()->indent('text');
// multiple indents
app()->cli()->output()->indent()->indent('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)

app()->cli()->ouput('Continue?');
if( !app()->cli()->confirm('y') )
{
	exit;
}

// load from file
app()->env()->load('/path/to/.env');

$dbUser = env('DB_USER'); // myuser
$dbPassword = env('DB_PWD'); // secret
// use default value if variable does not exist
$dbName = env('DB_NAME', 'default'); // default
// for critical env variables invalid key exception can be used:
$dbHost = env('DB_HOST', null, /* throw exception */ true);
// Exo\App\Exception\InvalidKeyException exception thrown: Invalid key "DB_HOST"

$httpHost = env('SERVER.HTTP_HOST');

if(app()->request()->isMethod('POST'))
{
	$name = app()->request()->input('name')->string();
	if(app()->request()->input('email')->has())
	{
		$email = app()->request()->input('email')->email();
	}
	// with validation example
	$username = app()->request()->input('username')
		->validator(
			app()->validator()
				->string()
				->alnum()
		)
		->string();
}

// 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()->set('user.id', 5); // creates session data: [user => [id => 5]]
// ...
if(app()->session()->has('user.id'))
{
	$userId = app()->session()->get('user.id');
}

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

if(app()->request()->cookie('myCookie')->has())
{
	var_dump( app()->request()->cookie('myCookie')->string() );
}

// 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();

/**
 * @property \Model\Item $item
 * @property \Model\User $user
 */
class Model extends \Exo\Factory\Annotation {}
// usage
$user = Model::getInstance()->user->get($userId);
$price = Model::getInstance()->item->getPrice($itemId);

class User extends \Exo\Factory\Singleton {}
// now this call will return \Model\User::getInstance()
$user = Model::getInstance()->user->get($userId);

/**
 * @property \Database\MySql\Db1\Table1 $table1
 */
class Db1 extends \Exo\Factory\Annotation {}
/**
 * @property \Database\MySql\Db2\Table1 $table1
 */
class Db2 extends \Exo\Factory\Annotation {}
/**
 * @property \Database\MySql\Db1 $db1
 * @property \Database\MySql\Db2 $db2
 */
class Database extends \Exo\Factory\Annotation {}
// usage
Database::getInstance()->db1->table1->insert([...]);
Database::getInstance()->db2->table1->insert([...]);

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() . '"');
}

/**
 * @method \Factory\Item item(int $itemId)
 * @method \Factory\User user(int $userId)
 */
class Factory extends \Exo\Factory\Mapper
{
	// rn $classes;
	}
}
// usage
$price = Factory::getInstance()->item($itemId)->getPrice();
$user = Factory::getInstance()->user($userId)->get();

class User extends \Exo\Factory\Singleton {}
// now this call will return \Factory\User::getInstance()
$user = Factory::getInstance()->user($userId)->get();

function factory(): Factory
{
	return Factory::getInstance();
}
// usage
$user = factory()->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);

\Exo\Validator\AbstractType::setAssertionExceptionDisplayValue(true);

// scalar value
$str = bind('Invalid ID: {$1}', 5);
// multiple scalar values
$str = bind('ID: {$1}, Name: {$2}', 5, 'Shay');

// indexed array
$str = bind('ID: {$0}, Name: {$1}', [5, 'Shay']);
// associative array
$str = bind('ID: {$id}, Name: {$name}', ['id' => 5, 'name' => 'Shay']);

// object
$user = new stdClass; $user->id = 5; $user->name = 'Shay';
$str = bind('ID: {$id}, Name: {$name}', $user);

// indexed multidimensional array
$str = bind('ID: {$0}, Name: {$1}', [[5, 'Shay'], [6, 'Max']]);
// associative multidimensional array
$str = bind('ID: {$id}, Name: {$name}',
	[['id' => 5, 'name' => 'Shay'], ['id' => 6, 'name' => 'Max']]);

// callback example
$str = bind('ID: {$id}, Name: {$name}', ['id' => 5, 'name' => 'Shay'],
	function($object){
		$object->id *= 1000;
		$object->name = strtoupper($object->name);
		return $object;
	}
);

$str = bind('ID: {$id}, Name: {$name}, Roles: {$roles}', [
		['id' => 5, 'name' => 'Shay', 'roles' => ['admin', 'editor']],
		['id' => 6, 'name' => 'Max', 'roles' => ['editor', 'viewer', 'guest']]
	], function($object){
		// convert to something useful
		$object->roles = implode(', ', unserialize($object->roles));
		return $object;
	});
// ID: 5, Name: Shay, Roles: admin, editor
// ID: 6, Name: Max, Roles: editor, viewer, guest

debug('Log this', ['context' => 'example']);

$dbUser = env('DB_USER');
$dbPassword = env('DB_PWD');
$dbName = env('DB_NAME', 'default'); // default value

logger('channel')->info('Log this', ['context' => 'example']);

pa(1, ['one'], new stdClass); // print all values

// setter
share(MY_KEY, 'value');
// getter
$myKey = share(MY_KEY);

$token = token(16);