studiometa/foehn
Composer 安装命令:
composer require studiometa/foehn
包简介
🍃 A modern WordPress framework powered by Tempest, featuring attribute-based auto-discovery for hooks, post types, blocks, and more.
README 文档
README
A modern WordPress framework powered by Tempest Framework, featuring attribute-based auto-discovery for hooks, post types, blocks, and more.
Note This package is part of the Føhn Framework monorepo. Please report issues and submit pull requests in the main repository.
Requirements
- PHP 8.5+
- WordPress 6.4+
- Composer
Installation
composer require studiometa/foehn
Quick Start
Bootstrap Føhn in your theme's functions.php:
<?php declare(strict_types=1); use Studiometa\Foehn\Kernel; Kernel::boot(__DIR__ . '/app');
That's it! Føhn will automatically discover and register all your classes in the app/ directory.
Features
Hooks
Register WordPress actions and filters directly on your methods:
<?php use Studiometa\Foehn\Attributes\AsAction; use Studiometa\Foehn\Attributes\AsFilter; final class ThemeHooks { #[AsAction('after_setup_theme')] public function setup(): void { add_theme_support('post-thumbnails'); add_theme_support('title-tag'); } #[AsFilter('excerpt_length')] public function excerptLength(): int { return 30; } }
Post Types
Define custom post types as classes with automatic Timber classmap integration:
<?php use Studiometa\Foehn\Attributes\AsPostType; use Studiometa\Foehn\Models\Post; #[AsPostType( name: 'product', singular: 'Product', plural: 'Products', public: true, hasArchive: true, menuIcon: 'dashicons-cart', supports: ['title', 'editor', 'thumbnail'], )] final class Product extends Post { public function price(): ?float { return $this->meta('price') ? (float) $this->meta('price') : null; } }
Taxonomies
<?php use Studiometa\Foehn\Attributes\AsTaxonomy; #[AsTaxonomy( name: 'product_category', singular: 'Category', plural: 'Categories', postTypes: ['product'], hierarchical: true, )] final class ProductCategory {}
ACF Blocks
Create ACF blocks with dependency injection:
<?php use Studiometa\Foehn\Attributes\AsAcfBlock; use Studiometa\Foehn\Contracts\AcfBlockInterface; use Studiometa\Foehn\Contracts\ViewEngineInterface; use StoutLogic\AcfBuilder\FieldsBuilder; #[AsAcfBlock( name: 'hero', title: 'Hero Banner', category: 'layout', icon: 'cover-image', )] final readonly class HeroBlock implements AcfBlockInterface { public function __construct( private ViewEngineInterface $view, ) {} public static function fields(): FieldsBuilder { return (new FieldsBuilder('hero')) ->addImage('background') ->addWysiwyg('content') ->addLink('cta'); } public function compose(array $block, array $fields): array { return [ 'background' => $fields['background'] ?? null, 'content' => $fields['content'] ?? '', 'cta' => $fields['cta'] ?? null, ]; } public function render(array $context, bool $isPreview = false): string { return $this->view->render('blocks/hero', $context); } }
Typed DTOs for Block Context
Instead of returning plain arrays from compose(), use typed DTOs for autocompletion and type safety:
<?php use Studiometa\Foehn\Concerns\HasToArray; use Studiometa\Foehn\Contracts\Arrayable; use Studiometa\Foehn\Data\ImageData; use Studiometa\Foehn\Data\LinkData; final readonly class HeroContext implements Arrayable { use HasToArray; public function __construct( public string $title, public ?ImageData $background = null, public ?LinkData $cta = null, public string $height = 'medium', ) {} }
Then use it in your block:
public function compose(array $block, array $fields): HeroContext { return new HeroContext( title: $fields['title'] ?? '', background: ImageData::fromAttachmentId($fields['background'] ?? null), cta: LinkData::fromAcf($fields['cta_link'] ?? null), height: $fields['height'] ?? 'medium', ); }
The DTO is automatically flattened to a snake_case array before reaching render() and Twig templates. Property names like imageUrl become image_url in the template context.
Built-in DTOs:
| DTO | Description | Factory |
|---|---|---|
LinkData |
Link/button fields | LinkData::fromAcf($acfLink) |
ImageData |
Image/attachment fields | ImageData::fromAttachmentId($id) |
SpacingData |
Spacing fields | SpacingData::fromAcf($fields, $prefix) |
All compose() methods on AcfBlockInterface, BlockInterface and BlockPatternInterface accept either array or Arrayable return types.
Native Gutenberg Blocks with Interactivity API
<?php use Studiometa\Foehn\Attributes\AsBlock; use Studiometa\Foehn\Contracts\InteractiveBlockInterface; use WP_Block; #[AsBlock( name: 'theme/accordion', title: 'Accordion', category: 'widgets', interactivity: true, )] final readonly class AccordionBlock implements InteractiveBlockInterface { public static function attributes(): array { return [ 'items' => ['type' => 'array', 'default' => []], 'allowMultiple' => ['type' => 'boolean', 'default' => false], ]; } public static function initialState(): array { return []; } public function initialContext(array $attributes): array { return [ 'openItems' => [], 'allowMultiple' => $attributes['allowMultiple'] ?? false, ]; } public function render(array $attributes, string $content, WP_Block $block): string { // ... } }
Context Providers
Inject data into specific templates:
<?php use Studiometa\Foehn\Attributes\AsContextProvider; use Studiometa\Foehn\Contracts\ContextProviderInterface; #[AsContextProvider(templates: ['single', 'single-*'])] final readonly class SingleContext implements ContextProviderInterface { public function provide(array $context): array { $post = $context['post'] ?? null; $context['reading_time'] = $this->calculateReadingTime($post->content()); $context['related_posts'] = $this->getRelatedPosts($post); return $context; } }
Template Controllers
Handle template rendering with full control:
<?php use Studiometa\Foehn\Attributes\AsTemplateController; use Studiometa\Foehn\Contracts\ViewEngineInterface; use Timber\Timber; #[AsTemplateController('single', 'single-*')] final readonly class SingleController { public function __construct( private ViewEngineInterface $view, ) {} public function __invoke(): string { $context = Timber::context(); return $this->view->renderFirst([ "pages/single-{$context['post']->post_type}", 'pages/single', ], $context); } }
Block Patterns
Register block patterns with Twig templates:
<?php use Studiometa\Foehn\Attributes\AsBlockPattern; use Studiometa\Foehn\Contracts\BlockPatternInterface; #[AsBlockPattern( name: 'theme/hero-full-width', title: 'Hero Full Width', categories: ['heroes'], )] final readonly class HeroFullWidth implements BlockPatternInterface { public function compose(): array { return [ 'heading' => __('Welcome', 'theme'), 'cta_text' => __('Learn more', 'theme'), ]; } }
REST API Routes
<?php use Studiometa\Foehn\Attributes\AsRestRoute; use WP_REST_Request; final class ProductsApi { #[AsRestRoute('theme/v1', '/products', methods: ['GET'])] public function index(WP_REST_Request $request): array { return ['products' => []]; } #[AsRestRoute('theme/v1', '/products/(?P<id>\d+)', methods: ['GET'])] public function show(WP_REST_Request $request): array { return ['product' => []]; } }
Shortcodes
<?php use Studiometa\Foehn\Attributes\AsShortcode; final class ButtonShortcode { #[AsShortcode('button')] public function render(array $atts, ?string $content = null): string { $atts = shortcode_atts([ 'url' => '#', 'style' => 'primary', ], $atts); return sprintf( '<a href="%s" class="btn btn--%s">%s</a>', esc_url($atts['url']), esc_attr($atts['style']), esc_html($content) ); } }
CLI Commands
Føhn provides WP-CLI commands for scaffolding:
# Generate a new block wp foehn make:block Hero --acf # Generate a new post type wp foehn make:post-type Product # Generate a new taxonomy wp foehn make:taxonomy ProductCategory --post-types=product # Clear discovery cache wp foehn discovery:clear # Warm discovery cache wp foehn discovery:cache
Dependency Injection
All discovered classes support constructor injection via Tempest's container:
<?php use Studiometa\Foehn\Attributes\AsAction; final readonly class NewsletterHooks { public function __construct( private NewsletterService $newsletter, private LoggerInterface $logger, ) {} #[AsAction('user_register')] public function onUserRegister(int $userId): void { $user = get_user_by('id', $userId); $this->newsletter->subscribe($user->user_email); $this->logger->info('User subscribed to newsletter', ['user_id' => $userId]); } }
Configuration
Føhn can be configured in your theme:
<?php // config/foehn.php return [ 'cache' => [ 'enabled' => wp_get_environment_type() === 'production', 'path' => get_template_directory() . '/storage/cache', ], 'views' => [ 'paths' => ['templates'], ], 'blocks' => [ 'namespace' => 'theme', ], ];
Documentation
For complete documentation, see the Føhn documentation.
License
MIT License. See LICENSE for details.
studiometa/foehn 适用场景与选型建议
studiometa/foehn 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 13 次下载、GitHub Stars 达 4, 最近一次更新时间为 2026 年 02 月 04 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「framework」 「wordpress」 「attributes」 「blocks」 「timber」 「discovery」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 studiometa/foehn 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 studiometa/foehn 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 studiometa/foehn 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
PHP Framework HLEB2 is the foundation of the web application. Provides ease of development and application performance.
A library that allows you to easily use the PHP-VCR library in your PHPUnit tests.
This Package provides some usefully console features like the attribute syntax for arguments and options, validation, auto ask and casting.
The last validation library you will ever need!
A PHPDoc docblock interpreter that is simple, easy to use, and provides Attribute alternatives.
Module with SP files for attribute_check
统计信息
- 总下载量: 13
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 4
- 点击次数: 45
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-02-04