Skip to content

Getting started with Basehim development

4 min read

Basehim is an open-source PHP CMS with a REST API and a built-in MCP server for AI agents. It runs on ordinary shared hosting: no Composer, no build step, no daemon. This page is the map for developers. It tells you which part of Basehim to work in, and where to read next.

Three ways to build on Basehim

You want to…Build a…Lives inStart with
Change how the public site looksThemecontent/themes/{slug}/Theme development
Add features, screens, data or integrationsAppcontent/apps/{slug}/App development
Use Basehim from another program or an AI agentAPI clientanywhereREST API guide, MCP and AI agents

The split is deliberate:

  • A theme is presentation only. It can't register routes or create database tables. It can register filters, for example to adjust SEO tags.
  • An app is PHP that runs on every request. It can do almost anything core can.
  • An API client never touches the PHP at all.

If a theme needs behaviour, ship a small app next to it.

Run it locally in five minutes

You need PHP 8.1 or newer with pdo_mysql, and a MySQL 5.7+ or MariaDB 10.3+ database.

git clone https://github.com/basehim/basehim.git
cd basehim
php -S localhost:8000 server.php

Open http://localhost:8000. You'll be sent to the installer, which asks for your database details and creates the admin account. If you don't have a database handy, one container is enough:

docker run -d --name basehim-db -p 3306:3306 \
  -e MARIADB_ROOT_PASSWORD=secret -e MARIADB_DATABASE=basehim mariadb:11
# installer: host 127.0.0.1, database basehim, user root, password secret

Note: server.php is the router for PHP's built-in server, for local work only. On a real host, Apache uses the bundled .htaccess and server.php answers 404. See Installation.

While you develop, set APP_DEBUG=true in .env. Errors then show the file and line instead of a generic page. Turn it off again on a live site.

Where things live

basehim/
├── index.php           front controller: every request starts here
├── bootstrap.php       autoloader, .env, session, global helpers, boots the app
├── install.php         web installer (locks itself when done)
├── server.php          router for `php -S` (local only)
├── .env                your settings and secrets (never commit it)
├── app/
│   ├── Core/           router, container, hooks, database, App base class
│   ├── Http/           controllers (Web, Admin, Api, Ajax) and middleware
│   ├── Services/       business logic: posts, media, users, SEO, scheduler…
│   └── Repositories/   data access
├── config/             app, database, auth, capabilities, ajax
├── routes/             ajax.php, api.php, admin.php, web.php
├── database/migrations/ numbered .sql files
├── admin/              the admin panel's views and assets
├── content/
│   ├── apps/           installed apps (one folder each)
│   └── themes/         installed themes (one folder each)
└── storage/            uploads, cache, sessions, logs (must be writable)

As a theme or app developer you only write inside content/. Everything else is core, and updates replace it.

How a request flows

  1. index.php works out the install's base path, so subfolder installs work. If .env is missing it redirects to the installer.
  2. bootstrap.php registers the autoloader, reads .env and starts the session. It then calls Application::boot(), which:
    • builds the service container and the hook registry;
    • boots every active app, calling each app's boot();
    • runs the active theme's boot.php.
  3. Routes are loaded in this order: routes/ajax.php, api.php, admin.php, web.php. Apps added their own routes during boot(), before any of these.
  4. The router dispatches to a controller. A public page ends in ThemeService::render(), which runs your theme's template with the page's data.
  5. The response is sent. Core then adds the SEO tags to the finished page. After that, the scheduler may run due background tasks while the visitor is already reading.

Two things follow from this:

  • boot() runs on every request, so keep it cheap.
  • Hooks registered in boot() are in place before any content renders.

The extension toolkit at a glance

  • Hooks. addAction() and addFilter() let apps react to events (post.created, user.deleted…) and change values (post.content, seo.title…). See Hooks and filters.
  • The App API. $this->api() gives an app posts, pages, media, users, comments, terms, menus, cache, mail, outbound HTTP and scheduling, all in-process. See The App API.
  • AJAX actions. One endpoint, POST /ajax, with CSRF, capability, rate-limit and input checks done for you. See AJAX actions.
  • REST API. /api/v1, using API keys or JWT. See REST API guide.
  • MCP. /mcp, used by AI assistants over OAuth 2.1 or with an API key. Apps can add their own tools. See MCP and AI agents.

Your first hour

  1. Install locally (above) and sign in at /admin.
  2. Follow Build your first app to see the app lifecycle, an admin screen and front-end output.
  3. Follow Build a theme from scratch to see templates, partials and Customizer options.
  4. Create an API key under API → Keys and try the calls in the REST API guide.

Getting help

  • Search these docs, or browse all tutorials.
  • The source is on GitHub. Its docs/ folder has the reference notes these guides grew from, and content/themes/default is a small theme worth reading.
  • Found a security problem? Don't open a public issue. Follow SECURITY.md in the repository.

Keep building

More guides, or jump straight into the API reference.

Discussion

Leave a comment