Application #git

Application (as a class froq\App and internally a global variable $app) is the base where all application logic is being run and managed, and comes with lots of handy / ready-to-use properties and methods. Here we'll see all properties & methods step by step examples.

Checking URI root (base) & environment

public function fooAction() {
    // true (if root is "/" as default or defined like "/api/v1").
    assert($this->app->isRoot() === true);
    assert(($this->app->root === '/api/v1') === true);

    // Environment property.
    assert($this->app->env instanceof \froq\AppEnv);

    // true (if app is running on PHP's built-in server or a server like "foo.local").
    assert($this->app->env->isLocal() === true);
    assert($this->app->env->isDevelopment() === true);
    assert(($this->app->env == \froq\AppEnv::DEVELOPMENT) === true); // Mind "==" here.
}

Accessing / using Logger, Request, Response properties

public function fooAction() {
    // Level is LogLevel::ALL as default (can be changed via $logger->setLevel()).
    assert($this->app->logger instanceof \froq\log\Logger);

    // Global request / response objects.
    assert($this->app->request instanceof \froq\http\Request);
    assert($this->app->response instanceof \froq\http\Response);

    // As a shortcut, as same as the properties above.
    assert($this->request instanceof \froq\http\Request);
    assert($this->response instanceof \froq\http\Response);
}

Optional properties Session, Database, Cache

These properties ($session, $database and $cache) will only be created automatically by given the configuration options in app/config/config.php file and accessible from all over the controllers (e.g. $this->app->session).

// Sample Session options (as default):
'session' => true,
// Sample Session options (as custom):
'session' => [
    'name'     => string,
    'hash'     => 'uuid' /* or 32, 40, 16 */, 'hashUpper' => bool,
    'savePath' => 'path/to/session-folder', 'saveHandler' => /* class or [class, class-file] */,
    'cookie'   => [
        'lifetime' => int,  'path'     => string, 'domain'   => string,
        'secure'   => bool, 'httponly' => bool,   'samesite' => string,
    ],
],

// Sample database options.
'database' => [
    'dsn'       => 'pgsql:host=localhost;dbname=test;sslmode=disable',
    'user'      => string, 'pass' => string,
    'logging'   => array /* default=null */,
    'profiling' => bool /* default=false */,
]

// Sample cache options.
'cache' => [
    'id' => string, 'options' => [
        'id'     => ?string,
        'ttl'    => ?int, /* default=60 */
        'agent'  => 'file|apcu|redis|memcached',
        'static' => bool /* default=true */,
    ]
]

Note: As you may guess, all these properties are app-wide global properties. So, if any specific database or cache is necessary, they could be created and used in-place.

Caching operations

Application's cache is a global approach to caching operations and these operations can be done by the following two methods. In any case where another caching approach (or agent) is needed, froq\cache\CacheFactory can be used.

// Set a cache item.
$this->app->cache(key, value, ?ttl);

// Get a cache item.
value = $this->app->cache(key);

// Delete a cache item or all items.
$this->app->uncache(key);
$this->app->uncache('*');

Config operations

Froq! keeps all the (app) configuration options read-only, meaning that, all options can only be set via app/config/config.php file but are accessible application-wide via $app->config() method.

// Set an option in config.php file.
'allowedMimeTypes' => ['image/jpeg', 'image/jpe', 'image/jpg']

// Get/use an option.
public function uploadAction() {
    $allowedMimeTypes = $this->app->config('allowedMimeTypes');
    $uploadedFileMimeType = /* Resolved or received MIME type. */;

    if (!array_contains($allowedMimeTypes, $uploadedFileMimeType)) {
        throw new UnsupportedMediaTypeException(); /* froq\http\exception\client */
        // throw new HttpException(code: 415);     /* Alternative: froq\http */
        // throw $this->createHttpException(415);  /* Alternative: froq\app\Controller. */
    }
}

Logging operations

General / arbitrary logging operations can be done with $app->log() method (uses $app->logger property, an instance of froq\log\Logger) or with $app->logger property directly. If any other specific logger is required, againg froq\log\Logger class or an external class can be used in case.

Note: While the default app's log level is LogLevel::ALL, it can also be changed in configuration file via log.level option or $app->logger->setLevel() method using froq\log\LogLevel (pseudo-enum) class constants (source).

