PHPackages                             simcript/pano - 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. simcript/pano

ActiveProject[Framework](/categories/framework)

simcript/pano
=============

Pano application skeleton

v1.4.5(6d ago)03MITPHPPHP &gt;=8.2

Since Feb 21Pushed 2w agoCompare

[ Source](https://github.com/simcript/pano)[ Packagist](https://packagist.org/packages/simcript/pano)[ RSS](/packages/simcript-pano/feed)WikiDiscussions main Synced today

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

Pano Application Skeleton
=========================

[](#pano-application-skeleton)

A minimal, ready-to-run application skeleton built on top of the **[Pano](https://github.com/simcript/pano-framework)** nano-framework.

Pano is a lightweight PHP runtime that gives you an explicit, predictable foundation with **full architectural control**. This skeleton wires up that runtime with a sensible project layout, a working `Default` module, configuration, and a web + CLI entry point so you can start building your own domains immediately.

> Built for **Pano Framework `^1.4`** (currently `v1.4.4`).

---

Requirements
------------

[](#requirements)

- **PHP &gt;= 8.2**
- [Composer](https://getcomposer.org/)

---

Installation
------------

[](#installation)

Create a new project with Composer:

```
composer create-project simcript/pano my-app
cd my-app
```

Or clone this repository and install dependencies manually:

```
git clone https://github.com/simcript/pano.git my-app
cd my-app
composer install
```

The `.env` file is created automatically from `.env.example` after install. If it isn't, copy it yourself:

```
cp .env.example .env
```

---

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

[](#quick-start)

Start the built-in PHP development server:

```
php -S localhost:8000 -t public
```

Open `http://localhost:8000` in your browser — you should see the **"It works!"**welcome page. The skeleton is alive.

---

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

[](#project-structure)

```
my-app/
├── pano                      # CLI entry point (executable)
├── public/
│   ├── index.php             # Web front controller
│   └── .htaccess             # Apache rewrite rules
├── config/
│   ├── app.php               # Application configuration
│   └── modules.php           # Module ↔ domain/resolver mapping
├── modules/                  # Your application lives here
│   └── Default/
│       ├── DefaultModule.php
│       ├── Handlers/
│       ├── Interceptors/
│       ├── Commands/
│       └── Views/
├── tests/                    # PHPUnit test suite
├── .env                      # Environment variables (not committed)
├── .env.example
└── composer.json

```

Two constants are defined at the very start of every entry point and drive the whole runtime:

ConstantMeaning`PANO_STARTED`Request start timestamp (microtime), for timing`BASE_PATH`Absolute path to the project root, with trailing `/`Everything — config loading, `.env` resolution, module paths — is relative to `BASE_PATH`, so keep that in mind if you move entry points.

---

Configuration
-------------

[](#configuration)

### `.env`

[](#env)

```
APP_NAME=Pano
APP_ENV=local          # "local" enables the dev/pano module
APP_KEY=base64:key     # Application key
APP_DEBUG=true         # Show detailed errors (disable in production)
APP_URL=https://neda.tst
MODULE_RESOLVER=path   # "path" or "subdomain"
```

> **Tip:** Pano parses `.env` values. `true`/`false`/`null` become proper booleans/null, and numeric strings become numbers.

### `config/app.php`

[](#configappphp)

Reads environment values into a config array. Access any value anywhere using the `config()` helper:

```
config('app.name');        // "Pano"
config('app.debug', false); // true, with a fallback
```

### `config/modules.php`

[](#configmodulesphp)

Maps a **module key** to a module class. This is how Pano decides which module handles the current request:

```
return [
    'pano' => env('APP_ENV', 'production') === 'local' ? DefaultModule::class : null,
    ''     => \Modules\Default\DefaultModule::class,
];
```

- The empty key `''` is the **default** module.
- `'pano'` is only active in the `local` environment.

---

The Module Resolver
-------------------

[](#the-module-resolver)

Pano routes an incoming request to a module **before** it routes to a handler. How the module is chosen depends on `MODULE_RESOLVER`:

### `path` (default)

[](#path-default)

The **first URL segment** is the module key. The remainder is the route path.

URLModule keyRoute`/blog/posts/12``blog``/posts/12``/``''``/`### `subdomain`

[](#subdomain)

The **subdomain** is the module key. The root domain is derived from `APP_URL`, so only the leading sub-part of that host is treated as a module:

HostModule key`blog.neda.tst` (APP\_URL=`https://neda.tst`)`blog``api.v2.neda.tst``api.v2``neda.tst` (the root itself)`''`If the resolver is neither `path` nor `subdomain`, an `Exception` is thrown.

> If no module matches the resolved key, Pano throws `"No module found for ''"`. Make sure every reachable key is registered in `config/modules.php`.

---

Core Concepts
-------------

[](#core-concepts)

Pano is intentionally small. Five concepts carry the whole runtime:

ConceptResponsibility**Module**A self-contained domain; defines routes, views, logging**Handler**A controller-like class that produces a `Response`**Interceptor**Runs before handlers (`onRequest`) and after (`onResponse`)**Command**A CLI action, like a console controller**View**Renders templates with layouts and sectionsThe base contracts live in the `Pano\Kernel` namespace; ready-to-use concrete implementations live in `Pano\Foundation`.

ConceptBase (abstract) contractConcrete implementationModule`Pano\Kernel\BaseModule`*(you extend it)*Router`Pano\Kernel\BaseRouter``Pano\Foundation\Router`Request`Pano\Kernel\BaseRequest``Pano\Foundation\Request`Response`Pano\Kernel\BaseResponse``Pano\Foundation\Response`View`Pano\Kernel\BaseView``Pano\Foundation\View`Handler`Pano\Kernel\BaseHandler`*(you extend it)*Interceptor`Pano\Kernel\BaseInterceptor`*(you extend it)*Command`Pano\Kernel\BaseCommand`*(you extend it)*Logger`Pano\Kernel\BaseLogger``Pano\Foundation\Logger`Exception`Pano\Kernel\BaseException``Pano\Foundation\Exception`> **Important:** Always extend the `Pano\Kernel\Base*` contracts (and use the `Pano\Foundation\*` implementations). The older `Pano\Core` / `Pano\Enum`namespaces no longer exist.

---

Building a Module
-----------------

[](#building-a-module)

A module is a `readonly` class extending `BaseModule`. It must define three methods: `routes()`, `view()`, and `log()`.

```
