Quick Start
This brief tutorial will help you get your first PHPolar application up and running.
Create a demo application
# composer create-project phpolar/skeleton example-app
# cd example-app
# php -S localhost:0 -t public
Set up form validation
<?php
final class Pet
{
#[MaxLength(20)]
#[PrimayKey]
#[Required]
public string $id;
#[MaxLength(20)]
public string $breed;
#[Label("Name of Pet")]
#[MaxLength(50)]
public string $name;
#[Max(300)]
public float $weight;
}
Create template *
<?php
(function (Pet $view) {
?>
<!-- ... -->
<form method="post" action="/process-pet-form">
<input type="hidden" name="id" value="<?= $view->id ?>">
<label for="name"><?= $view->getLabel("name") ?></label>
<input type="text" id="name" name="name" value="<?= $view->name ?>">
<!-- ... -->
* The example above is using the PurePHP library. Any templating library may be used.
Set up data storage
<?php
return [
"PET_DATA_STORAGE" => new PetStorage(/*...*/),
];
Set Up Request Handling
final class GetPetForm implements RoutableInterface
{
#[Inject("PET_DATA_STORAGE")]
public AbstractStorage $petStorage;
public function process(#[Model] ?Pet $pet = null): string
{
// ...
return $responseBodyContent;
}
}
final class SubmitPetForm implements RoutableInterface
{
#[Inject("PET_DATA_STORAGE")]
public AbstractStorage $petStorage;
public function process(#[Model] ?Pet $pet = null): string
{
// ...
return $responseBodyContent;
}
}
Set Up Routing
$routes = new RouteMap($propertyInjector);
$routes->add(RequestMethods::GET, "/pet-form", $getPetFormTarget);
$routes->add(RequestMethods::POST, "/pets", $submitPetFormFactory);
Set Up Container
$container = (new ContainerLoader())->load($container, $dependencyMap);
Requires a PSR-11 Container
Start App
App::create($container)->receive($request);
You have just created a web application with templating, routing, and dependency injection.
Libraries
phpolar/csv-file-storage
Adds support for storing data on disk in CSV formatphpolar/model
Provides support for configuring the properties of an object for validation, formatting, and storagephpolar/property-injector
Provides automatic dependency injection for propertiesphpolar/pure-php
Pure PHP Templatesphpolar/validators
A set of property validatorsHow to Override Framework Defaults
Override framework defaults by adding a PHP file that returns an
associative array to
src/config/dependencies/conf.d/
.
What Can be Overriden?
Behavior | Dependency Identifier | Replacement Must Implement |
---|---|---|
The last middleware executed by the framework's default routing middleware. |
Phpolar\Phpolar\Http\RoutingMiddleware::class
|
Psr\Http\Server\MiddlewareInterface
|
Emits the PSR-7 HTTP Response. |
Phpolar\Phpolar\DependencyInjection\DiTokens::RESPONSE_EMITTER
|
Laminas\HttpHandlerRunner\Emitter\EmitterInterface
|
See https://www.php-fig.org/psr/psr-15/meta/#queue-based-request-handler |
Phpolar\Phpolar\Http\MiddlewareQueueRequestHandlerInterface::class
|
Psr\Http\Message\RequestHandlerInterface
|
Example
return [
Phpolar\Phpolar\Http\RoutingMiddleware::class =>
// provide an implementation of Psr\Http\Server\MiddlewareInterface
$middlewareOverride,
// ...
];
Design Goals and Intent
This documents the design goals, objectives, and strategies the authors of the framework have decided on and the intent behind those decisions.
-
Testability
Use property dependency injection instead of the service locator pattern.
Intent
Reduce the amount of work required to write tests.
with Service Locator Pattern final class CumbersomeTest
{
public function testA(): void
{
// ..
$sut->dep = $this->createStub(SomeDependency::class);
$container = $this->createStub(ContainerInterface::class);
$resut = $sut->process($container, /* ... */);
// ..
}
}with property injection final class BetterTest
{
public function testA(): void
{
// ..
$sut->dep = $this->createStub(SomeDependency::class);
$resut = $sut->process(/* ... */);
// ..
}
} -
Simplicity
Use the dependency injection container for application configuration.
Intent
Allow for simple application bootstrapping.
App::create($container)->receive($request);
// that's it -
Type Safety
Require instances instead of class name strings for route targets when configuring routes
Intent
Prevent passing incorrect strings when setting up routing.
Instead of $route->add(/* ... */, RoutableA::class); // <-this is just a string
incorrectly passing a string that is not a class name $route->add(/* ... */, "not a class name");
this is more type safe $route->add(/* ... */, $routeTarget);
-
Flexibility
-
Configure the dependency injection container by placing
regular PHP files that return an array of
dependencies in
src/config/dependencies/conf.d/
. The key of each key value pair should be the dependency identifier. When the ContainerLoader is used, the dependencies will automatically be loaded into the container. - Uses several PSR approved by PHP-FIG to support user preferences for libraries
-
Support optional middleware usage App::create($container)
->use($authenticationCallbackMiddleware)
->use($middlewareB)
->use($middlewareC)
->receive($request);
-
Configure the dependency injection container by placing
regular PHP files that return an array of
dependencies in
PSRs Supported
Number | Title |
---|---|
3 | Logger Interface |
7 | HTTP Message Interface |
11 | Container Interface |
15 | HTTP Handlers |
17 | HTTP Factories |