citomni/kernel 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

citomni/kernel

最新稳定版本:v1.0.2.2

Composer 安装命令:

composer require citomni/kernel

包简介

Ultra-lean application kernel for CitOmni-based apps. PHP, PSR-4, deterministic boot, zero runtime magic.

README 文档

README

Ultra-lean application kernel for CitOmni-based applications.

citomni/kernel is the smallest common runtime layer in the CitOmni ecosystem. It provides the structural primitives shared by CitOmni-based applications and packages, while deliberately avoiding transport runtimes, persistence logic, and framework-level magic.

The package exists to keep application boot deterministic, explicit, cheap to execute, and frugal with resources. In CitOmni terms, that means performance is not treated as a luxury feature bolted on later, but as part of the architectural contract from day one.

Small runtime surface. Predictable behavior. Less waste. Green by design.

What this package is

The kernel provides:

  • App as the central application object
  • Cfg as a strict, deep, read-only configuration wrapper
  • Arr for deterministic array normalization and merge helpers
  • Mode for execution-mode selection
  • thin abstract base classes for:
    • BaseController
    • BaseCommand
    • BaseOperation
    • BaseRepository
    • BaseService

In practice, the kernel gives the rest of CitOmni a shared structural contract without trying to become a transport runtime on its own.

What this package is not

The kernel does not provide:

  • HTTP routing runtime
  • request / response handling
  • session handling
  • cookies
  • CSRF protection
  • template rendering
  • CLI command dispatch
  • error handling / exception rendering
  • mail, logging, DB access, or other infrastructure services

Those concerns belong in mode packages such as citomni/http and citomni/cli, or in provider/application code.

Design goals

CitOmni kernel is built around four priorities:

  1. Determinism

    • Boot order must be explicit and reviewable.
    • Merge rules must be stable.
    • No hidden registration or discovery.
  2. Low overhead

    • Minimal indirection.
    • Minimal runtime work.
    • No namespace scanning or reflection-driven service discovery.
    • Less framework work per request/process.
  3. High performance

    • Predictable data shapes.
    • Fail-fast behavior.
    • Cheap runtime primitives.
    • Lower overhead means lower CPU time, lower memory churn, and less wasted work.
  4. Maintainable structure

    • Transport concerns stay outside the kernel.
    • Persistence concerns stay outside the kernel.
    • Shared contracts stay small and explicit.

CitOmni's performance philosophy is practical rather than theatrical: do less, allocate less, surprise less. That is good for latency, good for hosting costs, and, at scale, simply better engineering hygiene.

Installation

Install through Composer:

composer require citomni/kernel

In real applications, citomni/kernel is normally used together with one or both mode packages:

  • citomni/http
  • citomni/cli

Package structure

Current package structure:

citomni/kernel/
├── .gitignore
├── composer.json
├── CONVENTIONS.md
├── LICENSE
├── NOTICE
├── README.md
├── TRADEMARKS.md
├── src/
│   ├── App.php
│   ├── Arr.php
│   ├── Cfg.php
│   ├── Mode.php
│   ├── Command/
│   │   └── BaseCommand.php
│   ├── Controller/
│   │   └── BaseController.php
│   ├── Operation/
│   │   └── BaseOperation.php
│   ├── Repository/
│   │   └── BaseRepository.php
│   └── Service/
│       └── BaseService.php
└── tests/
    └── Command/
        └── BaseCommandTest.php

PSR-4 autoload root:

"CitOmni\\Kernel\\": "src/"

Architectural role

Within CitOmni, the kernel defines the smallest shared contract used by applications and packages.

Conceptually:

  • Adapters speak transport protocols.
  • Operations decide what happens.
  • Repositories talk to storage.
  • Services provide reusable tools.
  • Utils compute.
  • Exceptions encode failure semantics.

The kernel itself only ships the shared primitives and thin base classes behind that structure. It does not implement transport runtimes or infrastructure services itself.

Entry-point constants

CitOmni applications define a few constants early in the entrypoint.

Typical HTTP entrypoint:

<?php
declare(strict_types=1);

define('CITOMNI_ENVIRONMENT', 'dev');           // dev | stage | prod
define('CITOMNI_PUBLIC_PATH', __DIR__);
define('CITOMNI_APP_PATH', \dirname(__DIR__));
// Optional (recommended for stage/prod):
// define('CITOMNI_PUBLIC_ROOT_URL', 'https://www.example.com');

require CITOMNI_APP_PATH . '/vendor/autoload.php';

