PHPackages                             illogical/lava - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. illogical/lava

ActiveLibrary[Framework](/categories/framework)

illogical/lava
==============

A PHP micro-framework

1.2.2(1y ago)436MITPHPPHP &gt;=5.4.0

Since Apr 5Pushed 1y ago1 watchersCompare

[ Source](https://github.com/illogical-ru/lava)[ Packagist](https://packagist.org/packages/illogical/lava)[ RSS](/packages/illogical-lava/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (13)Used By (0)

Lava
====

[](#lava)

Micro-Framework

Requirements: PHP 5.4+, 7+, 8+

Installation
------------

[](#installation)

If you are using [Composer](https://getcomposer.org/), run the command

```
composer require illogical/lava
```

Or you can [download the archive](https://github.com/illogical-ru/lava/archive/master.zip)

Usage
-----

[](#usage)

Use the Lava\\App class

```
use Lava\App;
```

If you downloaded the archive, use the autoloader

```
require_once '.../Lava/Autoloader.php';

$al = new Lava\Autoloader;
$al->register();
```

Environment
-----------

[](#environment)

### Lava\\App::conf(\[data\]) : accessor

[](#lavaappconfdata--accessor)

Config

```
App::conf([
    'charset' => 'utf-8',             // encoding for HTTP headers
    'type'    => 'html',              // default type
    'home'    => '/path-to-home',     // home folder
    'pub'     => '/pub-uri',          // public folder
    'safe'    => [
        // default values
        'algo' => 'md5',              // hashing algorithm
        'sign' => '',                 // signature
        'salt' => '0123456789abcdef', // salt character set
    ],
    'storage' => [
        // default storage, named "0"
        [
            'driver'   => 'PDO',
            'dsn'      => 'mysql:.../mysqld.sock;dbname=test;',
            'username' => 'root',
            'password' =>  NULL,
        ],
        'db2' => [...],
        ...
    ],
]);

echo App::conf()->charset; # utf-8
```

### Lava\\App::env() : accessor

[](#lavaappenv--accessor)

Environment

```
echo       App::env()->method;    # GET
var_export(App::env()->accept()); # array (0 => 'text/html', 1 => '*/*')
```

### Lava\\App::args() : accessor

[](#lavaappargs--accessor)

Variables

Value priority: custom, POST, GET

```
// URL: http://example.com/sandbox/?foo=3&bar=4&foo=5

echo       App::args()->foo;    # 5
var_export(App::args()->foo()); # array (0 => '3', 1 => '5')
```

### Lava\\App::cookie() : accessor

[](#lavaappcookie--accessor)

Cookies

Offsets for expire:

- s - second
- m - minute
- h - hour
- D - day
- W - week
- M - month
- Y - year

```
// setting
App::cookie()->foo = 'bar';
App::cookie()->bar = [1, 2, 3];

// read
echo       App::cookie()->foo;    # bar
var_export(App::cookie()->bar()); # array (0 => '1', 1 => '2', 2 => '3')

// additional parameters: expire, path, domain, secure
App::cookie()->foo('bar', '1M');  // expire = 1 month
```

### Lava\\App::host(\[scheme \[, subdomain\]\]) : host

[](#lavaapphostscheme--subdomain--host)

Returns the name of the host

If `scheme` is TRUE, then the current scheme

```
echo App::host();      # host
echo App::host(TRUE);  # http://host
echo App::host('ftp'); # ftp://host
```

### Lava\\App::home(\[node, ...\]) : home

[](#lavaapphomenode---home)

Returns the home folder

If not set in the config, then the current folder where the script is running

```
echo App::home();             # /path-to-home
echo App::home('foo', 'bar'); # /path-to-home/foo/bar
```

### Lava\\App::pub(\[node, ...\]) : pub

[](#lavaapppubnode---pub)

Returns the public folder

If not set in the config, then the current folder

```
echo App::pub();             # /pub-uri
echo App::pub('foo', 'bar'); # /pub-uri/foo/bar
```

### Lava\\App::uri(\[path|route \[, data \[, append\]\]\]) : uri

[](#lavaappuripathroute--data--append--uri)

Returns URI

Variables from `data` will be appended as query\_string

The `append` flag adds the current query\_string

```
// URL: http://example.com/sandbox/?zzz=456

echo App::uri();                        # /sandbox/
echo App::uri('foo', ['bar' => 123]);   # /sandbox/foo?bar=123
echo App::uri('/foo', 'bar=123', TRUE); # /foo?zzz=456&bar=123
```

### Lava\\App::url(\[path|route \[, data \[, append\]\]\]) : url

[](#lavaappurlpathroute--data--append--url)

Returns URL

```
// URL: http://example.com/sandbox/?zzz=456

echo App::url();                        # http://example.com/sandbox/
echo App::url('foo', ['bar' => 123]);   # http://example.com/sandbox/foo?bar=123
echo App::url('/foo', 'bar=123', TRUE); # http://example.com/foo?zzz=456&bar=123
```

Routes
------

[](#routes)

### Lava\\App::route(\[rule \[, cond\]\]) : route

[](#lavaapprouterule--cond--route)

Placeholder `:name` corresponds to the full fragment `([^\/]+)`

Placeholder `#name` matches the name `([^\/]+?)(?:\.\w*)?`

Placeholder `*name` matches the remainder `(.+)`

You can add a restriction on environment variables in the additional `cond` conditions

If the rule does not start with a slash, it will be appended to the public folder `Lava\App::pub()`

```
// URL: http://example.com/foo1.bar/foo2.bar/foo3.bar/foo4.bar
App ::route('/:node1/#node2/*node3')
    ->to   (function($node1, $node2, $node3) { // handler
        echo $node1;                           #  foo1.bar
        echo $node2;                           #  foo2
        echo $node3;                           #  foo3.bar/foo4.bar
    });
// route search
App::routes_match();

// environment constraint
App::route('/foo', [
    'method'     => ['GET', 'HEAD'], // if the method is GET or HEAD
    'user_addr'  => '127.0.0.1',     // and the user is local
    'user_agent' => '/^Mozilla/',    // and the browser is Mozilla
]);

// method restriction only
App::route('/foo', 'DELETE');
```

### Lava\\App::route\_get(\[rule\]) : route

[](#lavaapproute_getrule--route)

Restrict the route to the GET method

```
App::route_get ('/foo');
// analog
App::route     ('/foo', 'GET');
```

### Lava\\App::route\_post(\[rule\]) : route

[](#lavaapproute_postrule--route)

```
App::route_post('/foo');
```

### Lava\\App::routes\_match() : result

[](#lavaapproutes_match--result)

Executes handlers for matched routes

If the handler returns `TRUE`, it continues checking the rest of the routes, otherwise it stops checking and returns the result of the handler

```
App::routes_match();
```

### route-&gt;cond(cond) : route

[](#route-condcond--route)

Add an environment constraint to the route

```
App ::route('/foo')
    ->cond (['user_addr' => '/^192\.168\./']);
```

### route-&gt;name(name) : route

[](#route-namename--route)

Used to convert a route to a path

```
// URL: http://example.com/foo/123
App ::route('/foo/#id')
    ->name ('route_name')
    ->to   (function($id) {
        echo App::uri('route_name', ['id' => $id + 1]); #  /foo/124
    });
```

### route-&gt;to(mixed) : route

[](#route-tomixed--route)

Route handler

Default method `Lava\App::env()->method`

```
// function
App::route('/foo')->to(function() {echo 'hello';});

// class|namespace, method
App::route('/foo')->to('Controller\Foo', 'bar');

// file, method
// the class name must match the file name
// an instance of the Foo class will be created and the bar method will be called
App::route('/foo')->to('controller/Foo.php', 'bar');

// file, class|namespace, method
App::route('/foo')->to('controller/Foo.php', 'Ctrl\Foo', 'bar');
// if the class is different from the file name or if we need to specify a namespace
```

Rendering
---------

[](#rendering)

### Lava\\App::render(handlers) : has\_handler

[](#lavaapprenderhandlers--has_handler)

Executes a handler with type `Lava\App::type()`, if none exists, then with index `0`

If there is no type of the requested data, `Lava\App::conf()->type` is used

If the type is `json` and there is a `Lava\App::args()->callback` value, returns `JSONP`

```
App::route('/page')->to(function() {
    App::render([
        'html' => 'HTML CONTENT',
        'json' => ['bar' => 123],
        function () {
            echo 'OTHER TYPE: ' . App::type();
        },
    ]);
});

// URL: http://example.com/page.html
App::routes_match(); # HTML CONTENT

// URL: http://example.com/page.json
App::routes_match(); # {"bar":123}

// URL: http://example.com/page.xml
App::routes_match(); # OTHER TYPE: xml

// if Lava\App::conf()->type == 'html'
// URL: http://example.com/page
App::routes_match(); # HTML CONTENT
```

### Lava\\App::redirect(\[url|uri|route \[, data \[, append\]\]\]) : void

[](#lavaappredirecturluriroute--data--append--void)

Appends to the `Location` header

```
App::redirect('/foo');
```

Security
--------

[](#security)

### Lava\\App::safe()-&gt;uuid() : uuid

[](#lavaappsafe-uuid--uuid)

Returns UUID

You can specify the hashing algorithm in the config, the default is `md5`

```
echo App::safe()->uuid(); # 055fb982653fef1ae76bde78b10f7221
```

### Lava\\App::safe()-&gt;uuid\_signed() : \[signed\_uuid, uuid\]

[](#lavaappsafe-uuid_signed--signed_uuid-uuid)

Returns the signed UUID

You can specify the signature in the config, defaults to an empty string

```
list($signed, $uuid) = App::safe()->uuid_signed();

echo $signed; # 31bd185d9b3929eb56ae6e4712b73962dcd6b2b55b5287117b9d65380f4146e3
echo $uuid;   # 31bd185d9b3929eb56ae6e4712b73962
```

### Lava\\App::safe()-&gt;check(signed\_uuid) : uuid

[](#lavaappsafe-checksigned_uuid--uuid)

Checks the signed UUID

```
echo App::safe()->check($signed); # 31bd185d9b3929eb56ae6e4712b73962
```

### Lava\\App::safe()-&gt;salt(size) : random\_string

[](#lavaappsafe-saltsize--random_string)

Returns a random string of the given length

You can change the list of available characters in the config, default is `0123456789abcdef`

```
echo App::safe()->salt(16); # f8da4f571ec3de9d
```

Validation
----------

[](#validation)

### Lava\\App::is\_valid(val, tests) : bool\_result

[](#lavaappis_validval-tests--bool_result)

Tests:

- tinyint\[:unsigned\]
- smallint\[:unsigned\]
- mediumint\[:unsigned\]
- integer\[:unsigned\]
- bigint\[:unsigned\]
- numeric\[:precision\[:scale\]\]
- boolean
- string\[:min\_size\[:max\_size\]\]
- char\[:size\]
- email
- url
- ipv4
- date
- time
- datetime
- lt\[:num\]
- lte\[:num\]
- gt\[:num\]
- gte\[:num\]
- bool
- array
- regexp
- function

```
// the string is between 1 and 20 characters and matches Email
echo App::is_valid('me@example.com', ['string:1:20', 'email']); # TRUE
```

Storage\\PDO
------------

[](#storagepdo)

### Lava\\Storage::source('PDO', opts) : storage

[](#lavastoragesourcepdo-opts--storage)

Creation

```
$storage = Lava\Storage::source('PDO', [
    'dsn'      => 'mysql:unix_socket=...mysqld.sock;dbname=name',
    'username' => 'root',
    'password' => '',
]);
```

### storage-&gt;exec(query\[, bind\]) : row\_count

[](#storage-execquery-bind--row_count)

Runs the SQL query and returns the number of rows involved in its execution

```
$storage->exec('DELETE FROM users');

$storage->exec(
    'INSERT INTO users (login, email) VALUES (?, ?)',
    ['username', 'abc@mail']
);

$storage->exec(
    'INSERT INTO users (login, email) VALUES (:login, :email)',
    ['login' => 'username', 'email' => 'abc@mail']
);
```

### storage-&gt;fetch(query\[, bind\]) : row

[](#storage-fetchquery-bind--row)

Fetching a row from the result set

```
$user = $storage->fetch('SELECT * FROM users WHERE id = ?', 123);
```

### storage-&gt;fetch\_all(query\[, bind\[, index\]\]) : rows

[](#storage-fetch_allquery-bind-index--rows)

Fetching all rows from the result set

`index` is used to specify the name of the field whose value will become the index

```
$users = $storage->fetch_all('SELECT * FROM users');
```

### storage-&gt;last\_insert\_id() : id

[](#storage-last_insert_id--id)

ID of the last inserted row

```
$id = $storage->last_insert_id();
```

### storage-&gt;error() : error\_info

[](#storage-error--error_info)

Driver-defined error message

```
$error = $storage->error();
```

### storage-&gt;factory(\[target\]) : factory

[](#storage-factorytarget--factory)

Query factory

#### factory-&gt;get(\[index\]) : rows

[](#factory-getindex--rows)

Data selection

```
// index data with id value
$data = $storage->factory('users')->get('id');
# query: SELECT * FROM `users`
```

#### factory-&gt;one() : row

[](#factory-one--row)

Selecting one record

```
$data = $storage->factory('users')->one();
# query: SELECT * FROM `users` LIMIT ?
# bind:  1
```

#### factory-&gt;columns(expression) : factory

[](#factory-columnsexpression--factory)

Columns or calculations

```
$data = $storage
    ->factory('users')
    // columns(expression)
    ->columns('id')
    // columns([alias => expression, ...])
    ->columns(['full_name' => 'CONCAT(first_name, " ", last_name)'])
    ->get();
# query: SELECT `id`, CONCAT(first_name, " ", last_name) AS `full_name` FROM `users`
```

#### factory-&gt;\*join(target, relations\[, bind\]) : factory

[](#factory-jointarget-relations-bind--factory)

Table joins

```
$data = $storage
    ->factory('users')
    ->join('profiles', 'id')
    ->left_join('sessions', 'sessions.user_id = users.id')
    ->right_join('roles', ['roles.user_id' => 'users.id', 'roles.id' => '?'], 123)
    ->get();
# query: SELECT * FROM `users`
#        JOIN `profiles` USING (`id`)
#        LEFT JOIN `sessions` ON sessions.user_id = users.id
#        RIGHT JOIN `roles` ON `roles`.`user_id` = `users`.`id` AND `roles`.`id` = ?
# bind:  123
```

#### factory-&gt;filter\*(expression) : factory

[](#factory-filterexpression--factory)

Data filtering: `filter_eq`, `filter_ne`, `filter_lt`, `filter_gt`, `filter_lte`, `filter_gte`, `filter_like`, `filter_not_like`, `filter_in`, `filter_not_in`, `filter_between`, `filter_is_null`, `filter_is_not_null`, `filter_raw`

Context change: `filter_and`, `filter_or`, `filter_not`

`filter` works depending on context, for data as `filter_eq`, for closures as `filter_and`

```
$data = $storage
    ->factory('users')
    // filter(key, val)
    ->filter_eq('id', 123)
    // filter(expression)
    ->filter_ne(['name' => 'guest', 'email' => 'test'])
    ->get();
# query: SELECT * FROM `users` WHERE (`id` = ? AND (`name` != ? AND `email` != ?))
# bind:  123, 'guest', 'test'

$data = $storage
    ->factory('users')
    ->filter_or(['name' => 'guest', 'email' => 'test'])
    ->get();
# query: SELECT * FROM `users` WHERE (`name` = ? OR `email` = ?)
# bind:  'guest', 'test'

$data = $storage
    ->factory('users')
    ->filter_or(function($filter) {
        // in a closure without the filter_ prefix
        $filter ->like('name', '%test%')
                ->and(function($filter) {
                    $filter->gt('id', 10)->lte('id', 20);
                })
                ->is_not_null('email');
    })
    ->get();
# query: SELECT * FROM `users` WHERE (`name` LIKE ? OR (`id` > ? AND `id` factory('users')
    ->filter_not(function($filter) {
        $filter ->or(['name' => 'guest', 'email' => 'test'])
                ->in('role_id', [2, 4, 6]);
    })
    ->get();
# query: SELECT * FROM `users` WHERE NOT ((`name` = ? OR `email` = ?) AND `role_id` IN (?, ?, ?))
# bind:  'guest', 'test', 2, 4, 6

// subqueries

$data = $storage
    ->factory('users')
    ->filter_in('id', $storage->factory('sessions')->columns('user_id')->filter('active', 1))
    ->get();
# query: SELECT * FROM `users` WHERE `id` IN (SELECT `user_id` FROM `sessions` WHERE `active` = ?)
# bind:  1
```

#### factory-&gt;group\_by(expression) : factory

[](#factory-group_byexpression--factory)

Grouping

```
$data = $storage
    ->factory('users')
    ->columns(['role_id', 'count' => 'COUNT(*)'])
    ->group_by('role_id')
    ->get();
# query: SELECT `role_id`, COUNT(*) AS `count` FROM `users` GROUP BY `role_id`
```

#### factory-&gt;having\*(expression) : factory

[](#factory-havingexpression--factory)

Similar to `filter`

Filtering data: `having_eq`, `having_ne`, `having_lt`, `having_gt`, `having_lte`, `having_gte`, `having_like`, `having_not_like`, `having_in`, `having_not_in`, `having_between`, `having_is_null`, `having_is_not_null`, `having_raw`

Context change: `having_and`, `having_or`, `having_not`

`having` works depending on context, for data as `having_eq`, for closures as `having_and`

```
$data = $storage
    ->factory('users')
    ->columns(['role_id', 'count' => 'COUNT(*)'])
    ->group_by('role_id')
    ->having_or(function($having) {
        $having->gt('count', 2)->lte('count', 5);
    })
    ->get();
# query: SELECT `role_id`, COUNT(*) AS `count` FROM `users` GROUP BY `role_id` HAVING (`count` > ? OR `count` factory('users')
    ->order_by('name')
    ->order_by_desc('email')
    ->get();
# query: SELECT * FROM `users` ORDER BY `name` ASC, `email` DESC
```

#### factory-&gt;limit(count\[, offset\]) : factory

[](#factory-limitcount-offset--factory)

Limits the number of records returned

```
$data = $storage
    ->factory('users')
    ->limit(10)
    ->get();
# query: SELECT * FROM `users` LIMIT ?
# bind:  10
```

#### factory-&gt;add(data\[, update\]) : row\_count

[](#factory-adddata-update--row_count)

Inserting data

```
$users   = $storage->factory('users');

$users->add([
    'login' => 'username',
    'email' => 'abc@mail',
]);
# query: INSERT INTO `users` SET `login` = ?, `email` = ?
# bind:  'username', 'abc@mail'

$archive = $storage->factory('archive_users');

$users->add($archive);
# query: INSERT INTO `users` SELECT * FROM `archive_users`

$users->columns(['login', 'email'])->add(
    $archive->columns(['username', 'email'])->filter_lt('id', 123)
);
# query: INSERT INTO `users` (`login`, `email`)
#        SELECT `username`, `email` FROM `archive_users` WHERE `id` < ?
# bind:  123

$storage
    ->factory('log')
    ->add(['name' => 'access', 'count' => 1], ['count = count + 1']);
# query: INSERT INTO `log` SET `name` = ?, `count` = ?
#        ON DUPLICATE KEY UPDATE count = count + 1
# bind:  'access', 1
```

#### factory-&gt;set(data) : row\_count

[](#factory-setdata--row_count)

Update data

```
$data = $storage
    ->factory('users')
    ->filter('id', 123)
    ->set(['name' => 'bar']);
# query: UPDATE `users` SET `name` = ? WHERE `id` = ?
# bind:  'bar', 123
```

#### factory-&gt;del() : row\_count

[](#factory-del--row_count)

Deleting data

```
$data = $storage
    ->factory('users')
    ->filter('id', 123)
    ->del();
# query: DELETE FROM `users` WHERE `id` = ?
# bind:  123
```

#### factory-&gt;count(\[key\]) : count

[](#factory-countkey--count)

Returns the number of expressions

```
$data = $storage
    ->factory('users')
    ->filter_gt('id', 123)
    ->count('email');
# query: SELECT COUNT(`email`) AS `val` FROM `users` WHERE `id` > ?
# bind:  123
```

#### factory-&gt;min(key) : value

[](#factory-minkey--value)

Returns the minimum value

```
$data = $storage
    ->factory('users')
    ->filter_like('email', '%@mail%')
    ->min('id');
# query: SELECT MIN(`id`) AS `val` FROM `users` WHERE `email` LIKE ?
# bind:  '%@mail%'
```

#### factory-&gt;max(key) : value

[](#factory-maxkey--value)

Returns the maximum value

```
$data = $storage
    ->factory('users')
    ->filter_in('role_id', [2, 3])
    ->max('id');
# query: SELECT MAX(`id`) AS `val` FROM `users` WHERE `role_id` IN (?, ?)
# bind:  2, 3
```

#### factory-&gt;avg(key) : value

[](#factory-avgkey--value)

Returns the average value

```
$data = $storage
    ->factory('users')
    ->avg('id');
# query: SELECT AVG(`id`) AS `val` FROM `users`
```

#### factory-&gt;sum(key) : value

[](#factory-sumkey--value)

Returns the sum of values

```
$data = $storage
    ->factory('users')
    ->sum('id');
# query: SELECT SUM(`id`) AS `val` FROM `users`
```

Model\\SQL
----------

[](#modelsql)

```
use Lava\Model\SQL;

class User extends SQL {

    // meta data
    protected static

        // optionally, storage name
        // name from Lava\App::conf()->storage()
        // default is "0"
        $storage = '0',

        // optionally, table name
        // defaults to a class name in snake case with an "s" at the end
        $table   = 'users',

        // optionally, the name of the primary key
        // defaults to "id"
        $id      = 'id',

        // columns description
        $columns = [
            'key' => [
                // value cannot be NULL
                'not_null' => FALSE|TRUE,
                // value is unique
                'unique'   => FALSE|TRUE,
                // default value
                'default'  => VALUE,
                // validation tests
                'valid'    => [tests],
            ],
            ...
        ],

        // optionally, default limit
        // if not specified at selection
        // unrestricted by default
        $limit   = 0;
}
```

### Creation

[](#creation)

```
$user = new User;

$user->name  = 'foo';
$user->email = 'mail@example.com';

$user->save();
```

### Selecting

[](#selecting)

```
// selecting one object by ID
$user  = User::one(123);

echo $user->name;

// selecting a collection by condition
$users = User::find(['active' => TRUE])->get();

foreach ($users as $user) {
    echo $user->name;
}
```

### Default values

[](#default-values)

```
class Session extends SQL {

    // method name must start with the prefix "column_default_"
    public static function column_default_created () {
        return date('Y-m-d H:i:s');
    }
}

$session = new Session;

echo $session->created; // now
```

### Relationship

[](#relationship)

```
class User extends SQL {

    public function sessions () {
        return $this
            ->has_many(Session::classname());
    }

    // additional filters
    public function active_sessions () {
        return $this
            ->sessions()
            ->filter(['active' => TRUE]);
    }
}

class Session extends SQL {

    public function user () {
        return $this->belongs_to(User::classname());
    }
}

$user   = User::one(123);

// property returns a collection
foreach ($user->sessions as $session) {
    echo $session->id;
}

// method returns resultset
$active = $user ->sessions()
                ->filter(['active' => TRUE])
                ->get();

// similarly
$active = $user ->active_sessions;
```

### Import &amp; Export

[](#import--export)

```
class Session extends SQL {

    public static function import ($data) {

        if (isset($data['ip'])) {
            $data['ip'] = long2ip($data['ip']);
        }

        return $data;
    }

    public static function export ($data) {

        if (isset($data['ip'])) {
            $data['ip'] = ip2long($data['ip']);
        }

        return $data;
    }
}
```

###  Health Score

28

—

LowBetter than 54% of packages

Maintenance34

Infrequent updates — may be unmaintained

Popularity11

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~79 days

Recently: every ~62 days

Total

12

Last Release

625d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f2f72635680892ae495b2b256b77c81befa55489c2adfebdc76f8106a05e3e34?d=identicon)[illogical-ru](/maintainers/illogical-ru)

---

Top Contributors

[![illogical-ru](https://avatars.githubusercontent.com/u/14827295?v=4)](https://github.com/illogical-ru "illogical-ru (16 commits)")

---

Tags

frameworkmicro-frameworkphpframeworkmicro

### Embed Badge

![Health badge](/badges/illogical-lava/health.svg)

```
[![Health](https://phpackages.com/badges/illogical-lava/health.svg)](https://phpackages.com/packages/illogical-lava)
```

###  Alternatives

[slim/slim

Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs

12.2k49.9M1.3k](/packages/slim-slim)[phprest/phprest

PHP Rest Framework.

3049.3k](/packages/phprest-phprest)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
