- Introduction
- Installation
- Usage
- Configs
- Route
- Providers
- Middleware
- Bindings
- Views
- Database
- Tests
- License
Armie is an expressive and extendable lightweight PHP framework designed to provide high performance with all the essential features needed for quick application development.
It is more suited for small applications or microservices but can easily handle the development of large-scale applications with minimum extension or abstraction.
It includes support for different design paradigms and architectural patterns:
- Model-View-Controller (MVC)
- Service-oriented
- Microservices
- Event Driven
- Asynchronous Queuing
composer require busarm/armie
Traditional HTTP server using PHP-FPM and NGINX or Apache.
Run a single application
# ../myapp/public/index.php
define('APP_START_TIME', floor(microtime(true) * 1000));
require __DIR__ . '/../vendor/autoload.php';
$config = (new Config())
->setAppPath(dirname(__DIR__))
->setConfigPath('Configs')
->setViewPath('Views');
$app = new App($config);
$app->get('/product/{id}')->to(ProductController::class, 'get');
$app->run()->send();
Host multiple applications or modules. Supports path and domain routing
# ../index.php
require __DIR__ . '/../vendor/autoload.php';
$server = (new Server())
// Use `myapp` for requests with path `v1/....`
->addRoutePath('v1', __DIR__ . '/myapp/public')
// Use `mydevapp` for requests with domain name `dev.myapp.com`
->addDomainPath('dev.myapp.com', __DIR__ . '/mydevapp/public');
$server->run()->send();
# ../myapp/public/index.php
/**
* @var \Psr\Http\Message\ServerRequestInterface|null $request Capture Server request
* @var \Armie\Interfaces\ServiceDiscoveryInterface|null $discovery Capture Service discovery
*/
require __DIR__ . '/../vendor/autoload.php';
$config = (new Config())
->setAppPath(dirname(__DIR__))
->setConfigPath('Configs')
->setViewPath('Views');
$app = new App($config);
$app->setServiceDiscovery($discovery ?? new LocalServiceDiscovery([]));
$app->get('/product/{id}')->to(ProductController::class, 'get');
return $app->run(Request::capture($request ?? null, $config));
Asynchronous HTTP Server (powered by workerman)
High perfomant Asychronous HTTP Server with support for serveral event-looping providers such as: swoole
, libevent
, ev
, libuv
, react
. Provides the following features:
- Background workers to handle multi processing, asynchronous task and cron job processing
- Socket workers to handle web socket connections
- Concurrency with Promises and built in (
async
,await
,concurrent
) functions - Real-time events with built in (
listen
,dispatch
) functions - Asynchronous queuing with built in (
enqueue
) function
# ./start.php
$config = (new Config())
->setAppPath(dirname(__DIR__))
->setConfigPath('Configs')
->setViewPath('Views');
$app = new App($config);
$app->get('/product/{id}')->to(ProductController::class, 'get');
$app->start("localhost", 8080,
(new HttpServerConfig)
->setLooper(Looper::EV)
->setHttpWorkers(8)
->setTaskWorkers(4)
->addJob(function () {
log_debug("Testing EVERY_MINUTE Cron Job");
}, Cron::EVERY_MINUTE)
->addJob(function () {
log_debug("Testing Custom Seconds Cron Job");
}, 600)
->addJob(function () {
log_debug("Testing One-Time Only Job");
}, (new DateTime('+30 seconds')))
// MessengerSocketController implements SocketControllerInterface
->addSocket(2222, MessengerSocketController::class));
Run command to start application
# Windows
php start.php
# Unix (Linux or Mac) [Recommended]
php start.php start
Configure application
$config = (new Config())
->setAppPath(__DIR__)
->setConfigPath('Configs')
->setViewPath('Views')
->setSecret("mysamplesecret123456")
->setCookieEncrypt(true)
->setHttp((new HttpConfig)
->setCheckCors(true)
->setAllowAnyCorsDomain(true)
->setAllowedCorsHeaders(['*'])
->setAllowedCorsMethods(['GET']))
->setLogRequest(false)
->setSessionEnabled(true)
->setSessionLifetime(60)
->setDb((new PDOConfig)
->setConnectionDriver("mysql")
->setConnectionHost("127.0.0.1")
->setConnectionDatabase('default')
->setConnectionPort(3310)
->setConnectionUsername("root")
->setConnectionPassword("root")
->setConnectionPersist(true)
->setConnectionErrorMode(true)
->setConnectionPoolSize(10)
);
$app = new App($config);
...
Configs can be attached using separate configuration files.
Add config file to your config path. E.g myapp/Configs/database.php
# database.php
// Use constant
define("DB_NAME", "my-db-dev");
define("DB_HOST", "localhost");
// Use dynamic configs
return [
'db_name'=>'my-db-dev',
'db_host'=>'localhost',
];
// Access dynamic configs
// Set
app()->config->set('db_name', 'my-db-dev-2');
// Get
app()->config->get('db_name');
....
$config->addFile('database')
$app = new App($config);
....
Add HTTP routes.
....
$app = new App($config);
$app->get('/user/{id}')->to(UserController::class, 'get');
$app->get('/user/{id}')->to(UserController::class, 'get');
$app->post('/user/{id}')->to(UserController::class, 'create');
$app->put('/user/{id}')->to(UserController::class, 'update'),
$app->delete('/user/{id}')->to(UserController::class, 'delete'),
$app->run()->send();
....
$app = new App($config);
$app->get('/user/{id}')->call(function (RequestInterface $request, string $id) {
// Perform action ...
});
$app->run()->send();
....
$app = new App($config);
$app->get('/user/{id}')->view(UserPage::class);
$app->run()->send();
....
$app = new App($config);
// Using Custom Route Class - Single
$app->router->addRoute(MyRoute::get('/user/{id}')->to(UserController::class, 'get'));
// Using Custom Route Class - List
$app->router->addRoutes([
MyRoute::get('/user/{id}')->to(UserController::class, 'get'),
MyRoute::post('/user')->to(UserController::class, 'create'),
MyRoute::put('/user/{id}')->to(UserController::class, 'update'),
MyRoute::delete('/user/{id}')->to(UserController::class, 'delete'),
]);
$app->run()->send();
Extend application features and configurations.
class CustomProvider implements ProviderInterface
{
/**
* @inheritDoc
*/
public function process(App $app): void
{
// Perform custom action....
}
}
...
$app = new App($config);
$app->addProvider(new CustomProvider());
...
Intercept HTTP request and response. PSR Middleware supported.
class AuthenticateMiddleware implements MiddlewareInterface
{
public function process(RequestInterface|RouteInterface $request, RequestHandlerInterface $handler): ResponseInterface {
// Perform custom action....
// Or forward to next request handler
return $handler->handle($request);
}
}
# Attach global middleware
....
$app = new App($config);
$app->addMiddleware(new AuthenticateMiddleware())
....
# Attach middleware to specific route
....
$app->put('/user/{id}')->to(UserController::class, 'update')->middlewares([
new AuthenticateMiddleware()
]);
$app->router->addRoute(
Route::put('/user/{id}')->to(UserController::class, 'update')->middlewares([
new AuthenticateMiddleware()
])
);
....
Bind an interface to a particular class. Hence, the specified class object will be used when resolving dependencies.
....
$app = new App($config);
$app->addBinding(CacheInterface::class, RedisCache::class)
....
// Manually
$cache = app()->make(CacheInterface::class)
// Automatically
class UserController
{
public function __construct(private CacheInterface $cache)
{
}
}
Add view file to your view path. E.g myapp/Views/login.php
# In Controller (or anywhere you wish to load view)
// Using app instance
app()->loader->view('login', ['username' => $uname, 'password' => $pass]);
// Using helpers
view('login', ['username' => $uname, 'password' => $pass]);
Add view file(s) to your view path. E.g myapp/Views/LoginPage.php
, myapp/Views/components/login.php
# In-line rendering
class LoginPage extends View
{
public function __construct(protected LoginPageDto|BaseDto|array|null $data = null, protected $headers = array())
{
}
public function render()
{
$header = new HeaderComponent;
return <<<HTML
<html>
<body>
<div>{$header}</div>
<div>Username: {$this->get("username")}</div>
</body>
</html>
HTML;
}
}
# Component rendering
class LoginPage extends View
{
public function __construct(protected LoginPageDto|BaseDto|array|null $data = null, protected $headers = array())
{
}
public function render()
{
return $this->include('components/login', true);
}
}
A simple but expressive database object-relational mapper (ORM) built on top of PHP Data Objects (PDO)
class ProductModel extends Model
{
/**
* @inheritDoc
*/
public function getFields(): array
{
return [
new Field('id', DataType::INT),
new Field('name', DataType::STRING),
new Field('type', DataType::STRING),
new Field('qty', DataType::INT),
new Field('categoryId', DataType::INT),
new Field('createdAt', DataType::DATETIME),
new Field('updatedAt', DataType::DATETIME),
new Field('deletedAt', DataType::DATETIME)
];
}
/**
* @inheritDoc
*/
public function getRelations(): array
{
return [
new OneToOne('category', $this, new Reference(CategoryTestModel::class, ['categoryId' => 'id']))
];
}
/**
* @inheritDoc
*/
public function getTableName(): string
{
return 'products';
}
/**
* @inheritDoc
*/
public function getKeyName(): ?string
{
return 'id';
}
/**
* @inheritDoc
*/
public function getCreatedDateName(): ?string
{
return 'createdAt';
}
/**
* @inheritDoc
*/
public function getUpdatedDateName(): ?string
{
return 'updatedAt';
}
/**
* @inheritDoc
*/
public function getSoftDeleteDateName(): ?string
{
return 'deletedAt';
}
}
$model = ProductModel::create(['name' => 'IPhone 14', 'qty' => 3, 'type' => 'Mobile Phone', 'categoryId' => 1]);
$model = ProductModel::update(1, ['name' => 'IPhone 14', 'qty' => 3, 'type' => 'Mobile Phone', 'categoryId' => 1]);
// Or
...
$product = new ProductModel;
$product->load(['name' => 'IPhone 14', 'qty' => 3, 'type' => 'Mobile Phone', 'categoryId' => 1]);
$product->save();
...
$model = ProductModel::findById(1);
// Or
$model = (new ProductModel)->find(1);
...
$model = ProductModel::getAll();
// Or
$model = (new ProductModel)->all();
class ProductRepository extends Repository
{
public function __construct()
{
parent::__construct(new ProductModel);
}
}
// Or - Use Generic Repository
$productRepo = new Repository(new ProductModel)
...
$productRepo = new ProductRepository();
$result = $productRepo->paginate(1, 3);
To execute the test suite, you'll need to install all development dependencies.
$ git clone https://github.com/busarm/armie
$ composer install
$ composer test
You can use PHP server built-in server to test:
$ php -S localhost:8181 -t tests/app/v1
The Armie Framework is licensed under the MIT license. See License File for more information.