PHPackages                             quellabs/canvas - 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. quellabs/canvas

ActiveLibrary[Framework](/categories/framework)

quellabs/canvas
===============

A modern, lightweight PHP framework with contextual containers, automatic service discovery, and ObjectQuel ORM integration

1.0.91(1mo ago)142271MITPHPPHP &gt;=8.3

Since Jun 3Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/quellabs/canvas)[ Packagist](https://packagist.org/packages/quellabs/canvas)[ GitHub Sponsors](https://github.com/sponsors/quellabs)[ RSS](/packages/quellabs-canvas/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (33)Versions (86)Used By (1)

Canvas
======

[](#canvas)

[![Packagist](https://camo.githubusercontent.com/7511f5356745f11fee11cf68c62b89e0e9fc68ba57e30dca022426b3e75aff53/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7175656c6c6162732f63616e7661732e737667)](https://packagist.org/packages/quellabs/canvas)

A PHP framework built for real-world projects — especially the messy ones. Canvas drops into existing PHP codebases without forcing a rewrite, while giving new projects a clean, annotation-driven architecture with aspect-oriented programming and a readable ORM.

The legacy problem
------------------

[](#the-legacy-problem)

Most frameworks assume you're starting fresh. Canvas doesn't. Point it at your existing application, and it takes over routing while your old `.php` files keep working:

```
// config/app.php
return [
    'legacy_enabled' => true,
    'legacy_path'    => dirname(__FILE__) . "/../legacy"
];
```

Canvas checks its own routes first. Unmatched URLs fall through to your legacy files — every existing page keeps working from day one. Your legacy code can immediately use Canvas services:

```
// legacy/admin/dashboard.php — your existing file, now with Canvas services
$users = canvas('EntityManager')->findBy(User::class, ['active' => true]);
```

Legacy files using `header()`, `die()`, and `exit()` are automatically preprocessed to work within Canvas's request/response flow. The preprocessing is recursive — included files are transformed too, and results are cached.

Migrate one route at a time. When a Canvas controller claims a URL, it takes precedence over the legacy file. When every route is migrated, set `legacy_enabled` to `false`.

Cross-cutting concerns without the mess
---------------------------------------

[](#cross-cutting-concerns-without-the-mess)

Here's what a controller looks like when authentication, caching, and rate limiting are tangled into business logic:

```
// The usual approach
public function manage() {
    if (!$this->auth->isAuthenticated()) {
        return redirect('/login');
    }

    $key = 'users_' . md5(serialize($params));
    if ($cached = $this->cache->get($key)) {
        return $cached;
    }

    if ($this->rateLimiter->tooManyAttempts($ip, 100)) {
        return response('Too many requests', 429);
    }

    $users = $this->em()->findBy(User::class, ['active' => true]);
    $result = $this->render('admin/users.tpl', compact('users'));
    $this->cache->set($key, $result, 300);
    return $result;
}
```

Canvas separates cross-cutting concerns into aspects — reusable classes applied via annotations:

```
// Canvas: business logic only, concerns declared as aspects
/**
 * @Route("/admin/users")
 * @InterceptWith(RequireAuthAspect::class, priority=100)
 * @InterceptWith(CacheAspect::class, ttl=300)
 * @InterceptWith(RateLimitAspect::class, limit=100, window=3600)
 */
public function manage() {
    $users = $this->em()->findBy(User::class, ['active' => true]);
    return $this->render('admin/users.tpl', compact('users'));
}
```

Three lines of business logic. Authentication, caching, and rate limiting are declared, not coded. Each aspect is a standalone class — `BeforeAspect` for auth checks, `AroundAspect` for caching, `AfterAspect` for logging:

```
class RequireAuthAspect implements BeforeAspect {
    public function __construct(private AuthService $auth) {}

    public function before(MethodContextInterface $context): ?Response {
        if (!$this->auth->isAuthenticated()) {
            return new RedirectResponse('/login');
        }
        return null; // proceed to method
    }
}
```

Aspects inherit through controller hierarchies — apply `@InterceptWith` on a base class and every child controller gets it automatically. Priority ordering controls execution sequence within each inheritance level.

ObjectQuel ORM
--------------

[](#objectquel-orm)

Canvas integrates with [ObjectQuel](https://objectquel.com) through the `quellabs/canvas-objectquel` package. Simple lookups use familiar `find` and `findBy` methods. Complex queries use ObjectQuel's declarative syntax:

```
$results = $this->em()->executeQuery("
    range of p is App\\Entity\\Post
    range of u is App\\Entity\\User via p.authorId
    retrieve (p, u.name) where p.title = /^Tech/i
    sort by p.publishedAt desc
    window 0 using window_size 20
");
```

Pattern matching, regex, full-text search, and relationship traversal are first-class query expressions — not raw SQL escapes. ObjectQuel can also join database entities with JSON files in a single query via `json_source()`, something no other PHP ORM supports.

How to Install
--------------

[](#how-to-install)

```
# New project
composer create-project quellabs/canvas-skeleton my-app

# Existing project
composer require quellabs/canvas
```

Quick start
-----------

[](#quick-start)

```
class BlogController extends BaseController {

    /**
     * @Route("/posts/{id:int}")
     */
    public function show(int $id) {
        $post = $this->em()->find(Post::class, $id);
        return $this->render('post.tpl', compact('post'));
    }
}
```

Controllers are discovered automatically through Composer metadata. Routes are defined with annotations. Typed route parameters (`{id:int}`) are validated before your method runs. No configuration files, no route registration.

Canvas uses [Smarty](https://www.smarty.net/) as its default template engine. Twig support is available through a separate package.

Features
--------

[](#features)

- **Legacy-first integration** — wrap existing PHP apps with route fallthrough, automatic preprocessing of `header()`/`die()`/`exit()`, and a `canvas()` helper for accessing services from legacy code
- **Aspect-oriented programming** — Before, Around, and After aspects with annotation parameters, priority ordering, and inheritance through controller hierarchies
- **ObjectQuel ORM** — declarative query language with pattern matching, hybrid JSON sources, and Data Mapper architecture
- **Contextual dependency injection** — resolve different interface implementations based on request context, without conditional logic in your code
- **Signal/slot event system** — Qt-style decoupled service communication with type checking and priority-based handlers
- **Task scheduling** — cron-style background jobs with timeouts and concurrent execution handling
- **Validation &amp; sanitization** — declarative rules applied as aspects, keeping controllers clean
- **Visual inspector** — debug bar with database queries, request analysis, and custom panels
- **CLI tooling** — route listing, route matching, task management, asset publishing, entity generation

Documentation
-------------

[](#documentation)

Full docs, guides, and API reference: **[canvasphp.com/docs](https://canvasphp.com/docs)**

Contributing
------------

[](#contributing)

Bug reports and feature requests via GitHub issues. PRs welcome — fork, branch, follow PSR-12, add tests.

Support
-------

[](#support)

If Canvas saves you time, consider [sponsoring development](https://github.com/sponsors/quellabs).

License
-------

[](#license)

MIT

###  Health Score

50

—

FairBetter than 96% of packages

Maintenance90

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity67

Established project with proven stability

 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 ~3 days

Total

85

Last Release

45d ago

PHP version history (2 changes)1.0.90PHP &gt;=8.4

1.0.91PHP &gt;=8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/57e4ab872b3e37536367f2d26b192df3d3bb6a6a1cebec9a104d14a6d2ffe157?d=identicon)[noescom](/maintainers/noescom)

---

Top Contributors

[![quellabs](https://avatars.githubusercontent.com/u/7067695?v=4)](https://github.com/quellabs "quellabs (14 commits)")

---

Tags

dependency-injectionframeworkormphpphp-frameworkframeworkdependency-injectiontemplatingcontextual-containers

### Embed Badge

![Health badge](/badges/quellabs-canvas/health.svg)

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

###  Alternatives

[laravel/framework

The Laravel Framework.

34.7k509.9M17.0k](/packages/laravel-framework)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[silverstripe/framework

The SilverStripe framework

7213.5M2.5k](/packages/silverstripe-framework)[contao/core-bundle

Contao Open Source CMS

1231.6M2.4k](/packages/contao-core-bundle)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

595.2M386](/packages/shopware-core)[doppar/framework

The Doppar Framework

366.7k8](/packages/doppar-framework)

PHPackages © 2026

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