在PHP中将类连接在一起?
我已经为我的项目创建了一个基本结构,并找到了将类连接在一起的方法,我创建了一个以前加载的类数组,并从缓存中调用它们。然后我有一个保存缓存的静态类,当我需要一个类时,我调用这个静态类 我觉得有更好的方法来做这件事,也许我做错了什么?其他PHP应用程序如何做到这一点?我对它还很陌生,所以我想知道是否有人能给我一个更好的方法来做这件事?感觉可以大大改进 当前目录结构 静态类,我使用它来获取我的类(提供者,我称之为提供者)在PHP中将类连接在一起?,php,directory-structure,Php,Directory Structure,我已经为我的项目创建了一个基本结构,并找到了将类连接在一起的方法,我创建了一个以前加载的类数组,并从缓存中调用它们。然后我有一个保存缓存的静态类,当我需要一个类时,我调用这个静态类 我觉得有更好的方法来做这件事,也许我做错了什么?其他PHP应用程序如何做到这一点?我对它还很陌生,所以我想知道是否有人能给我一个更好的方法来做这件事?感觉可以大大改进 当前目录结构 静态类,我使用它来获取我的类(提供者,我称之为提供者) 静态、单例、全局 我很抱歉让您失望,因为我相信您在代码方面投入了大量精力,。。
静态、单例、全局
我很抱歉让您失望,因为我相信您在代码方面投入了大量精力,。。。但是您可以或者应该考虑从不使用任何静态方法(也不使用单例或全局变量)开始。并提出了一些原因
依赖注入(DI)
在一个类中,不要使用另一个类的方法来创建实例。如果您需要任何类型的对象,请将其注入构造函数或任何其他需要它的方法中。这称为依赖项注入。见和
因此,您应该有以下内容(我将在下面进一步更改):
依赖注入容器(DIC)
负责注入依赖项(如布线)的结构称为(DIC)。例如:,或)。它仅在应用程序的入口点构造和使用(如index.php或bootstrap.php)。您将在其中注册您的应用程序依赖项,并且它将自动将它们注入到所有应用程序中。然后,您的应用程序类将是多余的。您的应用程序将“专注”于其实际用途,而不是创建和/或实现其完成任务所需的结构。相关术语为(IoC)
服务定位器
注意:不要将DIC作为依赖项传递。如果这样做,您就是在“创建”所谓的服务定位器。这意味着您的类将依赖容器来获取(“定位”)它的一些依赖项,或者通过容器运行它的一些进程
紧密耦合
与前一点类似:不要在类内使用new
关键字创建对象,因为这样会使类依赖于另一个-紧耦合。看见解决方案与上面相同:依赖项注入
顺便说一句,在您的代码中,您也将每个控制器耦合到应用程序类。如前所述,这应该避免
因此,在Template类中,您只需拥有:
class Template {
private $environment;
public function __construct(Twig_Environment $environment) {
$this->environment = $environment;
}
public function render($template, array $vars) {
return $this->environment->render($template, $vars);
}
}
并让细枝环境的创建在代码中的其他位置执行(例如通过DIC)。请注意return语句,而不是echo:让呈现内容的输出发生在更高的级别上,例如在引导级别(如index.php或bootstrap.php)-请参阅下面的最后一段代码
主要类的库
话虽如此,您可以在项目根niveau上拥有一个文件夹(如库或框架),其中可以包含以下所有核心类:模板、视图、模型层构造(如数据映射器、存储库、db适配器、服务等)、配置、会话、HTTP相关类(请求、响应、流、Uri等)。见和
路由
我在您的结构中看到一个routes.php文件。因此,我假设您已经熟悉这样一个事实:将URI(传递到客户端(浏览器))转换(解析)为包含特定控制器操作(包括操作参数)信息的路由对象是路由器的责任(最有可能由更多类组成:路由、调度器、路由器等)。实现示例:
前端控制器(请求调度)
分派请求的任务,例如在预定义的routes集合(基于提供的URI)中查找并最终创建route对象,并调用控制器操作(使用操作参数作为参数),属于所谓的前端控制器。它将作为依赖项接收路由器和调度器。简言之,此前端控制器从路由器获取路由对象,并将其信息传递给负责调用操作的调度器。例如:
class FrontController {
private $router;
private $dispatcher;
public function __construct(Router $router, Dispatcher $dispatcher) {
$this->router = $router;
$this->dispatcher = $dispatcher;
}
public function routeAndDispatch(ServerRequestInterface $request, ResponseInterface $response) {
$route = $this->router->route($request->getMethod(), $request->getUri()->getPath());
$this->dispatcher->dispatch($route, $request, $response);
return $this;
}
}
关于这个主题的一个很好的教程将在中介绍,以及后续部分
HTTP消息抽象(PSR-7)
考虑到基于请求和响应的web应用程序的性质,您应该熟悉一下该建议。它提供了HTTP消息的抽象,由HTTP请求和HTTP响应组成。一个好的独立库是
名称空间与文件系统结构(PSR-4)
关于文件系统/名称空间结构:根据建议,其中包含类的文件应与类同名。此外,如果使用导入(use
关键字),则不必在代码的其他位置也使用完全限定的类名。因此,correct类似于:
namespace App\Controllers\Frontend\Guest;
use App\App;
App::getProvider(...)->...;
控制器和视图-1:1关系
请注意,视图控制器关系可以是1:1。然后将分别调用它们的操作方法(在前控制器中):
第二个关注点分离-MVC目标
为了进一步分离任务,使用,作为域对象和数据库(或通常是持久层)之间的中介。例如,为了在域对象和
class LoginController {
private $template;
__construct(Template $template) {
$this->template = $template;
}
public function getView() {
$this->template->renderTemplate('index.html');
}
}
class Template {
private $environment;
public function __construct(Twig_Environment $environment) {
$this->environment = $environment;
}
public function render($template, array $vars) {
return $this->environment->render($template, $vars);
}
}
class FrontController {
private $router;
private $dispatcher;
public function __construct(Router $router, Dispatcher $dispatcher) {
$this->router = $router;
$this->dispatcher = $dispatcher;
}
public function routeAndDispatch(ServerRequestInterface $request, ResponseInterface $response) {
$route = $this->router->route($request->getMethod(), $request->getUri()->getPath());
$this->dispatcher->dispatch($route, $request, $response);
return $this;
}
}
namespace App\Controllers\Frontend\Guest;
use App\App;
App::getProvider(...)->...;
// Call the controller action.
call_user_func_array([$controller, "login"], <action-params>);
// Call the view action.
call_user_func_array([$view, "login"], <action-params>);
// Call the output method of the view and print.
echo call_user_func_array([$view, "output"]);
class PdoAdapter implements AdapterInterface {
private $connection;
public function __construct(PDO $connection) {
$this->connection = $connection;
}
public function fetchColumn(string $sql, array $bindings = [], int $columnNumber = 0) {
$statement = $this->connection->prepare($sql);
$statement->execute($bindings);
return $statement->fetchColumn($columnNumber);
}
}
class LoginController {
private $user;
public function __construct(UserInterface $user) {
$this->user = $user;
}
public function login($name, $password) {
$this->user->setName($name);
$this->user->setPassword($password);
}
}
class User implements UserInterface {
private $adapter;
private $name;
private $password;
public function __construct(AdapterInterface $adapter) {
$this->adapter = $adapter;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getPassword() {
return $this->password;
}
public function setPassword($password) {
$this->password = $password;
}
public function checkLoggedIn($name, $password) {
$sql = 'SELECT COUNT(*) FROM users WHERE name=:name AND password=:password LIMIT 1';
$bindings = [
':name' => $name,
':password' => $password,
];
return $this->adapter->fetchColumn($sql, $bindings) > 0;
}
}
class LoginView {
private $user;
private $template;
public function __construct(UserInterface $user, Template $template) {
$this->user = $user;
$this->template = $template;
}
public function login() {
//...
}
public function output() {
return $this->template->render('index.html', [
'loggedIn' => $this->user->checkLoggedIn(
$this->user->getName()
, $this->user->getPassword()
),
]);
}
}
<?php
class PdoAdapter implements AdapterInterface {
// ... The same ...
}
class UserMapper implements UserMapperInterface {
private $adapter;
public function __construct(AdapterInterface $adapter) {
$this->adapter = $adapter;
}
public function checkLoggedIn($name, $password) {
$sql = 'SELECT COUNT(*) FROM users WHERE name=:name AND password=:password LIMIT 1';
$bindings = [
':name' => $name,
':password' => $password,
];
return $this->adapter->fetchColumn($sql, $bindings) > 0;
}
}
class LoginController {
private $user;
private $mapper;
public function __construct(UserInterface $user, UserMapperInterface $mapper) {
$this->user = $user;
$this->mapper = $mapper;
}
public function login($name, $password) {
$this->user->setName($name);
$this->user->setPassword($password);
}
}
class User implements UserInterface {
private $name;
private $password;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getPassword() {
return $this->password;
}
public function setPassword($password) {
$this->password = $password;
}
}
class LoginView {
private $user;
private $mapper;
private $template;
public function __construct(UserInterface $user, UserMapperInterface $mapper, Template $template) {
$this->user = $user;
$this->mapper = $mapper;
$this->template = $template;
}
public function login() {
//...
}
public function output() {
$loggedIn = $this->mapper->checkLoggedIn(
$this->user->getName()
, $this->user->getPassword()
);
return $this->template->render('index.html', [
'loggedIn' => $loggedIn
]);
}
}
<?php
return [
'twig' => [
'options' => [
'debug' => TRUE,
'charset' => 'UTF-8',
'base_template_class' => 'Twig_Template',
'strict_variables' => FALSE,
'autoescape' => 'html',
'cache' => __DIR__ . '/../../storage/cache/twig',
'auto_reload' => TRUE,
'optimizations' => -1,
],
'extensions' => [/* Indexed array with Twig_Extension_xxx instances */
new Twig_Extension_Debug(),
],
],
];
<?php
// Here come the imports...
return [
'router' => function (ContainerInterface $c) {
$dispatcher = 'Here create the route dispatcher...';
return new Router($dispatcher);
},
'request' => factory([ServerRequestFactory::class, 'build']), /* Built with defaults */
'response' => function (ContainerInterface $c) {
return new Response(new Stream() /* defaults */);
},
'twigEnvironment' => function (ContainerInterface $c) {
$loader = new Twig_Loader_Filesystem();
$options = $c->get('twig')['options'];
$environment = new Twig_Environment($loader, $options);
$extensions = $c->get('twig')['extensions'];
foreach ($extensions as $extension) {
$environment->addExtension($extension);
}
return $environment;
},
];
<?php
use function DI\get;
use function DI\object;
use Mymvc\Models\User\User;
use Mymvc\Views\Login\LoginView;
use Mymvc\Models\User\UserMapper;
use Mymvc\Controllers\Login\LoginController;
return [
LoginController::class => object()
->constructorParameter('user', get(User::class))
->constructorParameter('mapper', get(UserMapper::class)),
LoginView::class => object()
->constructorParameter('user', get(User::class))
->constructorParameter('mapper', get(UserMapper::class)),
];
<?php
namespace Mymvc\Controllers\Login;
use Mymvc\Models\User\UserInterface;
use Mymvc\Models\User\UserMapperInterface;
class LoginController {
public function __construct(UserInterface $user, UserMapperInterface $mapper) {
//...
}
}
<?php
namespace Mymvc\Views\Login;
use Mylib\Presentation\Template;
use Mymvc\Models\User\UserInterface;
use Mymvc\Models\User\UserMapperInterface;
class LoginView {
public function __construct(UserInterface $user, UserMapperInterface $mapper, Template $template) {
//...
}
}
<?php require_once '../bootstrap.php'; ?>
$container->set(ServerRequestInterface::class, $request);
$container->set(ResponseInterface::class, $response);
<?php
return [
'paths' => [
// This folder contains the templates and the layouts of the application, used by your view classes if no "specific" layouts/templates are used.
'appTemplates' => __DIR__ . '/../../resources/templates/app'
// This folder contains the templates and the layouts loaded/rendered by the specific view classes (LoginView, HomeView, AboutUsVIew, etc).
'customTemplates' => __DIR__ . '/../../resources/templates/app'
],
];
$appTemplatesPath = $container->get('appTemplates');
$customTemplatesPath = $container->get('customTemplates');
$twigEnvironment = $container->get('twigEnvironment');
$template = new Template(
$twigEnvironment
, $appTemplatesPath
, $customTemplatesPath
);
$container->set(Template::class, $template);