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.
Keep routes, shared concerns, services, and data access easy to find as your application grows.
Define routes beside the controller methods that handle them. Typed parameters and HTTP method constraints make each endpoint's contract visible.
Declare reusable authentication, audit logging, or caching aspects on controllers and methods. Before, around, and after hooks keep shared behavior out of each action.
Use a Data Mapper with QUEL-inspired queries, relationship traversal via via, and entities that stay separate from persistence logic.
Code against interfaces and let Canvas resolve implementations for the current context. Keep application services focused and reusable.
Built-in CSRF protection, security headers, input hardening. Stop manually adding security checks to every endpoint.
Inspect requests and database queries while you build. Custom panels help you see what your application is doing.
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");
}
}
Canvas routes and existing files can run side by side while you move endpoints at your own pace.
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
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
Canvas doesn't force you to abandon your current tools and setup
Canvas ships with Smarty support, but you can use Twig, Blade, Handlebars, Latte or Plates.
No schema changes required. ObjectQuel maps to your existing tables as-is.
Same servers, same deployment pipeline. Just add Canvas via Composer.
Install Canvas via Composer and start building immediately
# Create a new Canvas project with skeleton
composer create-project quellabs/canvas-skeleton my-app
# Add Canvas to existing codebase
composer require quellabs/canvas
A family of open-source tools built around the same philosophy.
Start modernizing your legacy PHP codebase today.