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
| Item | Requirement |
|---|---|
| PHP | 8.1+ (8.3 recommended) with pdo_mysql, mbstring, curl, simplexml, fileinfo, zip, openssl |
| Database | MySQL 5.7+ / MariaDB 10.3+, utf8mb4 |
| Web server | Nginx 1.20+ or Apache 2.4+ with URL rewriting |
| Directories | Site root writable (uploads, var/cache, var/home-static) |
| Optional | HTTPS certificate, CDN such as Cloudflare |
Steps
- Download the release and unpack it into the site root;
index.php,core/,themes/andplugins/must sit side by side. - Create a database:
CREATE DATABASE yecha DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - Copy
config.inc.php.exampletoconfig.inc.phpand fill in the database settings andSECRET(see Configuration). - Open the site root in a browser and complete the one-time setup wizard. If tables already exist,
/install/reports "already installed". - 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; }- Right after installation: change the admin password, delete or lock the
install/directory, and confirmcore/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/varCommon installation errors
| Symptom | Cause | Fix |
|---|---|---|
/install/ returns 403 | The deny rules also block the installer | Split 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 databases | ALTER TABLE ye_contents ADD COLUMN words INT UNSIGNED NOT NULL DEFAULT 0; then backfill with CHAR_LENGTH(content) |
Blank home page / template not found | Theme lacks views/landing.php or views/layout.php | Switch back to the default yecha theme or add the templates |
| Pages 404 while the admin works | URL rewriting is not active | Apply the Nginx rules and reload nginx |
| Image upload fails | uploads is not writable | Fix 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
- Replace the core files (
index.php,core/,admin/,lang/) but keepconfig.inc.php,uploads/andvar/. - Add or replace theme/plugin directories; back up same-named themes before overwriting.
- Plugin migrations run automatically when a plugin is enabled (
Upgrade::apply(), idempotent and safe to re-run). - 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.xmland/feedwork;- 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/wwwrootTheme 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.pngtheme.json
| Field | Meaning |
|---|---|
| name / version / description | Display name, version and summary (used by the admin and the online themes page) |
| requires_core | Required core version |
| author / license | Author and license |
| settings | Admin-editable options: key / type (select, color, number, checkbox) / label / def / opts |
| home | Set 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
- Drop the directory into
themes/; it appears in the admin theme list. - After switching, flush page caches and rebuild the static home pages (see Cache and static files).
- Missing templates fall back to the default
yechatheme and are logged invar/log/php-error.log— never silent. - Before publishing, run
php -lover 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 *.sqlplugin.php manifest
| Field | Meaning |
|---|---|
| id / name / ver / desc / author / license | Basic metadata, also used by the online plugins page |
| requires.core | Required core version |
| toggle | Whether the plugin may be disabled |
| tables | Owned tables (created and dropped from this list) |
| pages | Public pages: [['slug'=>'hello','title'=>'Example']], routed at /<slug> |
| blocks | Sidebar blocks: [['id'=>'hello','label'=>'Example block']] |
| register_file | Included when enabled (usually boot.php) |
| admin_file / admin_fn | Admin page file and rendering function |
| page_file / page_fn | Public 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 callCsrf::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
| Constant | Meaning |
|---|---|
| DB_HOST / DB_PORT / DB_NAME / DB_USER / DB_PASS | Database connection |
| DB_PREFIX | Table prefix, default ye_ |
| SECRET | Site secret for sessions and encryption (AI keys etc.); changing it invalidates stored secrets |
| TZ | Timezone, default Asia/Shanghai |
| SITE_LANG | Default UI language, e.g. zh_CN |
| DEBUG | Must be false in production; true prints errors to the page |
Site options (options table)
| Option | Meaning |
|---|---|
| site.name / site.name_en | Site name in Chinese / English (English is used under /en/) |
| site.subtitle / site.subtitle_en | Tagline / English tagline |
| site.languages / site.default_lang | Enabled languages and the default, e.g. ["zh","en"] |
| theme.default / theme.options | Active theme and its settings |
| home.landing | 1 = use the theme portal home page |
| nav.hide_top / nav.hide_foot / nav.custom | Hidden menu items and custom navigation |
| stats.head / stats.foot | Analytics snippets (empty means zero outbound requests) |
| market.source | Market 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
SECRETbefore storage, and never echoed back; SECRETlives only inconfig.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=trueis for local debugging only.
Cache and static files
| Path | Purpose | When to clear |
|---|---|---|
var/cache/pages | Full-page cache | After content, theme or menu changes |
var/cache/blocks | Sidebar and block cache | After 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.