Lay - A Lite PHP Meta-Framework for Rapid Development
Lay is a modern, lightweight PHP meta-framework designed to get your projects up and running quickly. Built with developer experience in mind, it provides a clean architecture with powerful features while maintaining simplicity.
- π Rapid Development - Get started in minutes with sensible defaults
- ποΈ Domain-Driven Architecture - Organize your application by domains/modules
- π Built-in API Support - RESTful APIs with automatic routing and rate limiting
- π± Modern Frontend Integration - Built-in JavaScript utilities and asset management
- ποΈ Database Agnostic - Support for MySQL, PostgreSQL, and SQLite
- π Security First - CORS, CSRF protection, and secure session management
- β‘ Performance Optimized - Asset compression, caching, and production optimizations
- π¨ Flexible View System - Clean templating with PHP or integrate your favorite frontend
Create a new Lay project using Composer:
composer create-project pillardash/lay my-awesome-project
cd my-awesome-project- Copy the environment file and configure your settings:
cp .env.example .env- Edit
.envto match your environment:
# Set your environment
APP_ENV=DEVELOPMENT
# Database configuration (optional)
DB_DRIVER=mysql
DB_HOST=127.0.0.1
DB_USERNAME=root
DB_PASSWORD=your_password
DB_NAME=your_database# Using PHP built-in server
php -S localhost:8000
# Or using your preferred local server (Apache, Nginx, etc.)Visit http://localhost:8000 and you'll see your Lay application running! π
my-project/
βββ bricks/ # Business logic layer
β βββ Business/
β βββ Api/ # API hooks and logic
β βββ Controller/ # Application controllers
β βββ Model/ # Data models
β βββ Request/ # Request validation classes
β βββ Resource/ # API resource transformers
βββ utils/ # Utility classes and helpers
βββ web/ # Web layer
β βββ domains/ # Domain-specific code
β β βββ Api/ # API domain
β β β βββ Plaster.php # API configuration and hooks
β β β βββ index.php # API entry point
β β βββ Default/ # Default web domain (template for all regular domains)
β β βββ Plaster.php # Domain routes and page configuration
β β βββ foundation.php # Domain-specific setup
β β βββ layout/ # HTML layout components
β β β βββ head.inc # <head> section (meta, CSS, etc.)
β β β βββ body.inc # Main body wrapper
β β β βββ script.inc # JavaScript includes
β β βββ plaster/ # View templates
β β β βββ homepage.view # Homepage template
β β β βββ another.view # Example page template
β β βββ public/ # Domain-specific public assets
β β βββ index.php # Domain entry point
β β βββ favicon.ico # Domain favicon
β β βββ static/ # Static assets (CSS, JS, images)
β β βββ dev/ # Development assets
β β βββ css/ # Stylesheets
β β βββ js/ # JavaScript files
β β βββ images/ # Images
β β βββ ui/ # UI components
β βββ shared/ # Shared assets and resources
βββ foundation.php # Application bootstrap
βββ index.php # Application entry point
βββ bob.config.json # Build and deployment configuration
Lay organizes applications into domains - self-contained modules that handle specific parts of your application:
Domain::new()->create(
id: "api-endpoint",
builder: \Web\Api\Plaster::class,
pattern: "api",
type: DomainType::SPECIAL,
);
Domain::new()->create(
id: "default",
builder: \Web\Default\Plaster::class,
pattern: "*",
type: DomainType::REGULAR,
);Each regular domain follows a standard structure (using Default as an example):
web/domains/Default/
βββ Plaster.php # Main domain controller - defines routes and pages
βββ foundation.php # Domain-specific configuration and setup
βββ layout/ # HTML layout components
β βββ head.inc # <head> section - meta tags, CSS includes
β βββ body.inc # Main body wrapper and footer
β βββ script.inc # JavaScript includes and initialization
βββ plaster/ # View templates (your actual page content)
β βββ homepage.view # Homepage template
β βββ another.view # Additional page templates
βββ public/ # Domain-specific public assets
βββ index.php # Domain entry point for direct access
βββ favicon.ico # Domain-specific favicon
βββ static/ # Static assets organized by environment
βββ dev/ # Development assets
βββ css/ # Domain stylesheets
βββ js/ # Domain JavaScript
βββ images/ # Domain images
βββ ui/ # UI components and assets
Key Files Explained:
Plaster.php- Your domain's main controller where you define routes and configure pageslayout/*.inc- Reusable HTML components that wrap your contentplaster/*.view- Your actual page templates with contentpublic/static/- All your CSS, JavaScript, images, and other assets
Controllers handle your application logic using traits for common functionality:
class NewsletterSubController
{
use IsSingleton, ControllerHelper;
public function add(): array
{
$request = new SubNewsletterRequest();
if ($request->error)
return self::res_warning($request->error);
// Your logic here...
return self::res_success("Success message");
}
}Models extend BaseModelHelper for database operations:
class NewsletterSub extends BaseModelHelper
{
public static string $table = "newsletter_subs";
protected function cast_schema(): void
{
$this->cast("options", "array");
}
}Define your API endpoints using the hook system:
class Hook extends ApiHooks
{
protected function hooks(): void
{
$this
->post("subscribe-newsletter")->bind(fn() => NewsletterSubController::new()->add())
->get("list-subscribers/{page}")->bind(fn($page) => NewsletterSubController::new()->list((int) $page));
}
}Create request classes for automatic validation:
class SubNewsletterRequest extends RequestHelper
{
protected function rules(): void
{
$this->vcm(['field' => 'name', 'required' => false ]);
$this->vcm(['field' => 'email', 'is_email' => true ]);
}
}Lay includes a minimal but powerful JavaScript library for frontend interactions:
// Built-in DOM utilities
$id('my-element').innerHTML = 'Hello World';
$sel('.my-class').$class('add', 'active');
// AJAX made simple
$ajax($lay.src.api + 'subscribe-newsletter', {
method: 'POST',
data: { email: '[email protected]' },
}).then(response => {
console.log('Success!', response);
osNote("Success", "success")
});Lay can startup without connecting to the database by default, but if you want that, you can use the ->connect_db() method in your foundation.php:
Startup::new()
->name($site_name, "$site_name | Slogan Goes Here")
->connect_db()
->set_globals([
"desc" => "Your project description"
]);Lay includes production optimizations:
- Asset Compression - Automatic JS/CSS minification
- HTML Compression - Reduced payload sizes
- Caching - Domain and asset caching
- Security Headers - Production-ready security configuration
Configure in your .env:
APP_ENV=PRODUCTION
COMPRESS_HTML=true
COMPRESS_ASSETS=true
CACHE_DOMAINS=true- Official Documentation: https://lay.pillardash.com (Coming Soon)
- Example Applications: Check the
web/domains/Defaultfolder for examples - Community: Join our discussions and get help from the community
We welcome contributions! Please feel free to submit issues, feature requests, or pull requests.
Lay is open-source software licensed under the MIT License.
Ready to build something amazing? π
composer create-project pillardash/lay my-next-project