Documentation

YeCha 1.0.0 · a complete handbook from installation to extension. The current release is 1.0.0; this site ships 34 themes and 20 plugins, all browsable online at /en/theme and /en/plugin. Every command below runs on a production server; back up first (database + files + config).

On this page

Install

Requirements

ItemRequirement
PHP8.1+ (8.3 recommended) with pdo_mysql, mbstring, curl, simplexml, fileinfo, zip, openssl
DatabaseMySQL 5.7+ / MariaDB 10.3+, utf8mb4
Web serverNginx 1.20+ or Apache 2.4+ with URL rewriting
DirectoriesSite root writable (uploads, var/cache, var/home-static)
OptionalHTTPS certificate, CDN such as Cloudflare

Steps

  1. Download the release and unpack it into the site root; index.php, core/, themes/ and plugins/ must sit side by side.
  2. Create a database: CREATE DATABASE yecha DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  3. Copy config.inc.php.example to config.inc.php and fill in the database settings and SECRET (see Configuration).
  4. Open the site root in a browser and complete the one-time setup wizard. If tables already exist, /install/ reports "already installed".
  5. Configure URL rewriting (minimal Nginx):
location / { try_files $uri $uri/ /index.php?$query_string; }
location ~ ^/(var|data|install|tools|tests|core|config\.inc\.php) { deny all; }
location ~* \.(bak|orig|old|save|swp|tmp|sql|gz|log)$ { deny all; }
  1. Right after installation: change the admin password, delete or lock the install/ directory, and confirm core/ is not served.

Directory permissions

chown -R www:www /www/wwwroot/yecha.org
find /www/wwwroot/yecha.org -type d -exec chmod 755 {} \;
find /www/wwwroot/yecha.org -type f -exec chmod 644 {} \;
chmod -R 775 /www/wwwroot/yecha.org/uploads /www/wwwroot/yecha.org/var

Common installation errors

SymptomCauseFix
/install/ returns 403The deny rules also block the installerSplit install into its own rule, never with ^~
Site-wide 500, log shows Unknown column 'c.words'The 1.0.0 core reads ye_contents.words, missing in old databasesALTER TABLE ye_contents ADD COLUMN words INT UNSIGNED NOT NULL DEFAULT 0; then backfill with CHAR_LENGTH(content)
Blank home page / template not foundTheme lacks views/landing.php or views/layout.phpSwitch back to the default yecha theme or add the templates
Pages 404 while the admin worksURL rewriting is not activeApply the Nginx rules and reload nginx
Image upload failsuploads is not writableFix ownership as above

Upgrade

Back up first

B=/root/backup/$(date -u +%Y%m%dT%H%M%SZ); mkdir -p "$B"
mysqldump --single-transaction --routines yecha | gzip > "$B/yecha.sql.gz"
tar czf "$B/site.tar.gz" -C /www/wwwroot yecha.org --exclude='uploads/*' --exclude='var/cache/*'
cp -a /www/wwwroot/yecha.org/config.inc.php "$B/config.inc.php.orig"

Steps

  1. Replace the core files (index.php, core/, admin/, lang/) but keep config.inc.php, uploads/ and var/.
  2. Add or replace theme/plugin directories; back up same-named themes before overwriting.
  3. Plugin migrations run automatically when a plugin is enabled (Upgrade::apply(), idempotent and safe to re-run).
  4. Flush caches and rebuild the static home pages (see Cache and static files).

Post-upgrade checks

  • Sample 10 pages: all 200 and no 5xx;
  • The admin can log in and save settings;
  • Theme/plugin counts match the directories on disk;
  • /sitemap.xml and /feed work;
  • For multilingual sites, check /en/ and /en/category/…/.

Roll back

gunzip -c /root/backup/<timestamp>/yecha.sql.gz | mysql yecha
tar xzf /root/backup/<timestamp>/site.tar.gz -C /www/wwwroot

Theme development

Structure

themes/<id>/
  theme.json          # metadata and admin-side settings
  style.css           # theme styles
  views/
    layout.php        # page shell (required)
    landing.php       # portal home (optional; used when home.landing=1)
    landing-en.php    # per-language home (optional; used under /en/)
    404.php
    partials/         # list / post / page / search / archives …
  logo.png / favicon.png

theme.json

FieldMeaning
name / version / descriptionDisplay name, version and summary (used by the admin and the online themes page)
requires_coreRequired core version
author / licenseAuthor and license
settingsAdmin-editable options: key / type (select, color, number, checkbox) / label / def / opts
homeSet to landing to use the portal home page

Minimal working theme

themes/hello/theme.json:

{ "name": "Hello", "version": "0.1.0", "requires_core": "1.0.0", "description": "Minimal example theme" }

themes/hello/views/layout.php:

<!DOCTYPE html><html lang="<?= e($htmlLang ?? 'en') ?>"><head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title><?= e($title) ?></title>
<link rel="stylesheet" href="<?= e(View::asset('style.css')) ?>">
</head><body>
<header><?php foreach (Nav::list('top', Lang::short()) as $n): ?>
<a href="<?= e($n['href']) ?>"<?= $n['active'] ? ' class="active"' : '' ?>><?= e($n['label']) ?></a>
<?php endforeach; ?></header>
<main><?= $main ?></main>
<aside><?= $side ?></aside>
</body></html>

Templates and variables

  • $title, $main (body HTML), $side (sidebar HTML), $htmlLang;
  • Helpers: e() escape, __() language pack, site_label() site name, theme_opt('key','default') theme settings, Nav::list('top'|'foot') navigation, Poly::termUrl()/termName() multilingual terms;
  • Emit assets through View::asset('style.css') to get a versioned URL (?v=mtime) automatically.