Notes:

  • CITOMNI_PUBLIC_PATH is HTTP-only.
  • CITOMNI_APP_PATH points to the app root.
  • CITOMNI_PUBLIC_ROOT_URL is optional in development, but recommended explicitly in stage/production.
  • The kernel assumes these are defined by the delivery-layer entrypoint, not by the kernel itself.

Mode

Mode selects the active execution mode.

enum Mode: string {
	case HTTP = 'http';
	case CLI  = 'cli';
}

Pass this to App so the kernel can load the correct mode-specific baselines and provider constants.

Arr

Arr contains small deterministic array helpers used by the kernel.

Arr::mergeAssocLastWins(array $a, array $b): array

Recursive merge for associative arrays where the later side wins per key.

Behavior:

  • associative arrays are merged recursively
  • numeric arrays (lists) are replaced, not merged
  • integer keys are overwritten directly by the later side

Arr::normalizeConfig(mixed $x): array

Normalizes config-like input into a plain array.

Accepted inputs:

  • array
  • object
  • Traversable

Anything else throws RuntimeException.

Cfg

Cfg is a strict, deep, read-only wrapper around the merged configuration array.

Examples:

$app->cfg->http->base_url;
$app->cfg->locale->timezone;
$app->cfg->security->csrf_protection;

Behavior:

  • associative arrays are wrapped as nested Cfg nodes
  • numeric arrays remain plain arrays
  • unknown keys throw OutOfBoundsException
  • writes/unsets throw LogicException
  • implements ArrayAccess, IteratorAggregate, and Countable
  • toArray() returns the underlying raw array

Important access note:

$csrf = (bool)($this->app->cfg->security->csrf_protection ?? true);

The null-coalescing operator is only safe for the final key in the chain. If intermediate nodes may not exist, guard them first with isset().

App

App is the central application object.

final class App {
	public readonly Cfg $cfg;
	public readonly array $routes;

	public function __construct(string $configDir, Mode $mode);
	public function __get(string $id): object;

	public function getAppRoot(): string;
	public function getConfigDir(): string;

	public function buildConfig(?string $env = null): array;
	public function warmCache(bool $overwrite = true, bool $opcacheInvalidate = true, ?string $env = null): array;

	public function hasService(string $id): bool;
	public function hasAnyService(string ...$ids): bool;
	public function hasPackage(string $slug): bool;
	public function hasNamespace(string $prefix): bool;

	public function vardumpServices(): void;
	public function memoryMarker(string $label, bool $asHeader = false): void;
}

Responsibilities

App is responsible for:

  • building final configuration
  • building final route arrays
  • building the final service map
  • exposing services as lazy singletons
  • exposing the final config as a strict read-only object

At a practical level, App is the one shared object used throughout a CitOmni request/process.

Construction

new App($configDir, $mode) expects:

  • a path that resolves to your app's /config directory
  • a Mode enum (Mode::HTTP or Mode::CLI)

If the config directory cannot be resolved, construction fails fast.

If compiled cache files exist, the constructor prefers them:

  • <appRoot>/var/cache/cfg.{http|cli}.php
  • <appRoot>/var/cache/routes.{http|cli}.php
  • <appRoot>/var/cache/services.{http|cli}.php

If a cache file is missing or invalid, the kernel falls back to the normal build pipeline.

Service resolution

Services are resolved lazily from a deterministic service map.

Supported definition shapes:

'mailer' => \App\Service\Mailer::class,

or:

'mailer' => [
	'class' => \App\Service\Mailer::class,
	'options' => [
		'transport' => 'smtp',
	],
],

Instantiation behavior is explicit:

  • string FQCN -> new $class($this)
  • array definition -> new $class($this, $options)

Unknown service ids fail fast.

There is:

  • no autowiring
  • no namespace scanning
  • no fallback discovery
  • no hidden service registration

Helper examples

<?php
declare(strict_types=1);

// 1) Check availability
if (!$app->hasService('router')) {
	throw new RuntimeException('Router service missing.');
}
$app->router->run();

// 2) Pick the first available cache backend
$candidates = ['apcuCache', 'redisCache', 'fileCache'];
$cacheId = null;

foreach ($candidates as $id) {
	if ($app->hasService($id)) {
		$cacheId = $id;
		break;
	}
}

if ($cacheId !== null) {
	$app->{$cacheId}->set('healthcheck', 'ok', ttl: 60);
}

// 3) Feature toggle by package slug
if ($app->hasPackage('citomni/auth')) {
	$app->role->enforce('ADMIN');
}

