PHP code example of thee-prime / wpflint
1. Go to this page and download the library: Download thee-prime/wpflint library . Choose the download type require .
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
thee-prime / wpflint example snippets
/**
* Plugin Name: My Shop
* Text Domain: my-shop
*/
declare(strict_types=1);
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
fecycle::for( __FILE__ )
->on_activate( function () { \MyShop\Installer::activate(); } )
->on_deactivate( function () { \MyShop\Installer::deactivate(); } )
->on_uninstall( \MyShop\Installer::class )
->register();
$app = Application::get_instance( __DIR__ );
$app->register( MyShop\Providers\ShopServiceProvider::class );
$app->bootstrap();
use WPFlint\Application;
$app = Application::get_instance( __DIR__ ); // get or create singleton
$app->register( MyServiceProvider::class ); // register a provider
$app->bootstrap(); // hook into WP lifecycle
$app->base_path(); // plugin root directory
$app->base_path( 'config/db.php' ); // append a path
$app->is_booted(); // bool
// Binding
$app->bind( LoggerInterface::class, FileLogger::class );
$app->singleton( CacheManager::class, fn( $app ) => new CacheManager( $app ) );
$app->instance( 'config', $config );
// Resolution
$service = $app->make( OrderService::class ); // auto-resolves constructor deps
$service = $app->get( OrderService::class ); // PSR-11 alias
// Contextual bindings
$app->when( OrderService::class )
->needs( LoggerInterface::class )
->give( OrderLogger::class );
use WPFlint\Providers\ServiceProvider;
class ShopServiceProvider extends ServiceProvider {
public function register(): void {
$this->app->singleton( OrderService::class, fn( $app ) => new OrderService( $app ) );
}
public function boot(): void {
// Register routes, hooks, assets, etc.
}
}
class PaymentServiceProvider extends ServiceProvider {
public bool $defer = true;
public function register(): void { ... }
public function provides(): array { return array( PaymentGateway::class ); }
}
use WPFlint\Facades\Config;
// config/app.php returns an array — filename is the top-level key.
Config::get( 'app.name' );
Config::get( 'app.missing', 'default' );
Config::set( 'app.debug', true );
Config::has( 'app.name' );
Config::push( 'app.middleware', 'throttle' );
use WPFlint\Facades\Cache;
use WPFlint\Facades\Event;
use WPFlint\Facades\Config;
Cache::remember( 'key', 3600, fn() => Order::all() );
Event::fire( new OrderPlaced( $order ) );
Config::get( 'app.name' );
class Orders extends Facade {
protected static function get_facade_accessor(): string { return 'orders'; }
}
use WPFlint\Http\Router;
$router = $app->make( Router::class );
// AJAX routes
$router->ajax( 'my-shop/save-order', array( OrderController::class, 'store' ) )
->middleware( array( 'nonce:save_order', 'can:edit_posts' ) );
$router->ajax( 'my-shop/products', array( ProductController::class, 'index' ) )
->nopriv()
->middleware( array( 'throttle:60,1' ) );
// REST API routes
$router->rest( 'my-shop/v1', function ( RestRouter $r ) {
$r->get( '/orders', array( OrderRestController::class, 'index' ) );
$r->post( '/orders', array( OrderRestController::class, 'store' ) );
$r->get( '/orders/(?P<id>\d+)', array( OrderRestController::class, 'show' ) );
$r->put( '/orders/(?P<id>\d+)', array( OrderRestController::class, 'update' ) );
$r->delete( '/orders/(?P<id>\d+)', array( OrderRestController::class, 'destroy' ) );
} );
class EnsureStoreIsOpen implements MiddlewareInterface {
public function handle( Request $request, Closure $next ) {
if ( ! get_option( 'store_open' ) ) {
return Response::error( 'Store is closed.', 403 );
}
return $next( $request );
}
}
class OrderController extends Controller {
public function __construct( private OrderService $orders ) {}
public function store( StoreOrderRequest $request ): Response {
// $request already validated + sanitized
$order = $this->orders->create( $request->validated() );
return Response::json( $order->to_array(), 201 );
}
}
class OrderRestController extends RestController {
protected string $namespace = 'my-shop/v1';
protected string $rest_base = 'orders';
public function index( \WP_REST_Request $req ): \WP_REST_Response {
return $this->respond( Order::all() );
}
}
class StoreOrderRequest extends Request {
public function authorize(): bool { return current_user_can( 'edit_posts' ); }
public function rules(): array {
return array(
'status' => 'd', 'total' => 'floatval' );
}
}
Response::json( array( 'order' => $order ), 201 );
Response::error( 'Not found.', 404 );
Response::no_content();
$response->with_header( 'X-Custom', 'value' );
$response->send_ajax();
$response->to_rest();
use WPFlint\Http\RestAuth;
// In register_rest_route():
'permission_callback' => RestAuth::capability( 'manage_options' )
'permission_callback' => RestAuth::logged_in()
'permission_callback' => RestAuth::public_access()
'permission_callback' => RestAuth::all_of( 'edit_posts', 'upload_files' )
'permission_callback' => RestAuth::any_of( 'edit_posts', 'edit_pages' )
// Versioned namespace builder:
RestAuth::namespace( 'my-plugin', 1 ) // 'my-plugin/v1'
RestAuth::namespace( 'my-plugin', 2 ) // 'my-plugin/v2'
// Direct boolean checks:
RestAuth::
class CreateOrdersTable extends Migration {
public function up(): void {
$this->schema()->create( 'orders', function ( Blueprint $t ) {
$t->big_increments( 'id' );
$t->string( 'status' )->default( 'pending' );
$t->decimal( 'total', 10, 2 );
$t->timestamps();
} );
}
public function down(): void { $this->schema()->drop( 'orders' ); }
}
$migrator = new Migrator( new MigrationRepository( 'my-shop' ), array(
CreateOrdersTable::class,
) );
$migrator->run();
$table->big_increments( 'id' );
$table->string( 'email' );
$table->decimal( 'total', 10, 2 );
$table->boolean( 'active' );
$table->timestamps();
$table->soft_deletes();
$table->index( 'email' );
$table->unique( 'slug' );
$table->foreign( 'user_id' )->references( 'id' )->on( 'users' )->on_delete( 'CASCADE' );
QueryBuilder::table( 'orders' )
->where( 'status', 'pending' )
->where( 'total', '>', 100 )
->order_by( 'created_at', 'DESC' )
->limit( 10 )
->get();
class Order extends Model {
protected static string $table = 'orders';
protected array $fillable = array( 'status', 'total' );
protected array $casts = array( 'total' => 'float', 'meta' => 'array' );
public function scope_pending( ModelQueryBuilder $q ): ModelQueryBuilder {
return $q->where( 'status', 'pending' );
}
}
$order = Order::find( 1 );
$orders = Order::pending()->where( 'total', '>', 50 )->get_models();
$order = Order::create( array( 'status' => 'pending', 'total' => 99.50 ) );
$order = Order::cached( 42 ); // cached find (TTL 3600s)
class User extends Model {
public function orders(): HasMany { return $this->has_many( Order::class ); }
public function profile(): HasOne { return $this->has_one( Profile::class ); }
}
class Order extends Model {
public function user(): BelongsTo { return $this->belongs_to( User::class ); }
}
$orders = $user->orders()->get_results();
// Eager loading (prevents N+1):
$users = User::where( 'active', 1 )->with( array( 'orders', 'profile' ) )->get_models();
use WPFlint\Admin\AdminPage;
add_action( 'admin_menu', function () {
AdminPage::make( 'My Plugin', 'my-plugin' )
->capability( 'manage_options' )
->icon( 'dashicons-admin-tools' )
->position( 80 )
->render( function () {
View::make( 'admin.dashboard' )->output();
} )
->submenu( 'Settings', 'my-plugin-settings', function () {
View::make( 'admin.settings' )->output();
} )
->submenu( 'Tools', 'my-plugin-tools', function () {
View::make( 'admin.tools' )->output();
} )
->register();
} );
use WPFlint\Settings\Settings;
add_action( 'admin_init', function () {
Settings::make( 'my_plugin_options', 'my_plugin_settings' )
->page( 'my-plugin-settings' )
->section( 'general', 'General', function ( $s ) {
$s->field( 'api_key', 'API Key' )->type( 'text' )->ugin_settings', array() );
$api_key = $opts['api_key'] ?? '';
use WPFlint\Admin\Notice;
// Flash — shown once after redirect:
Notice::success( 'Settings saved.' )->dismissible()->flash();
Notice::error( 'Something went wrong.' )->flash();
// Persistent — shown until dismissed:
Notice::warning( 'API key missing.' )->persistent( 'my_plugin_api_key' );
Notice::dismiss( 'my_plugin_api_key' );
// Inline — inside your own admin_notices callback:
add_action( 'admin_notices', function () {
echo Notice::info( 'Plugin activated!' )->render();
} );
use WPFlint\Admin\MetaBox;
add_action( 'add_meta_boxes', function () {
$box = MetaBox::make( 'book_details', 'Book Details' )
->screen( 'book' )
->context( 'normal' )
->priority( 'high' );
$box->field( '_isbn', 'ISBN' )->type( 'text' )->description( 'e.g. 978-3-16-148410-0' );
$box->field( '_pages', 'Pages' )->type( 'number' );
$box->field( '_summary', 'Summary' )->type( 'textarea' );
$box->field( '_genre', 'Genre' )->type( 'select' )
->options( array( 'fiction' => 'Fiction', 'non-fiction' => 'Non-Fiction' ) );
$box->field( '_featured', 'Featured' )->type( 'checkbox' );
$box->register();
} );
// Read saved values anywhere:
$isbn = get_post_meta( $post->ID, '_isbn', true );
$pages = (int) get_post_meta( $post->ID, '_pages', true );
use WPFlint\Shortcodes\Shortcode;
Shortcode::make( 'my_cta' )
->defaults( array( 'text' => 'Get Started', 'url' => '#', 'style' => 'primary' ) )
->render( function ( array $atts, string $content ): string {
return sprintf(
'<a href="%s" class="btn btn-%s">%s</a>',
esc_url( $atts['url'] ),
esc_attr( $atts['style'] ),
esc_html( $atts['text'] )
);
} )
->register();
// With a View template:
Shortcode::make( 'pricing_table' )
->defaults( array( 'plan' => 'basic' ) )
->render( fn( $atts ) => View::make( 'shortcodes.pricing' )->with( $atts )->render() )
->register();
use WPFlint\Blocks\Block;
add_action( 'init', function () {
Block::make( 'my-plugin/hero' )
->title( 'Hero Section' )
->category( 'design' )
->editor_script( 'my-plugin-blocks' )
->attributes( array(
'heading' => array( 'type' => 'string', 'default' => 'Welcome' ),
'align' => array( 'type' => 'string', 'default' => 'center' ),
) )
->render( function ( array $attrs ): string {
return sprintf(
'<section class="hero hero--%s"><h1>%s</h1></section>',
esc_attr( $attrs['align'] ),
esc_html( $attrs['heading'] )
);
} )
->register();
} );
use WPFlint\Widgets\AbstractWidget;
class TestimonialWidget extends AbstractWidget {
protected string $widget_title = 'Testimonial';
protected string $description = 'Displays a customer quote.';
protected function output( array $args, array $instance ): void {
echo $args['before_widget'];
echo '<blockquote>' . esc_html( $instance['quote'] ?? '' ) . '</blockquote>';
echo $args['after_widget'];
}
protected function fields( array $instance ): void {
echo '<p><label>Quote:<br>
<textarea name="' . esc_attr( $this->get_field_name( 'quote' ) ) . '">'
. esc_textarea( $instance['quote'] ?? '' ) . '</textarea></label></p>';
}
protected function sanitize( array $new, array $old ): array {
return array( 'quote' => sanitize_textarea_field( $new['quote'] ?? '' ) );
}
}
// Register in service provider boot():
TestimonialWidget::register();
use WPFlint\View\View;
// Set base path once (or register ViewServiceProvider):
View::set_base_path( plugin_dir_path( __FILE__ ) . 'resources/views' );
// Render to string:
$html = View::make( 'admin.settings' )
->with( array( 'title' => 'Settings', 'options' => $opts ) )
->render();
// Output directly:
View::make( 'partials.notice' )->with( 'message', 'Saved!' )->output();
// Per-instance path override:
View::make( 'email.welcome' )->from( '/custom/path/views' )->render();
<div class="wrap">
<h1> echo esc_html( $title );
use WPFlint\Mail\Mail;
// Plain text:
Mail::to( '[email protected] ' )
->subject( 'Welcome!' )
->message( 'Thanks for signing up.' )
->send();
// HTML:
Mail::to( '[email protected] ' )
->subject( 'Order Confirmed' )
->from( '[email protected] ', 'My Shop' )
->html( '<h1>Order confirmed!</h1>' )
->cc( '[email protected] ' )
->send();
// PHP template:
Mail::to( $user->user_email )
->subject( 'Order #' . $order->id )
->from( get_option( 'admin_email' ), get_bloginfo( 'name' ) )
->template( 'emails.order-confirmed', array( 'order' => $order ) )
->attach( '/var/www/uploads/invoice.pdf' )
->send();
use WPFlint\Lifecycle\Lifecycle;
Lifecycle::for( __FILE__ )
->on_activate( function () {
global $wpdb;
// Create tables, set defaults...
} )
->on_deactivate( function () {
wp_clear_scheduled_hook( 'my_plugin_daily' );
} )
->on_uninstall( \MyPlugin\Uninstaller::class ) // must be a named class
->register();
use WPFlint\Assets\Script;
use WPFlint\Assets\Style;
// Direct enqueueing:
Script::make( 'my-app', plugin_dir_url( __FILE__ ) . 'app.js' )
->deps( array( 'jquery' ) )
->version( '1.0' )
->footer()
->localize( 'MyApp', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ) ) )
->only_on( 'is_admin' )
->enqueue();
Style::make( 'my-style', plugin_dir_url( __FILE__ ) . 'app.css' )
->version( '1.0' )
->only_on( fn() => is_page( 'shop' ) )
->enqueue();
// Via AssetManager (auto-hooked to wp_enqueue_scripts + admin_enqueue_scripts):
$manager = $app->make( \WPFlint\Assets\AssetManager::class );
$manager->script( 'my-app', '...' )->footer()->version( '1.0' );
$manager->style( 'my-style', '...' )->version( '1.0' );
use WPFlint\Events\Dispatcher;
use WPFlint\Facades\Event;
// Define an event:
class OrderPlaced extends \WPFlint\Events\Event {
public function __construct( public int $order_id, public float $total ) {}
}
// Listen:
Event::listen( OrderPlaced::class, SendConfirmationEmail::class );
Event::listen( OrderPlaced::class, function ( OrderPlaced $e ) {
error_log( 'Order placed: ' . $e->order_id );
} );
// Fire:
Event::fire( new OrderPlaced( 42, 99.50 ) );
// WordPress hook bridge:
Event::listen_wp( 'save_post', PostSaved::class );
use WPFlint\Facades\Cache;
Cache::put( 'key', 'value', 300 );
Cache::get( 'key', 'default' );
Cache::remember( 'all_orders', 3600, fn() => Order::all() );
Cache::forget( 'key' );
Cache::flush();
// Tags:
Cache::tags( 'orders' )->remember( 'order_list', 300, fn() => Order::all() );
Cache::tags( 'orders' )->flush();
// Bypass cache:
Cache::fresh()->remember( 'key', 3600, $callback );
use WPFlint\Logging\Logger;
$logger = new Logger( 'my-plugin', WP_CONTENT_DIR . '/logs/my-plugin.log' );
$logger->info( 'Order placed', array( 'order_id' => 42 ) );
$logger->warning( 'Low stock', array( 'product' => 'Widget', 'qty' => 2 ) );
$logger->error( 'Payment failed', array( 'error' => $e->getMessage() ) );
$logger->debug( 'Cache miss', array( 'key' => 'all_orders' ) );
use WPFlint\Queue\QueueManager;
class SendWelcomeEmail extends \WPFlint\Queue\Job {
public function __construct( private int $user_id ) {}
public function handle(): void {
$user = get_userdata( $this->user_id );
Mail::to( $user->user_email )->subject( 'Welcome!' )->send();
}
}
$queue = $app->make( QueueManager::class );
$queue->push( new SendWelcomeEmail( $user->ID ) );
$queue->push( new SendWelcomeEmail( $user->ID ), 300 ); // delay 5 min
use WPFlint\Scheduling\Scheduler;
$scheduler = $app->make( Scheduler::class );
$scheduler->call( fn() => Cache::flush() )
->daily()
->at( '02:00' );
$scheduler->call( fn() => $migrator->run() )
->weekly()
->on( 'monday' );
$scheduler->command( 'my-plugin/sync-products' )
->hourly();
use WP_Mock;
use WP_Mock\Tools\TestCase;
class MyFeatureTest extends TestCase {
public function setUp(): void {
WP_Mock::setUp();
}
public function tearDown(): void {
WP_Mock::tearDown();
}
public function test_something(): void {
WP_Mock::userFunction( 'get_option' )->andReturn( 'value' );
$this->assertSame( 'value', get_option( 'my_key' ) );
}
}
tests/
bootstrap.php
ApplicationTest.php
Container/ContainerTest.php
Http/RouterTest.php
Database/ORM/ModelTest.php
Cache/CacheManagerTest.php
Events/DispatcherTest.php
Admin/NoticeTest.php, MetaBoxTest.php, AdminTest.php
Assets/AssetTest.php
Blocks/BlockTest.php
Lifecycle/LifecycleTest.php
Mail/MailTest.php
Settings/SettingsTest.php
Shortcodes/ShortcodeTest.php
View/ViewTest.php
Widgets/WidgetTest.php
Http/RestAuthTest.php
...