Debug and publish

  1. Drop the directory into themes/; it appears in the admin theme list.
  2. After switching, flush page caches and rebuild the static home pages (see Cache and static files).
  3. Missing templates fall back to the default yecha theme and are logged in var/log/php-error.log — never silent.
  4. Before publishing, run php -l over every file and check the collapsed menu at 390px.

Plugin development

Structure

plugins/<id>/
  plugin.php      # manifest (returns an array)
  boot.php        # hooks registered when enabled (register_file)
  admin.php       # admin page (admin_file/admin_fn)
  page.php        # public page (page_file/page_fn)
  migrations/     # idempotent migrations *.php or *.sql

plugin.php manifest

FieldMeaning
id / name / ver / desc / author / licenseBasic metadata, also used by the online plugins page
requires.coreRequired core version
toggleWhether the plugin may be disabled
tablesOwned tables (created and dropped from this list)
pagesPublic pages: [['slug'=>'hello','title'=>'Example']], routed at /<slug>
blocksSidebar blocks: [['id'=>'hello','label'=>'Example block']]
register_fileIncluded when enabled (usually boot.php)
admin_file / admin_fnAdmin page file and rendering function
page_file / page_fnPublic page file and function; return ['html'=>…, 'title'=>…]
uninstall['tables'=>[], 'options'=>[], 'like'=>[]]

Minimal plugin

plugins/hello/plugin.php:

<?php
return [
  'id' => 'hello', 'name' => 'Hello', 'ver' => '0.1.0',
  'desc' => 'Minimal example plugin', 'author' => 'YeCha', 'license' => 'MIT',
  'requires' => ['core' => '1.0.0'], 'toggle' => true,
  'pages' => [['slug' => 'hello', 'title' => 'Example']],
  'register_file' => 'boot.php',
  'page_file' => 'page.php', 'page_fn' => 'hello_page',
];

plugins/hello/boot.php:

<?php
Hooks::on('front.head', function (array $p): array { return $p; }, 10);

plugins/hello/page.php:

<?php
function hello_page(string $slug): array {
  return ['html' => '<h1>Hello</h1><p>From the example plugin.</p>', 'title' => 'Example · ' . site_label()];
}

Enable it in the admin panel and open /hello.

Routes, security and migrations

  • Multi-level paths: the core passes /<slug>/… to the plugin page; sub-segments are in $GLOBALS['YC_PLUGIN_PATH'];
  • Admin pages require an admin session; forms must include Csrf::field() and call Csrf::check();
  • Put migrations in migrations/ and make them idempotent (CREATE TABLE IF NOT EXISTS, check before create);
  • Disabling stops registration but keeps data; uninstalling follows uninstall.

Configuration

config.inc.php

ConstantMeaning
DB_HOST / DB_PORT / DB_NAME / DB_USER / DB_PASSDatabase connection
DB_PREFIXTable prefix, default ye_
SECRETSite secret for sessions and encryption (AI keys etc.); changing it invalidates stored secrets
TZTimezone, default Asia/Shanghai
SITE_LANGDefault UI language, e.g. zh_CN
DEBUGMust be false in production; true prints errors to the page

Site options (options table)

OptionMeaning
site.name / site.name_enSite name in Chinese / English (English is used under /en/)
site.subtitle / site.subtitle_enTagline / English tagline
site.languages / site.default_langEnabled languages and the default, e.g. ["zh","en"]
theme.default / theme.optionsActive theme and its settings
home.landing1 = use the theme portal home page
nav.hide_top / nav.hide_foot / nav.customHidden menu items and custom navigation
stats.head / stats.footAnalytics snippets (empty means zero outbound requests)
market.sourceMarket manifest URL; empty = local var/market/manifest.json

Secrets

  • Never hard-code keys in themes, plugins or the repository. AI, SMS and payment keys are entered in the admin panel, encrypted with SECRET before storage, and never echoed back;
  • SECRET lives only in config.inc.php (mode 640, owned by root:www) and never in version control;
  • Before rotating SECRET, export and re-enter the affected keys, otherwise old ciphertext cannot be decrypted;
  • Logs, error pages and exports must never contain keys; DEBUG=true is for local debugging only.

Cache and static files

PathPurposeWhen to clear
var/cache/pagesFull-page cacheAfter content, theme or menu changes
var/cache/blocksSidebar and block cacheAfter sidebar or plugin changes
var/home-static/Baked home pages (including /en/)After home page, theme or analytics changes
php -r 'chdir("/www/wwwroot/yecha.org"); require "config.inc.php"; require "core/bootstrap.php"; Cache::flush("pages"); Cache::flush("blocks");'
# rebuild static home pages: visit the site or use the admin "Static home page → Generate"

FAQ

/en/category/xxx/ returns 500

The database lacks ye_contents.words. Add the column and backfill as described in common installation errors, then flush the page cache.

The home page still looks like the old theme

The home page is baked into var/home-static/index.html and en/index.html. Delete or regenerate it so the new theme is rendered.

The admin logs in and immediately bounces back

Usually an HTTPS/session cookie mismatch. Make sure the origin sets fastcgi_param HTTPS on, that site.url matches the visited domain, and clear browser cookies once.

Translated content does not appear

Check that the poly plugin is enabled, site.languages contains the language, and the translation status is publish. Term names and aliases are managed in the admin multilingual screen.

Market installation fails

Only https packages are accepted and SHA-256 is mandatory. Verify the package structure (plugin.php / theme.json), and that every PHP file passes php -l.

How do I keep only a corporate site and hide the blog?

Enable the site plugin and switch to corporate mode in the admin panel; blog posts redirect with 301 while plugin pages and docs are unaffected. Switch back at any time.

How do I migrate a site?

Dump the database and archive the site files including uploads/. On the new server follow Install, import the database, update config.inc.php, then flush caches and rebuild static pages.