// 4) Namespace presence
if ($app->hasNamespace('\CitOmni\Commerce')) {
	// Optional commerce module is installed
}

// 5) Read routes directly
$homeController = $app->routes['/']['controller'] ?? null;

// 6) Lightweight timing/memory markers (dev only)
$app->memoryMarker('boot', true);
$app->memoryMarker('after-routing');

Merge model

CitOmni kernel follows deterministic merge rules.

Config and dispatch maps

For configuration and dispatch maps, merge behavior is:

  • deep associative merge
  • last wins

Configuration supports shared provider and app layers before mode-specific overlays. There is no vendor CFG_COMMON; the vendor baseline is supplied by the selected mode package.

Effective config order:

  1. vendor mode-specific baseline
  2. provider common cfg, if defined
  3. provider mode-specific cfg, if defined
  4. app common cfg, if present
  5. app mode-specific cfg, if present
  6. app common environment overlay, if present
  7. app mode-specific environment overlay, if present

Dispatch maps remain mode-specific. HTTP uses routes, and CLI uses commands. There is no ROUTES_COMMON or COMMANDS_COMMON.

Services

For services, merge behavior is:

  • PHP array union (+)
  • left wins

Service maps support shared provider and app layers before mode-specific overrides. There is no vendor MAP_COMMON; the vendor baseline is supplied by the selected mode package.

Effective precedence, highest first:

  • app mode-specific service file
  • app common service file
  • provider mode-specific service map
  • provider common service map
  • vendor mode-specific service map

In short:

  • config/dispatch maps: last wins
  • services: left wins

That distinction is intentional and part of the contract.

Build sources

Official app skeletons provide these standard config files:

  • config/citomni_cfg.php
  • config/services.php
  • config/providers.php

Runtime tolerates missing standard files for backwards compatibility. New apps get them from citomni/installer.

Optional app overlays include:

  • config/citomni_cfg.{env}.php
  • config/citomni_http_cfg.php
  • config/citomni_http_cfg.{env}.php
  • config/citomni_cli_cfg.php
  • config/citomni_cli_cfg.{env}.php
  • config/services_http.php
  • config/services_cli.php

Config

App::buildConfig() builds config in this order. Later entries override earlier entries through deep associative merge.

For HTTP:

  1. mode-package baseline CFG_HTTP
  2. provider CFG_COMMON, if defined
  3. provider CFG_HTTP, if defined
  4. config/citomni_cfg.php, if present
  5. config/citomni_http_cfg.php, if present
  6. config/citomni_cfg.{env}.php, if present
  7. config/citomni_http_cfg.{env}.php, if present

For CLI:

  1. mode-package baseline CFG_CLI
  2. provider CFG_COMMON, if defined
  3. provider CFG_CLI, if defined
  4. config/citomni_cfg.php, if present
  5. config/citomni_cli_cfg.php, if present
  6. config/citomni_cfg.{env}.php, if present
  7. config/citomni_cli_cfg.{env}.php, if present

Missing app config files are ignored for backwards compatibility. Existing config files must return values accepted by Arr::normalizeConfig(). Official scaffolded config files return arrays.

Dispatch maps

Dispatch maps remain mode-specific and use deep associative merge with last-wins behavior.

HTTP routes are built from:

  1. mode-package baseline ROUTES_HTTP
  2. provider ROUTES_HTTP, if defined
  3. config/citomni_http_routes.php, if present
  4. config/citomni_http_routes.{env}.php, if present

CLI commands are built from:

  1. mode-package baseline COMMANDS_CLI
  2. provider COMMANDS_CLI, if defined
  3. config/citomni_cli_commands.php, if present
  4. config/citomni_cli_commands.{env}.php, if present

There are no common dispatch constants. ROUTES_HTTP and COMMANDS_CLI stay tied to their transport adapters.

Services

Services are built with PHP array union semantics. The first matching service id wins.

Effective HTTP precedence, highest first:

  1. config/services_http.php, if present
  2. config/services.php, if present
  3. provider MAP_HTTP, if defined
  4. provider MAP_COMMON, if defined
  5. mode-package baseline MAP_HTTP

Effective CLI precedence, highest first:

  1. config/services_cli.php, if present
  2. config/services.php, if present
  3. provider MAP_CLI, if defined
  4. provider MAP_COMMON, if defined
  5. mode-package baseline MAP_CLI

Later providers in config/providers.php have higher precedence than earlier providers. Existing service files must return arrays.

Providers and boot metadata

Provider packages contribute boot metadata through:

