Build New PHP Apps
With Clear Boundaries
Start a new project with routes defined on controllers, reusable aspects for authentication and other shared concerns, contextual dependency injection, and expressive ObjectQuel queries. Canvas also lets existing PHP apps adopt the same architecture one route at a time.
A Clear Foundation for New PHP Projects
Keep routes, shared concerns, services, and data access easy to find as your application grows.
Controller-Based Routing
Define routes beside the controller methods that handle them. Typed parameters and HTTP method constraints make each endpoint's contract visible.
Aspect-Oriented Programming
Declare reusable authentication, audit logging, or caching aspects on controllers and methods. Before, around, and after hooks keep shared behavior out of each action.
ObjectQuel ORM
Use a Data Mapper with QUEL-inspired queries, relationship traversal via via, and entities that stay separate from persistence logic.
Contextual Services
Code against interfaces and let Canvas resolve implementations for the current context. Keep application services focused and reusable.
Secure by Default
Built-in CSRF protection, security headers, input hardening. Stop manually adding security checks to every endpoint.
Powerful Inspector
Inspect requests and database queries while you build. Custom panels help you see what your application is doing.
Start with the architecture you want to keep
A new Canvas application begins with the project skeleton. Add a controller, put its route on the method, and attach shared behavior where it applies. You can introduce ObjectQuel when your application needs persistence. This makes Canvas a practical choice for teams that want explicit controller boundaries and reusable cross-cutting behavior from the first feature.
Build your first app/**
* @InterceptWith(RequireAuthAspect::class)
*/
class ProjectController extends BaseController {
/**
* @Route("/projects/{id:int}")
*/
public function show(int $id): Response {
return new Response("Project $id");
}
}
Already Have a PHP App?
Canvas routes and existing files can run side by side while you move endpoints at your own pace.
Legacy Fallthrough - Use Canvas Features in Existing Files
Step 1: Legacy file keeps working
// legacy/products.php - still works unchanged
session_start();
if (!isset($_SESSION['user']) ||
$_SESSION['role'] !== 'admin') {
header('Location: /login');
exit;
}
$conn = mysqli_connect(/*...*/);
$id = (int)$_GET['id'];
$result = mysqli_query($conn,
"SELECT * FROM products WHERE id = $id");
$product = mysqli_fetch_assoc($result);
log_admin_action($_SESSION['user'],
'view_product', $id);
include 'templates/product.php';
Step 2: Bridge to Canvas services (no DI required)
// legacy/products.php - modernize piece by piece
session_start();
if (!isset($_SESSION['user']) ||
$_SESSION['role'] !== 'admin') {
header('Location: /login');
exit;
}
// Start using ObjectQuel in legacy file
$em = canvas('EntityManager');
$product = $em->find(
Product::class,
(int)$_GET['id']
);
log_admin_action($_SESSION['user'],
'view_product', $product->id);
include 'templates/product.php';
Step 3: Move to controller (one URL at a time, when ready)
/**
* @InterceptWith(RequireAuthAspect::class)
* @InterceptWith(RequireAdminAspect::class)
* @InterceptWith(AuditLogAspect::class)
*/
class ProductController extends BaseController {
/**
* @Route("/products/{id:int}")
*/
public function show(int $id) {
$product = $this->em()->find(Product::class, $id);
return $this->render('product.tpl', $product);
}
}
// Canvas handles this route now - legacy file no longer called
Why Canvas Exists
After 25 years building and modernizing PHP applications, I wanted a framework with clear routes, reusable services, and shared behavior that stays out of business logic. Canvas gives new projects that foundation from day one. Its legacy bridge also lets existing applications adopt the same architecture gradually, with deterministic routing and service access from existing files.
— Floris, Quellabs
No Stack Changes Required
Canvas doesn't force you to abandon your current tools and setup
Your Template Engine
Canvas ships with Smarty support, but you can use Twig, Blade, Handlebars, Latte or Plates.
Your Database Structure
No schema changes required. ObjectQuel maps to your existing tables as-is.
Your Deployment Process
Same servers, same deployment pipeline. Just add Canvas via Composer.
Get Started in Minutes
Install Canvas via Composer and start building immediately
New Project
# Create a new Canvas project with skeleton
composer create-project quellabs/canvas-skeleton my-app
Existing Project
# Add Canvas to existing codebase
composer require quellabs/canvas
The Quellabs Ecosystem
A family of open-source tools built around the same philosophy.
Two Execution Paths, One System
Start modernizing your legacy PHP codebase today.