Controller
Create a new controller file app/controller/FooController.php
.
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function index(Request $request)
{
return response('hello index');
}
public function hello(Request $request)
{
return response('hello webman');
}
}
When accessing http://127.0.0.1:8787/foo
, the page returns hello index
.
When accessing http://127.0.0.1:8787/foo/hello
, the page returns hello webman
.
Of course, you can change the routing rules through the route configuration, see Routing.
Tip
If a 404 error occurs and access is not available, please openconfig/app.php
, setcontroller_suffix
toController
, and restart.
Controller Suffix
Starting from version 1.3, Webman supports setting the controller suffix in config/app.php
. If controller_suffix
in config/app.php
is set to blank ''
, the controller will look like this:
app\controller\Foo.php
.
<?php
namespace app\controller;
use support\Request;
class Foo
{
public function index(Request $request)
{
return response('hello index');
}
public function hello(Request $request)
{
return response('hello webman');
}
}
It is strongly recommended to set the controller suffix to Controller
, as this avoids conflicts between controller and model class names, while enhancing security.
Description
- The framework automatically passes the
support\Request
object to the controller, which can be used to obtain user input data (like get, post, header, cookie, etc.), see Request. - The controller can return numbers, strings, or
support\Response
objects, but cannot return other types of data. - The
support\Response
object can be created using helper functions such asresponse()
,json()
,xml()
,jsonp()
,redirect()
, etc.
Controller Parameter Binding
Example
Webman supports automatic binding of request parameters to controller method parameters. For example:
<?php
namespace app\controller;
use support\Response;
class UserController
{
public function create(string $name, int $age): Response
{
return json(['name' => $name, 'age' => $age]);
}
}
You can pass the values of name
and age
through the GET
or POST
method, or via route parameters, for example:
Route::any('/user/{name}/{age}', [app\controller\UserController::class, 'create']);
The priority is route parameters
> GET
> POST
parameters.
Default Values
Suppose we access /user/create?name=tom
, we will get the following error:
Missing input parameter age
The reason is that we did not pass the age
parameter. This can be resolved by setting a default value for the parameter, for example:
<?php
namespace app\controller;
use support\Response;
class UserController
{
public function create(string $name, int $age = 18): Response
{
return json(['name' => $name, 'age' => $age]);
}
}
Parameter Types
When we access /user/create?name=tom&age=not_int
, we will get the following error:
Tip
For convenience in testing, we directly input parameters in the browser address bar. In actual development, parameters should be passed throughPOST
method.
Input age must be of type int, string given
This is because the incoming data will be converted according to type, and if conversion fails, a support\exception\InputTypeException
exception will be thrown. Since the passed age
parameter cannot be converted to int
, the above error occurs.
Custom Errors
We can use multilingual support to customize errors like Missing input parameter age
and Input age must be of type int, string given
, as referenced in the following command:
composer require symfony/translation
mkdir resource/translations/zh_CN/ -p
echo "<?php
return [
'Input :parameter must be of type :exceptType, :actualType given' => '输入参数 :parameter 必须是 :exceptType 类型,传递的类型是 :actualType',
'Missing input parameter :parameter' => '缺少输入参数 :parameter',
];" > resource/translations/zh_CN/messages.php
php start.php restart
Other Types
Webman supports parameter types such as int
, float
, string
, bool
, array
, object
, and class instances, for example:
<?php
namespace app\controller;
use support\Response;
class UserController
{
public function create(string $name, int $age, float $balance, bool $vip, array $extension): Response
{
return json([
'name' => $name,
'age' => $age,
'balance' => $balance,
'vip' => $vip,
'extension' => $extension,
]);
}
}
When we access /user/create?name=tom&age=18&balance=100.5&vip=true&extension[foo]=bar
, we will get the following result:
{
"name": "tom",
"age": 18,
"balance": 100.5,
"vip": true,
"extension": {
"foo": "bar"
}
}
Class Instances
Webman supports passing class instances through parameter type hints, for example:
app\service\Blog.php
<?php
namespace app\service;
class Blog
{
private $title;
private $content;
public function __construct(string $title, string $content)
{
$this->title = $title;
$this->content = $content;
}
public function get()
{
return [
'title' => $this->title,
'content' => $this->content,
];
}
}
app\controller\BlogController.php
<?php
namespace app\controller;
use app\service\Blog;
use support\Response;
class BlogController
{
public function create(Blog $blog): Response
{
return json($blog->get());
}
}
When accessing /blog/create?blog[title]=hello&blog[content]=world
, we will get the following result:
{
"title": "hello",
"content": "world"
}
Model Instances
app\model\User.php
<?php
namespace app\model;
use support\Model;
class User extends Model
{
protected $connection = 'mysql';
protected $table = 'user';
protected $primaryKey = 'id';
public $timestamps = false;
// Here you need to add fillable fields to prevent unsafe fields from being passed from the frontend
protected $fillable = ['name', 'age'];
}
app\controller\UserController.php
<?php
namespace app\controller;
use app\model\User;
class UserController
{
public function create(User $user): int
{
$user->save();
return $user->id;
}
}
When accessing /user/create?user[name]=tom&user[age]=18
, we will get a result similar to:
1
Controller Lifecycle
When controller_reuse
in config/app.php
is set to false
, a corresponding controller instance will be initialized for each request, and the controller instance will be destroyed after the request ends. This is similar to the operational mechanism of traditional frameworks.
When controller_reuse
in config/app.php
is set to true
, all requests will reuse the controller instance, meaning the controller instance stays in memory once created, and all requests reuse it.
Note
When controller reuse is enabled, requests should not change any properties of the controller, as these changes will affect subsequent requests. For example:
<?php
namespace app\controller;
use support\Request;
class FooController
{
protected $model;
public function update(Request $request, $id)
{
$model = $this->getModel($id);
$model->update();
return response('ok');
}
public function delete(Request $request, $id)
{
$model = $this->getModel($id);
$model->delete();
return response('ok');
}
protected function getModel($id)
{
// This method will retain the model upon the first request update?id=1
// If delete?id=2 is requested again, it will delete data of id 1
if (!$this->model) {
$this->model = Model::find($id);
}
return $this->model;
}
}
Tip
Returning data in the controller's__construct()
constructor function will have no effect, for example:
<?php
namespace app\controller;
use support\Request;
class FooController
{
public function __construct()
{
// Returning data in the constructor has no effect; the browser will not receive this response
return response('hello');
}
}
Difference Between Non-reuse and Reuse of Controllers
The differences are as follows:
Non-reuse of Controllers
A new controller instance is created for each request, and the instance is released after the request ends and the memory is reclaimed. Non-reuse of controllers is similar to traditional frameworks, aligning with most developers' habits. Due to the repeated creation and destruction of controllers, the performance may be slightly lower than that of reusing controllers (about 10% lower in helloworld performance tests, which can be largely ignored with business logic).
Reuse of Controllers
With reuse, a controller is instantiated only once per process, and this controller instance is not released after the request ends. Subsequent requests in the current process will reuse this instance. Reusing controllers provides better performance, but it does not conform to most developers' habits.
Situations Where Controller Reuse Cannot Be Used
Controller reuse cannot be enabled when the request modifies the properties of the controller, as these changes will affect subsequent requests.
Some developers prefer to do initialization for each request in the controller's constructor __construct()
, in which case controller reuse cannot be used, since the constructor will only be called once per process, not for each request.