定制 ironflow-framework/framework 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

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

ironflow-framework/framework

最新稳定版本:v0.1.5

Composer 安装命令:

composer require ironflow-framework/framework

包简介

IronFlow — PHP 8.2+ HMVC Framework Core

README 文档

README

IronFlow

ironflow-framework/framework

Le cœur d'IronFlow — conteneur DI, routeur, ORM, modules HMVC, CLI.

PHP 8.2+ License MIT Version Tests

Installation · Architecture modulaire · Injection de dépendances · Routing · ORM · CLI · Contribuer

Ce dépôt contient le noyau du framework. Il n'est pas destiné à être cloné directement pour démarrer un projet — utilise ironflow/skeleton pour ça. Ce README documente l'architecture interne, les API publiques et les conventions à respecter pour y contribuer.

Installation

Via le skeleton (recommandé) :

composer create-project ironflow/skeleton mon-app

Ou comme dépendance directe dans un projet existant :

composer require ironflow/framework

Prérequis : PHP 8.2+, Composer 2+, extensions pdo, mbstring, json.

Architecture modulaire

IronFlow organise le code en modules HMVC isolés avec dépendances déclaratives — inspiré de NestJS, porté en PHP idiomatique.

#[Module(
    name: 'blog',
    imports: ['auth'],                   // modules dont celui-ci dépend
    providers: [PostService::class],     // services internes
    exports: [PostService::class],       // API publique exposée aux autres modules
)]
class BlogModule extends BaseModule {}

Règles d'isolation

  • Un provider est privé par défaut : inaccessible hors du module, sauf s'il est listé dans exports.
  • Un module ne peut consommer que les providers exportés de ses dépendances déclarées dans imports.
  • Les violations sont détectées au démarrage, pas à l'exécution.

Graphe de dépendances

Au boot, IronFlow construit un graphe orienté de tous les modules, valide l'absence de cycles (tri topologique) et détermine l'ordre d'initialisation. Inspecte-le :

php forge module:graph           # affichage ASCII
php forge module:graph --check   # retourne une erreur en cas de cycle (utile en CI)

Scaffolding d'un module

php forge make:module Blog
php forge make:controller PostController --module=Blog --resource
php forge make:model Post --module=Blog --migration --factory

Injection de dépendances

Le conteneur résout les dépendances par réflexion sur les type hints. Aucune configuration manuelle nécessaire pour les cas courants.

#[Injectable]
class PostService
{
    public function __construct(
        private readonly PostRepository $posts,
        private readonly EventBus $events,
        #[Inject('config.app.name')] private readonly string $appName,
    ) {}
}

Ce que supporte le conteneur

Fonctionnalité Exemple
Auto-résolution par type hint PostRepository $posts
Injection de scalaires nommés #[Inject('config.mail.from')]
Injection dans les méthodes de contrôleurs public function show(Post $post, Request $request)
Singletons et liaisons explicites $container->bind(MailerInterface::class, SmtpMailer::class)
Providers de module définis dans providers: [...] de #[Module]

Liaison manuelle

// Dans un ServiceProvider ou un Module::register()
$this->container->bind(CacheInterface::class, RedisCache::class);
$this->container->singleton(Config::class, fn() => new Config(base_path('config')));

Routing

Routes basiques

Router::get('/posts/{id}', [PostController::class, 'show'])
    ->name('posts.show')
    ->middleware('auth')
    ->where('id', '[0-9]+');

Router::post('/posts', [PostController::class, 'store'])
    ->middleware(['auth', 'throttle:10,1']);

Groupes et ressources

Router::group(['prefix' => '/api/v1', 'middleware' => ['throttle:60']], function () {
    Router::resource('posts', Api\PostController::class);         // 7 routes RESTful
    Router::resource('comments', Api\CommentController::class)->only(['index', 'store']);
});

Routes nommées dans les templates

<a href="{{ route('posts.show', {id: post.id}) }}">Lire</a>

Inspection

php forge route:list
# +---------+----------------------------+------------------+-----------+
# | Method  | URI                        | Name             | Middleware|
# +---------+----------------------------+------------------+-----------+
# | GET     | /posts/{id}                | posts.show       | auth      |
# | POST    | /posts                     | posts.store      | auth,thro…|
#

ORM

Active Record sur doctrine/dbal. Pas de QueryBuilder exposé directement — passe par les modèles et les scopes.

Modèle de base

class Post extends Model
{
    protected string $table = 'posts';

    protected array $casts = [
        'published_at' => 'datetime',
        'status'       => PostStatus::class,   // enum PHP 8.1+
        'metadata'     => 'json',
    ];

    protected array $fillable = ['title', 'body', 'author_id'];

    use SoftDeletes;
}

Requêtes

// Eager loading anti N+1
$posts = Post::with('author', 'comments')
    ->withCount('comments')
    ->published()          // scope
    ->latest()
    ->paginate(15);

// Scope personnalisé
public function scopePublished(QueryBuilder $query): QueryBuilder
{
    return $query->where('status', PostStatus::Published)
                 ->where('published_at', '<=', now());
}

Relations supportées

Relation Méthode
Un-à-plusieurs hasMany(Comment::class)
Plusieurs-à-plusieurs avec pivot belongsToMany(Tag::class)->withPivot('weight')
À travers hasManyThrough(Comment::class, Post::class)
Inverse belongsTo(User::class)