src/Boot/Registry.php

Typical constants are:

  • MAP_COMMON
  • MAP_HTTP
  • MAP_CLI
  • CFG_COMMON
  • CFG_HTTP
  • CFG_CLI
  • ROUTES_HTTP
  • COMMANDS_CLI

All are optional.

Important rules:

  • routes must not be nested inside config constants
  • commands must not be nested inside config constants
  • there is no ROUTES_COMMON or COMMANDS_COMMON

A provider may contribute only services, only config, only routes, only commands, or any combination of these.

Mode-package baseline note

The current kernel implementation reads mode-package baselines from:

  • \CitOmni\Http\Boot\Registry
  • \CitOmni\Cli\Boot\Registry

Specifically, it expects mode-specific constants such as:

  • CFG_HTTP / CFG_CLI
  • MAP_HTTP / MAP_CLI
  • ROUTES_HTTP / COMMANDS_CLI

There is no vendor CFG_COMMON or vendor MAP_COMMON. Shared package defaults belong in provider CFG_COMMON / MAP_COMMON constants or in app-level common files.

So while provider packages are documented via src/Boot/Registry.php, the current kernel code also expects Registry-based baseline access from the mode packages it integrates with.

Caches

The kernel can use compiled cache artifacts for configuration, routes, and services.

Cache targets:

  • var/cache/cfg.http.php
  • var/cache/routes.http.php
  • var/cache/services.http.php
  • var/cache/cfg.cli.php
  • var/cache/routes.cli.php
  • var/cache/services.cli.php

These caches are performance tools, not correctness mechanisms.

Expected properties:

  • side-effect free
  • plain PHP files
  • deterministic content
  • safe to regenerate

warmCache()

warmCache(bool $overwrite = true, bool $opcacheInvalidate = true, ?string $env = null): array rebuilds config, dispatch, and services, then writes the three cache files atomically.

Returned shape:

[
	'cfg' => '/absolute/path/to/cfg.http.php' | null,
	'routes' => '/absolute/path/to/routes.http.php' | null,
	'services' => '/absolute/path/to/services.http.php' | null,
]

If overwrite is false and a target already exists, that entry returns null.

Typical deploy usage:

$app = new \CitOmni\Kernel\App(__DIR__ . '/../config', \CitOmni\Kernel\Mode::HTTP);
$app->warmCache(overwrite: true, opcacheInvalidate: true);

$cli = new \CitOmni\Kernel\App(__DIR__ . '/../config', \CitOmni\Kernel\Mode::CLI);
$cli->warmCache(overwrite: true, opcacheInvalidate: true);

Ensure the process can write to <appRoot>/var/cache/.

Dev helpers

vardumpServices()

Dev-only helper that dumps the current service map.

Outside dev, it throws RuntimeException.

memoryMarker(string $label, bool $asHeader = false): void

Dev-oriented lightweight marker for memory/timing diagnostics.

Behavior:

  • outside dev, it returns immediately

  • with $asHeader = true and headers still open, it emits:

    • X-CitOmni-MemMark: ...
  • otherwise it emits an HTML comment

This is intentionally cheap and practical, not a profiling framework.

Base classes shipped by the kernel

The base classes in this package are intentionally thin.

They exist to standardize constructor contracts and shared access to App, not to hide behavior behind inheritance.

BaseController

HTTP-facing adapter base.

Responsibilities belong to the adapter layer:

  • request parsing
  • CSRF/session checks
  • response shaping
  • view/model translation for transport output

A controller must not own SQL or non-trivial orchestration.

BaseCommand

CLI-facing adapter base.

Responsibilities belong to the adapter layer:

  • argument parsing
  • terminal output
  • exit codes

A command must not own SQL or cross-cutting workflow logic.

BaseOperation

Transport-agnostic orchestration base.

An operation:

  • is instantiated explicitly
  • is SQL-free
  • may coordinate services and repositories
  • returns domain-shaped arrays

Operations are introduced when orchestration earns its existence, not by default.

BaseRepository

Persistence base.

Repositories own:

  • SQL
  • datastore IO
  • persistence-oriented mapping and lookup logic

Repositories do not own transport shaping or workflow orchestration.

BaseService

Reusable App-aware service base.

Services are:

  • registered in the service map
  • resolved through $app->{id}
  • typically infrastructure or cross-cutting helpers

Examples include mail, logging, text lookup, formatting, caching, and similar reusable tools.

Recommended usage pattern

The normal decision path in CitOmni is:

  • trivial read/write:

    • Controller/Command -> Repository
  • non-trivial orchestration:

    • Controller/Command -> Operation -> Repository/Services

