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

src/Pet.php
<?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 *

src/templates/pet-form.phtml
<?php

/**
 * @var Pet
 */
$view $this;
?>
<!-- ... -->
<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

src/config/dependencies/conf.d/pet-storage.php
<?php

return [
    
"PET_DATA_STORAGE" => new PetStorage(/*...*/),
];

Set Up Request Handling

src/GetPetForm.php
final class GetPetForm implements RoutableInterface
{
    #[
Inject("PET_DATA_STORAGE")]
    public 
AbstractStorage $petStorage;

    public function 
process(#[Model] ?Pet $pet null): string
    
{
        
// ...
        
return $responseBodyContent;
    }
}
src/SubmitPetForm.php
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

public/index.php
$routes = new RouteMap($propertyInjector);
$routes->add(RequestMethods::GET"/pet-form"$getPetFormTarget);
$routes->add(RequestMethods::POST"/pets"$submitPetFormFactory);

Set Up Container

public/index.php
$container = (new ContainerLoader())->load($container$dependencyMap);

Requires a PSR-11 Container

Start App

public/index.php
App::create($container)->receive($request);

You have just created a web application with templating, routing, and dependency injection.

Libraries

phpolar/csrf-protection

Provides request checking and response filtering to mitigate CSRF attacks

phpolar/csv-file-storage

Adds support for storing data on disk in CSV format

phpolar/model

Provides support for configuring the properties of an object for validation, formatting, and storage

phpolar/property-injector

Provides automatic dependency injection for properties

phpolar/pure-php

Pure PHP Templates

phpolar/validators

A set of property validators

How 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

src/config/dependencies/config.d/overrides.php
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.

  1. 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(/* ... */);
            
    // ..
        
    }
    }
  2. Simplicity

    Use the dependency injection container for application configuration.

    Intent

    Allow for simple application bootstrapping.

    App::create($container)->receive($request);
    // that's it
  3. 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);
  4. 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);
PSRs Supported
Number Title
3 Logger Interface
7 HTTP Message Interface
11 Container Interface
15 HTTP Handlers
17 HTTP Factories