PHPackages                             savadub/savv - 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. savadub/savv

ActiveLibrary[Framework](/categories/framework)

savadub/savv
============

The Savv PHP Framework - Lightweight, fast, and WordPress-friendly.

v2.6.0(1mo ago)03MITPHPPHP &gt;=8.0

Since Apr 16Pushed 1mo agoCompare

[ Source](https://github.com/igefadele/savv_framework)[ Packagist](https://packagist.org/packages/savadub/savv)[ RSS](/packages/savadub-savv/feed)WikiDiscussions main Synced 2w ago

READMEChangelogDependencies (4)Versions (22)Used By (0)

Savv Web Framework
==================

[](#savv-web-framework)

> Savv is a zero-config, zero-build PHP engine engineered for building high-performance brand websites, studio portfolios, and public-facing web experiences. It is designed for developers, businesses, and individuals who demand the speed of a static site and the resilience of a lean PHP core, without the "build-tool tax."

> > The whole framework package folder is less than 1Mb. It's only 526Kb (~0.5Mb) as at v2.1.0

[![Packagist](https://camo.githubusercontent.com/57620a0ac64340dd1936c85788093aa87ee5c2d779388848747ba5d9809f858d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f736176616475622f73617676)](https://packagist.org/packages/savadub/savv)[![PHP Version](https://camo.githubusercontent.com/4d9004b6d3dd22b2c7d4a6c5e03ecf9df5409f596f55d63349ca1079bd8d8f97/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d253345253344382e302d383839324246)](https://php.net)[![License: MIT](https://camo.githubusercontent.com/784362b26e4b3546254f1893e778ba64616e362bd6ac791991d2c9e880a3a64e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667)](LICENSE)

---

Why Savv Web?
-------------

[](#why-savv-web)

Modern PHP frameworks are powerful — but they carry enormous overhead when all you need is a clean, fast, presentation-first website. Savv Web was built to fill that gap without compromise.

### How Savv Web Outpaces the Field

[](#how-savv-web-outpaces-the-field)

FeatureSavv WebTypical Full-Stack FrameworkStatic Site GeneratorFile-based routing — zero config✅❌✅No build tool required✅❌❌PWA built-in — no setup needed✅❌❌URL redirections — no settings required✅❌❌SSG-feel speed without build steps✅❌✅Deploys on any server, any compute tier✅⚠️✅Edit live in production — no rebuild wait✅❌❌**File-based routing with zero configuration.** Drop a PHP file into `views/pages/` and it resolves as a URL automatically. No route registration, no controllers, no config file to touch. The router discovers it at runtime via dynamic view resolution.

**No build tool whatsoever.** No Node.js. No npm. No webpack, Vite, or any compiler pipeline. Install Composer, run `composer install`, point your server at `public/`, and you are live.

**PWA baked in — nothing to configure.** The framework self-registers `GET /manifest.json` and `GET /sw.js` routes via `SystemController` and `src/Helpers/routes.php` — wired automatically on every boot. The service worker and manifest are generated dynamically from a single `configs/pwa.php` file. The `savv_head()` and `savv_scripts()` helpers inject all PWA meta tags, service worker registration, SPA transitions, and AOS animations in two function calls. Nothing else is required from you.

**Built-in URL redirections — no plugin, no settings page.** Define a key-value array in `configs/redirections.php`. The framework reads it at bootstrap time and registers redirect routes automatically. `yourdomain.com/fb` redirects to Facebook with one line of config.

**SSG-feel speed without build steps.** The `php savv route:cache` CLI command compiles all routes, views, redirections, and posts into a single serialized PHP array at `storage/framework/routes.php`. On every subsequent request, the router loads this manifest directly — no filesystem scanning, no dynamic discovery overhead. Static-site-level dispatch performance with a fully dynamic, editable codebase underneath.

**Deploys on any server, any compute tier.** Plain PHP runs equally well on shared hosting, budget VPS, bare metal, and enterprise cloud. No Node.js runtime. No special server modules. No memory-hungry application containers. If the server runs PHP 8, it runs Savv Web.

**Edit files live, in production, instantly.** There is no build process between your files and your live site. Edit a view, a config, or a page and reload. No waiting. No pipeline required for content changes — critical when something needs to be fixed in seconds.

---

What Savv Web Is Best For
-------------------------

[](#what-savv-web-is-best-for)

- Brand and corporate websites
- Agency and studio portfolios
- Marketing and campaign sites
- Product landing pages
- Company profile and brochure sites
- Public-facing platforms with a few dynamic endpoints

If your project needs deep ORM workflows, an admin panel ecosystem, or a large plugin system, a full-stack framework will serve you better. Savv Web is optimized for **clarity, speed, and minimal overhead** on content-driven sites.

---

Package Model
-------------

[](#package-model)

Savv Web is split into two repositories to keep responsibilities clean.

### Savv Web Framework ← *you are here*

[](#savv-web-framework--you-are-here)

The installable core package. Namespace: `Savv\`. All source lives in `src/`.

Contains the Application bootstrapper, Router (singleton), Request, Response, Config, Validator, Log, SystemController (PWA engine), Console Kernel, CLI commands, and all framework helpers.

### Savv Web Starter

[](#savv-web-starter)

The **[Savv Web Starter](https://github.com/igefadele/savv_starter)** is the ready-to-use project skeleton that already depends on this package. It is the **recommended starting point** for every new project.

---

Quick Start
-----------

[](#quick-start)

```
git clone https://github.com/igefadele/savv_starter my-project
cd my-project
composer install
```

Point your server document root to `public/` and start building immediately.

---

Installing the Framework Directly
---------------------------------

[](#installing-the-framework-directly)

### Via Packagist

[](#via-packagist)

```
composer require savadub/savv
```

### Via GitHub (VCS)

[](#via-github-vcs)

```
{
  "repositories": [
    {
      "type": "vcs",
      "url": "https://github.com/igefadele/savv_framework"
    }
  ],
  "require": {
    "savadub/savv": "dev-main"
  }
}
```

```
composer update
```

---

Project Structure
-----------------

[](#project-structure)

```
my-savv-app/
├── app/
│   ├── Controllers/
│   └── Middleware/
│
├── configs/
│   ├── mail.php
│   ├── middlewares.php
│   ├── posts.php
│   ├── pwa.php
│   ├── redirections.php
│   └── installations.php    # External CMS/website integrations
│
├── public/                  # ← Web server document root
│   └── index.php            # 3-line entry point
│
├── routes/
│   ├── web.php
│   └── api.php
│
├── storage/
│   ├── framework/
│   │   └── routes.php       # Route cache (generated by CLI)
│   └── logs/
│       └── 2026-04-17.log
│
├── views/
│   ├── layouts/
│   │   └── index.php        # Master layout wrapper
│   ├── pages/               # File-based routing root
│   │   ├── index.php        # → /
│   │   ├── about.php        # → /about
│   │   ├── offline.php      # → /offline (PWA fallback page)
│   │   ├── posts.php        # → /posts (blog listing page)
│   │   └── post-detail.php  # → /post-detail (blog post detail page)
│   ├── partials/
│   │   ├── head.php
│   │   ├── header.php
│   │   ├── footer.php
│   │   └── scripts.php
│   └── posts/               # Markdown post files
│
└── .env

```

---

Bootstrap Flow
--------------

[](#bootstrap-flow)

The entire application entry point is three lines:

```
// public/index.php
define('ROOT_PATH', dirname(__DIR__));
require ROOT_PATH . '/vendor/autoload.php';

$app = \Savv\Core\Application::bootstrap(dirname(__DIR__));
$app->run();
```

`Application::run()` executes this sequence on every request:

1. **Route cache check** — looks for `storage/framework/routes.php`
2. **Cache hit** → loads routes directly via `Router::loadRawRoutes()` (fast path — no file scanning)
3. **Cache miss** → calls `Router::loadRouteFiles()` which:
    - Loads internal framework routes from `src/Helpers/routes.php` (registers `/manifest.json` and `/sw.js`)
    - Loads all files in `routes/*.php` (`web.php`, `api.php`)
    - Calls `Router::registerRedirections()` to read `configs/redirections.php` and register redirect closures
4. **Request capture** — `Request::capture()` snapshots `$_GET`, `$_POST`, `$_SERVER`, `$_FILES`
5. **Dispatch** — `Router::dispatch()` matches against explicit registered routes
6. **Dynamic discovery** — if no explicit route matches a `GET` request, the router looks for a matching file in `views/pages/`
7. **CMS fallback** — if still unmatched, `handleExternalFallbacks()` checks `configs/installations.php` and hands off to any active CMS (e.g. WordPress)
8. **404** — `abort404()` renders `views/404.php` or outputs a plain 404 string

---

Routing
-------

[](#routing)

### 1. File-Based Routing — Zero Configuration

[](#1-file-based-routing--zero-configuration)

The most common way to add a page. Place any `.php` file in `views/pages/` and it is accessible as a URL with no other steps:

```
views/pages/index.php      → GET /
views/pages/about.php      → GET /about
views/pages/services.php   → GET /services
views/pages/blog/post.php  → GET /blog/post

```

Nothing to register. Nothing to configure. The router's `resolveDynamicView()` method handles discovery automatically.

The user is free to arrange the page files as they like, but they must ensure the main page file is inside the `views/pages/` directory. Page parts (partials, sections, components) can be anywhere in the `views/` directory or its subfolders. Just make sure to import the partials, sections, parts, and partials correctly into the said page file inside `views/pages/`.

### 2. Explicit Web Routes

[](#2-explicit-web-routes)

For routes that need custom logic before rendering, use `routes/web.php`:

```
router()->get('/', function () {
    require ROOT_PATH . '/views/pages/index.php';
});

router()->get('blog/{slug}', function ($slug) {
    // custom pre-render logic
    require ROOT_PATH . '/views/pages/blog.php';
})->name('blog.show');
```

When returning a page file from a custom route, the Router `view()` method can be used:

```
router()->get('about', function () {
    return router()->view('pages/about');
});
```

### 3. API Routes

[](#3-api-routes)

```
// routes/api.php
use App\Controllers\ContactController;

router()->group(['prefix' => 'api', 'name' => 'api.'], function ($router) {
    $router->post('contact-submit', [ContactController::class, 'submit'])
           ->name('submit.contact');
});
```

### Named Routes and Route Parameters

[](#named-routes-and-route-parameters)

```
// Define
router()->get('blog/{slug}', function ($slug) { ... })->name('blog.show');

// Generate URL
$url = route('blog.show', ['slug' => 'getting-started']);
// → /blog/getting-started
```

Returns `#` when the route name is not found.

### Route Caching

[](#route-caching)

Compile all routes into a performance-optimized static manifest for production:

```
php savv route:cache
```

Saves to `storage/framework/routes.php`. The router uses this on every boot — eliminating all dynamic file scanning. To clear the cache, delete the file. Regenerate it after adding new pages or routes.

---

Blogging
--------

[](#blogging)

Savv Web supports built-in blogging. Any `.md` file placed inside `views/posts/` becomes a blog post and is accessible at `domain.com/{slug}`, e.g., `domain.com/how-to-savv-website`.

### Blog Post Format

[](#blog-post-format)

Each Markdown file must start with frontmatter in the following format:

```
---
title: Savv Website
slug: savv-website
date: 2026-04-17
author: Ige Fadele
status: published  # can be draft, trashed, published
category: blogging
---

# Your Blog Post Content

Write your post content here in Markdown.

```

The frontmatter fields are:

- `title`: The post title
- `slug`: The URL slug (must be unique)
- `date`: Publication date in YYYY-MM-DD format
- `author`: Author name
- `status`: Publication status (`published`, `draft`, or `trashed`)
- `category`: Post category

Only posts with `status: published` are accessible publicly.

### Blog Pages

[](#blog-pages)

Use the provided page files in `views/pages/` for your blog:

- `posts.php`: The blog listing page (e.g., `/posts`)
- `post-detail.php`: The individual post detail page (e.g., `/how-to-savv-website`)

You can customize these pages to display posts as needed.

---

PWA — Built In, No Action Required
----------------------------------

[](#pwa--built-in-no-action-required)

The framework self-registers two routes via `src/Helpers/routes.php` on every boot:

```
GET /manifest.json  →  SystemController::getManifestFile()
GET /sw.js          →  SystemController::getServiceWorkerFile()

```

`SystemController` dynamically generates both responses from `configs/pwa.php`. The service worker implements install, activate, cache-first fetch, and offline fallback to `/offline`.

Configure your PWA entirely in one file:

```
// configs/pwa.php
return [
    'name'             => 'My Brand',
    'short_name'       => 'Brand',
    'description'      => 'What my site does.',
    'version'          => 'v1',      // Bump this to bust the service worker cache
    'theme_color'      => '#081065',
    'background_color' => '#ffffff',
    'display'          => 'standalone',
    'icons'            => [
        ['src' => '/assets/images/icons/icon-192x192.png', 'sizes' => '192x192', 'type' => 'image/png', 'purpose' => 'any'],
        ['src' => '/assets/images/icons/icon-512x512.png', 'sizes' => '512x512', 'type' => 'image/png', 'purpose' => 'any'],
    ],
    'precache' => [
        '/',
        '/offline',
        '/assets/css/main.css',
        '/assets/js/main.js',
    ],
];
```

That is the only step. No additional code needed.

---

Layout Helpers
--------------

[](#layout-helpers)

`src/Helpers/layouts.php` provides two helper functions that inject the full frontend stack in a single call each.

### `savv_head()`

[](#savv_head)

Call inside your ``. Injects:

- PWA manifest link (`/manifest.json`)
- `theme-color` and mobile web app meta tags
- Apple touch icon meta tag
- Bootstrap 5 CSS (CDN)
- Bootstrap Icons CSS (CDN)
- AOS (Animate On Scroll) CSS (CDN)

```

```

### `savv_scripts()`

[](#savv_scripts)

Call before ``. Injects:

- Bootstrap 5 JS bundle (CDN)
- AOS JS — auto-initialized (700ms, ease-out-cubic, once, offset 60)
- Swup with HeadPlugin and ScrollPlugin — SPA-feel page transitions
- PWA service worker registration
- Re-runs AOS and counters on every Swup page swap
- Calls `window.initPageScripts()` on each page swap if defined

```

```

After this, your site has SPA-feel navigation, and a fully registered PWA — no extra JavaScript written.

---

Views and Layouts
-----------------

[](#views-and-layouts)

Pages follow a simple capture-and-include pattern:

```
// views/pages/about.php

$pageTitle       = 'About — My Brand';
$pageDescription = 'Who we are and what we build.';

ob_start();
?>

        About Us
        Our story here.