This keeps the call graph explicit and prevents needless abstraction.

Example: explicit operation usage

<?php
declare(strict_types=1);

use App\Operation\PublishArticle;

$operation = new PublishArticle($this->app);
$result = $operation->run($input);

Operations are instantiated explicitly with new ...($this->app). They are not hidden behind container magic.

Example: service access

<?php
declare(strict_types=1);

if ($this->app->hasService('mailer')) {
	$this->app->mailer->send($message);
}

Example: config access

<?php
declare(strict_types=1);

$timezone = $this->app->cfg->locale->timezone ?? 'UTC';

Example: route access

<?php
declare(strict_types=1);

$route = $this->app->routes['/dashboard'] ?? null;

Application layout context

The kernel package itself is small, but it is designed to sit inside the wider CitOmni application structure.

Typical application-layer folders include:

  • src/Http/Controller
  • src/Cli/Command
  • src/Operation
  • src/Repository
  • src/Service
  • src/Exception
  • config
  • language
  • templates
  • public
  • bin
  • var

The kernel does not require every folder to exist in this package. It only supplies the shared structural primitives used by that architecture.

Performance philosophy

The kernel is designed around mechanical sympathy with ordinary PHP runtimes.

That means:

  • explicit wiring over discovery
  • arrays with predictable shapes
  • lazy service instantiation
  • fail-fast behavior
  • no hidden runtime work

The goal is not fashionable abstraction. The goal is a small, predictable runtime surface with low operating cost.

CitOmni prefers architecture that earns its keep. If a layer, helper, or boot mechanism adds overhead without adding proportional value, it does not belong in the kernel. The fastest code is not always the code with the cleverest story, but very often the code that simply does less.

That is part of what "green by design" means here: fewer moving parts, fewer wasted cycles, and less accidental framework work between input and outcome.

Why "green by design" matters

CitOmni treats resource efficiency as an engineering concern, not a slogan.

When a framework avoids unnecessary discovery, hidden boot work, and inflated abstraction layers, the result is not only easier to reason about. It also tends to use less CPU time, less memory, and fewer machine cycles to complete the same job.

For a single request, that may look modest. Across many requests, many CLI runs, and many deployments, it adds up. Lower overhead is good for speed, good for operational cost, and good for the broader goal of building software that wastes fewer resources.

Failure philosophy

The kernel fails fast by design.

Typical failure cases include:

  • missing config directory
  • malformed provider registration
  • invalid service definitions
  • unknown service ids
  • invalid cache writes
  • unknown config keys

The kernel should not silently recover from structural mistakes that deserve to be fixed.

Contributing

  • Code style: PHP 8.2+, PSR-4, tabs, K&R braces
  • Keep vendor files side-effect free
  • Do not hide failures behind silent fallbacks
  • Prefer deterministic structure over cleverness

Coding & Documentation Conventions

All CitOmni and LiteX projects follow the shared conventions documented here: CitOmni Coding & Documentation Conventions

License

CitOmni Kernel is open source under the MIT License. See: LICENSE.

Trademark notice: "CitOmni" and the CitOmni logo are trademarks of Lars Grove Mortensen. Use of the name or logo must follow the policy in NOTICE. Do not imply endorsement or affiliation without prior written permission.

FAQ (licensing)

Can I build proprietary providers or plugins on top of CitOmni? Yes. Your apps and providers may use any license terms you choose.

Do I need to keep attribution? Yes. Keep the copyright and license notice from LICENSE in distributions of the Software.

Can I call my product "CitOmni Something"? No. The name "CitOmni" and the logo are protected by trademark. Do not imply sponsorship, endorsement, or affiliation without permission.

Trademarks

"CitOmni" and the CitOmni logo are trademarks of Lars Grove Mortensen. You may make factual references to CitOmni, but do not modify the marks, create confusingly similar logos, or imply sponsorship, endorsement, or affiliation without prior written permission.

Do not register or use citomni (or confusingly similar terms) in company names, domains, social handles, or top-level vendor/package names.

For details, see NOTICE.

Author

Developed by Lars Grove Mortensen © 2012-present. Contributions and pull requests are welcome.

CitOmni Kernel keeps the common core deliberately small, because efficient software should spend its resources on the application's work, not on the framework getting ready to do it.

Built with ❤️ on the CitOmni philosophy: low overhead, high performance, and ready for anything.

统计信息

  • 总下载量: 26
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 9
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-09-28

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固