Migrations

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('body');
    $table->enum('status', ['draft', 'published'])->default('draft');
    $table->timestamp('published_at')->nullable();
    $table->foreignId('author_id')->constrained('users');
    $table->softDeletes();
    $table->timestamps();
});
php forge migrate
php forge migrate --rollback
php forge migrate --fresh --seed

Événements de modèle

Post::creating(fn(Post $post) => $post->slug = Str::slug($post->title));
Post::deleted(fn(Post $post) => Cache::forget("post:{$post->id}"));

Middlewares

Deux styles au choix — cohérence garantie dans les deux cas.

Style oignon (recommandé) :

class AuthMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        if (!$request->session()->has('user')) {
            return redirect()->route('login');
        }
        return $next($request);
    }
}

Style Django (hooks) :

class LogMiddleware
{
    public function processRequest(Request $request): ?Response  { /* ... */ }
    public function processResponse(Request $request, Response $response): Response { /* ... */ }
}

Middlewares globaux définis dans config/http.php, alias par route enregistrés dans le router.

Bus d'événements

Découplage inter-modules sans import direct.

// Dans BlogModule : émet
$this->events->dispatch(new PostPublished($post));

// Dans NewsletterModule : écoute — sans importer BlogModule
#[EventListener]
class SendNewsletterOnPublish
{
    public function handle(PostPublished $event): void
    {
        $this->mailer->queue(new NewPostEmail($event->post));
    }
}

L'abonnement est auto-détecté via #[EventListener] à condition que le module soit dans le graphe de dépendances.

Templates Twig

Namespaces par module :

{# modules/Blog/templates/posts/index.html.twig #}
{% extends '@app/layouts/main.html.twig' %}

{% block content %}
    {% for post in posts %}
        <a href="{{ route('posts.show', {id: post.id}) }}">{{ post.title }}</a>
        <span>{{ post.published_at | time_ago }}</span>
    {% endfor %}
    {{ paginator(posts) }}
{% endblock %}

Fonctions et filtres disponibles

Catégorie Exemples
Routing route('name', params), current_route()
Assets asset('app.css') — cache-busting automatique
Formulaires csrf_field(), old('field'), errors('field')
Filtres time_ago, markdown, slug, money, truncate
Auth auth_user(), is_auth()

CLI Forge

php forge list                           # toutes les commandes disponibles
php forge make:module <Name>             # scaffolde un module complet
php forge make:controller <Name> [opts]  # --module, --resource, --api
php forge make:model <Name> [opts]       # --module, --migration, --factory
php forge make:middleware <Name>
php forge make:command <Name>
php forge migrate [--fresh] [--seed] [--rollback]
php forge db:seed [--class=]
php forge route:list
php forge module:graph [--check]
php forge down [--message=] [--retry=]  # mode maintenance
php forge up
php forge serve [--host=] [--port=]

Commandes personnalisées

#[Command(signature: 'blog:seed {count=10} {--fresh}', description: 'Seed blog posts')]
class SeedBlogCommand extends BaseCommand
{
    public function handle(): int
    {
        $count = (int) $this->argument('count');
        // ...
        $this->info("$count posts créés.");
        return self::SUCCESS;
    }
}

Ce qui est sur étagère vs fait maison

Délégué à Construit par IronFlow
symfony/http-foundation — HTTP bas niveau Conteneur DI avec attributs PHP 8
symfony/console — fondation CLI Système de modules + graphe de dépendances
twig/twig — moteur de templates Routeur fluide avec route naming
doctrine/dbal — couche SQL ORM Active Record, migrations, factories
monolog/monolog — logging Middlewares, bus d'événements
firebase/php-jwt — tokens JWT Extension Twig, scaffolding CLI complet
vlucas/phpdotenv — variables d'env Auth session + JWT, validation, CSRF

Chaque composant externe est wrappé derrière nos propres interfaces dans Ironflow\Ironflow\. Ton code n'importe jamais Symfony\Component\HttpFoundation\Request — seulement Ironflow\Ironflow\Http\Request.

Roadmap

  • Conteneur DI avec attributs, résolution par réflexion
  • Architecture modulaire HMVC avec graphe de dépendances
  • Routeur fluide — groupes, ressources, named routes, contraintes
  • ORM Active Record — relations, eager loading, scopes, soft deletes
  • Migrations et Schema builder
  • CLI forge avec scaffolding complet
  • Auth session + JWT
  • Validation
  • CSRF, middlewares globaux et par-route
  • Bus d'événements découplé
  • Extension Twig maison + view composers
  • Cache — facade unifiée, drivers file/redis
  • File d'attente de jobs (queue)
  • WebSockets / diffusion temps réel
  • Documentation complète avec recettes

Contribuer

git clone https://github.com/ironflow-framework/framework
git clone https://github.com/ironflow-framework/skeleton

cd skeleton
composer install   # le framework est lié en repository path (symlink local)
php forge serve
# Lancer les tests
vendor/bin/phpunit

# Avec couverture
vendor/bin/phpunit --coverage-html coverage/

Conventions :

  • PSR-12, typages stricts (declare(strict_types=1) dans tous les fichiers)
  • Chaque feature → test unitaire + test d'intégration
  • Pas de breaking change en patch, dépréciation avant suppression en minor
  • Issues marquées good first issue pour débuter
  1. Fork du dépôt concerné
  2. git checkout -b feature/ma-feature
  3. Tests au vert (vendor/bin/phpunit)
  4. Pull Request vers main avec description des changements

Chaque framework est une théorie du bon code.
IronFlow parie sur la modularité explicite, les attributs PHP 8, et le respect de tes conventions à toi.

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-10

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固