public function loginAction() {
    [$username, $password] = $this->postParams(['username', 'password']);

    // Login helper (just an example).
    if (!Login::execute($username, $password)) {
        $this->app->log(format(
            'Failed login attempt, username: %s, password: %s, ip: %s',
            $username, $password, $this->request->client->getIp()
        ));

        // Or directly.
        // $this->app->logger->logInfo('Failed login attempt, ...');

        throw new UnauthorizedException();        /* froq\http\exception\client */
        // throw new HttpException(code: 401);    /* Alternative: froq\http */
        // throw $this->createHttpException(401); /* Alternative: froq\app\Controller. */
    }
}

Registering & using services

While services can be registered via app/confing/services.php file, that also can be done $app->service() method. But the thing is, if you don't use a main controller (e.g. AppController extended by all other related controllers), it's useless and you'd better use the services file only. Otherwise, we're going with AppController example here, but also going to see getting / using registered services.

// AppController
public function init() {
    // Add a callable returning string.
    $this->app->service('hello', static function ($name) {
        return format('Hello, %s', $name);
    });

    // Add a callable returning an object instance.
    $this->app->service('hello', static function ($name) {
        return new \app\library\Hello($name);
    });
}

// Other controller.
public function saveAction() {
    /** @var callable */
    $hello = $this->app->service('hello');
    $result = $hello($this->getParam('name'));

    /** @var \app\library\Hello */
    $hello = $this->app->service('hello')($this->getParam('name'));
    $result = $hello->greet();
}

Route (micro) methods

The froq\App class comes with some micro methods to provide these GET, POST, PUT, DELETE route definitions easy-in-short without using app/config/routes.php file, and this methods must be called in pub/index.php file inside of prepare() part. For other any specific HTTP-method related (e.g: PATCH) routes can be defined via route() method.

Note: Every given callable is binded to froq\app\Controller class, meaning that, $this variable is available as an instance of this class inside of these callables.
Note: Not everytime a closure callable is needed for a callback routine, meaning that, you can still declare controller classes and given their actions as callables.

Simple example

// A route with controller action.
$app->get('/book/:id', 'Book.show');

// A route with closure callable.
$app->get('/book/:id', function ($id) { ... });

// Another HTTP-method route.
$app->route('/book/:id', 'PATCH', function ($id) { ... });

Detailed example

// File: pub/index.php
use froq\http\response\Status;

// Up to you.
use app\entity\Book;
use app\service\BookService;

...
->prepare(function ($app) {
    ...
    BookService::setApp($app);

    // Send a Book.
    $app->get('/book/:id', function (int $id): void {
        /** @var Book|null */
        $book = BookService::findBook($id);

        $this->response->json(
            $book?->isFound() ? Status::OK : Status::NOT_FOUND,
            ['data' => ['book' => $book], 'error' => null]
        );
    });

    // Create a Book.
    $app->post('/book', function (): void {
        /** @var array */
        $data = $this->request->json();

        /** @var Book|null */
        $book = BookService::saveBook($data, $error);

        $this->response->json(
            $book?->isSaved() ? Status::CREATED : Status::INTERNAL_ERROR,
            ['data' => ['book' => $book], 'error' => $error]
        );
    });

    // Update a Book.
    $app->put('/book/:id', function (int $id): void {
        /** @var array */
        $data = $this->request->json();
        $data['id'] = $id;

        /** @var Book|null */
        $book = BookService::saveBook($data, $error);

        $this->response->json(
            $book?->isSaved() ? Status::ACCEPTED : Status::INTERNAL_ERROR,
            ['data' => ['book' => $book], 'error' => $error]
        );
    });

    // Delete a Book.
    $app->delete('/book/:id', function (int $id): void {
        /** @var Book|null */
        $book = BookService::removeBook($id);

        $this->response->json(
            $book?->isRemoved() ? Status::OK : Status::NOT_FOUND,
            ['data' => ['book' => $book], 'error' => null]
        );
    });
})

Note: If you don't want to push all these rotuing stuff into pub/index.php file, you can separate each related routing file and include them into index file.

...
->prepare(function ($app) {
    ...

    // Shortcut routes.
    include 'book.php';
    include 'book_author.php';
})
Getting Started Installation Configuration Web Servers
Application & Components Application Controller Repository View Routing
HTTP & Components Request Response Payloads
Database & Components Database Queries & Results Transactions Entities
Utilities Sugars Sugar Classes Sugar Functions Sugar Constants
Froq! Framework · Home · Docs · API · GitHub