Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Framework Laravel Package

topthink/framework

ThinkPHP is a fast, simple MVC PHP framework for web development, offering routing, ORM, caching, templating, and a rich toolkit for building applications quickly. This package is its core framework.

View on GitHub
Deep Wiki
Context7

Getting Started

ThinkPHP is a full-stack MVC framework; topthink/framework is the core runtime (container, HTTP kernel, router, middleware pipeline, config, events, logging, cache/session managers, validation). The ORM/model layer comes from topthink/think-orm, templating from topthink/think-view + think-template, and multi-app / tracing / captcha each ship as separate topthink/* packages.

Do not install the core into an empty project. Use the application skeleton, which pins a compatible framework version plus the app/, config/, route/ scaffolding:

composer create-project topthink/think tp
cd tp && php think run          # http://localhost:8000

If you are wiring your own bootstrap, the entry point is public/index.php:

require __DIR__ . '/../vendor/autoload.php';
$http = (new \think\App())->http;
$response = $http->run();
$response->send();
$http->end($response);

Look here first, in this order:

  1. public/index.php — kernel boot.
  2. app/ — BaseController, middleware.php, provider.php, event.php, service.php, common.php.
  3. route/app.php — where requests land.
  4. config/app.php, config/database.php, .env (loaded into env() / Config).
  5. Then read five core classes: think\App, think\Http, think\Route, think\Middleware, think\Request. They explain ~80% of framework behavior.

First use case — a route, a controller, a DB query:

// route/app.php
use think\facade\Route;
Route::get('hello/:name', function ($name) { return 'Hello, ' . $name; });
Route::get('users', 'User/index');
// app/controller/User.php
namespace app\controller;
use app\BaseController;
use think\facade\Db;

class User extends BaseController
{
    public function index()
    {
        return json(Db::name('user')->where('status', 1)->order('id', 'desc')->paginate(15));
    }
}

Scaffold the rest instead of typing it: php think make:controller Blog, make:model Blog, make:middleware Auth, make:validate Blog, make:command Sync.

Implementation Patterns

Routing: groups, middleware, resources. Route files are plain PHP returning nothing — you call Route::get/post/rule for their side effects. Attach middleware at the group level rather than repeating it:

Route::group('api/v1', function () {
    Route::get('posts/:id', 'Post/read')->pattern(['id' => '\d+']);
    Route::resource('posts', 'Post');           // index/create/save/read/edit/update/delete
})->middleware(\app\middleware\Auth::class);
Route::miss(fn() => json(['code' => 404], 404));

Middleware is the primary extension seam. One class, one method — keep it thin and let the controller own business logic:

class Auth
{
    public function handle(Request $request, Closure $next): Response
    {
        if (!$request->header('authorization')) {
            return json(['code' => 401], 401);
        }
        $request->uid = 1;              // stash context on the request
        return $next($request);
    }
}

Register global middleware in app/middleware.php; route/controller-level middleware is appended after the globals.

Two data-access styles — pick deliberately. Db::name() for reporting/aggregation and bulk work; models for anything with behavior:

Db::name('order')->whereBetween('created_at', $range)->group('status')->column('count(*)', 'status');
Db::transaction(function () use ($data) { Order::create($data); Stock::where('id', 1)->dec('qty')->update(); });
class Order extends \think\Model
{
    use \think\model\concern\SoftDelete;
    protected $autoWriteTimestamp = true;
    public function items() { return $this->hasMany(OrderItem::class); }
}
Order::with(['items'])->where('user_id', $uid)->select();

Validation lives beside the controller, not inside it. Use a dedicated Validate class with scenes so create/update rules stay separate:

$validate = Validate::rule([
    'title' => 'require|max:100',
    'email' => 'require|email|unique:user',
])->message(['title.require' => '标题必填']);
if (!$validate->scene('create')->check($data)) {
    return json(['msg' => $validate->getError()], 422);
}

Services for anything reusable. A think\Service gives you register() (bindings, before boot) and boot() (runs after all providers are loaded — safe for event listeners and route macros). List it in app/service.php; interface bindings go in app/provider.php.

Events decouple side effects (mail, audit, cache invalidation) from controllers: register in app/event.php, fire with Event::trigger('order.paid', $order), listen with Event::listen('order.paid', SendReceipt::class).

Multi-app for admin/api splits: composer require topthink/think-multi-app, then app/index, app/admin, each with its own controller/, route/, middleware.php. Route files move under the app directory — forgetting this is the usual "routes stopped working" cause.

Config and env discipline. .env → env('DB_HOST'); everything else → config('database.connections.mysql.host'). Never call env() outside config files if you plan to enable config caching.

Gotchas and Tips

  • Config cache silently ignores your edits. php think optimize (the consolidated command introduced in 8.1.4) or the granular optimize:config / optimize:route / optimize:schema write into runtime/; after changing config/, .env or the schema you must php think clear. The schema cache is the nastiest: a newly added column returns null until cleared.
  • Db::name() applies the table prefix, Db::table() does not. Mixing them produces "table not found" in production where the prefix differs from dev.
  • Request::param() merges route, get and post variables and applies the global filter. When precedence matters, be explicit: $request->get(), $request->post(), $request->route().
  • Facades are static proxies over the container — they only work after App::initialize(). In PHPUnit, queue workers, or standalone scripts you must bootstrap: $app = new \think\App(); $app->initialize(); before touching Db, Config, or Cache.
  • paginate() needs a live request object to build links; in CLI/jobs call paginate(15, false) or use ->each(), otherwise you get an exception or a bogus page size.
  • Middleware order is global → group → route, and $next($request) must be returned, not just called. A middleware that returns nothing yields an empty response with a 200. If you pass parameters through group middleware (->middleware(Auth::class, 'admin')), keep the framework at ≥ 8.1.4 — earlier 8.1.x had argument-forwarding bugs there.
  • URL case conversion. With url_convert on (default), paths are lowercased; CamelCase controllers must be addressed as camel_case. This bites hardest when porting REST clients between ThinkPHP and other frameworks.
  • Route → namespace binding + controller suffix is supported, so Route::rule('blog/:id', 'Blog/read') still resolves when you configure a controller_suffix. Verify generated URLs after enabling a suffix — URL generation was itself patched in 8.1.4, so keep topthink/framework locked to a single 8.1.x patch level rather than a floating ^8.1.
  • find() returns null, findOrFail() throws — pick per layer, and let app/ExceptionHandle.php shape the JSON error envelope rather than try/catching everywhere.
  • ORM vs query builder parity gaps. Model events, auto timestamps, soft delete and getters/mutators do not apply to Db::name() calls. Bulk update() skips model events entirely.
  • Debug tooling: app_debug=true in .env gives verbose exception pages; topthink/think-trace adds the bottom-of-page panel (queries, timing, loaded files); dump()/trace() helpers work anywhere. Turn both off in production.
  • Extension points worth knowing: think\Service::boot(), route macros (Route::macro()), custom think\Facade subclasses with getFacadeClass(), custom validators, custom console commands in config/console.php, and container bindings in app/provider.php. think\Config's hook/lazy hooks gained a key argument in 8.1.4 — relevant only if you subclass or hook Config directly.
  • PHP 8.5 note. 8.1.4 is the first patch that declares PHP 8.5 compatibility; on 8.1.0–8.1.3 you may hit deprecation noise or fatal errors on 8.5. Also, psr/http-message ^2.0 is now allowed, which unblocks Composer conflicts with PSR-7/PSR-17 libraries that already require v2 — worth upgrading for that alone if you interop with a PSR-7 stack.
  • Response/HTTP teardown. Response::send() and the HttpEnd error-logging path were hardened in 8.1.4; if you wrap $http->run() in your own try/catch (custom entry point, Swoole worker), upgrade rather than patching around the old exception swallowing.
  • Licensing: the package metadata reports NOASSERTION, while the project is Apache-2.0. Run your own license scan before shipping into a compliance-gated codebase.
  • Docs are predominantly Chinese (doc.thinkphp.cn); the English site lags on 6.x/7.x/8.x details. Budget time for that, and pin the framework patch version — the 6.x → 8.x jump removed/changed APIs (annotations, think\Facade helpers) in ways that break copy-pasted snippets, and even within 8.1 the router URL generation and middleware argument handling changed.
  • Dependents count of 0 is a Packagist artifact — almost everyone installs via topthink/think, not the core directly. Don't read it as a signal of adoption.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle