在PHP中将类连接在一起?

在PHP中将类连接在一起?,php,directory-structure,Php,Directory Structure,我已经为我的项目创建了一个基本结构,并找到了将类连接在一起的方法,我创建了一个以前加载的类数组,并从缓存中调用它们。然后我有一个保存缓存的静态类,当我需要一个类时,我调用这个静态类 我觉得有更好的方法来做这件事,也许我做错了什么?其他PHP应用程序如何做到这一点?我对它还很陌生,所以我想知道是否有人能给我一个更好的方法来做这件事?感觉可以大大改进 当前目录结构 静态类,我使用它来获取我的类(提供者,我称之为提供者) 静态、单例、全局 我很抱歉让您失望,因为我相信您在代码方面投入了大量精力,。。

我已经为我的项目创建了一个基本结构,并找到了将类连接在一起的方法,我创建了一个以前加载的类数组,并从缓存中调用它们。然后我有一个保存缓存的静态类,当我需要一个类时,我调用这个静态类

我觉得有更好的方法来做这件事,也许我做错了什么?其他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);