前言:laravel作为一个纯面向对象框架,使用了很多优秀的设计模式
拥有某些共性的功能可以使用工厂模式进行封装,使得维护者维护便利,使用者使用便利
interface Factory
{
// 通过名字获取连接
public function connection($name = null);
}
class RedisManager implements Factory
{
// 通过名字获取连接
public function connection($name = null)
{
$name = $name ?: 'default';
if (isset($this->connections[$name])) {
return $this->connections[$name];
}
return $this->connections[$name] = $this->configure(
$this->resolve($name), $name
);
}
// 解析名字获得连接
public function resolve($name = null)
{
$name = $name ?: 'default';
$options = $this->config['options'] ?? [];
if (isset($this->config[$name])) {
return $this->connector()->connect($this->config[$name], $options);
}
throw new InvalidArgumentException("Redis connection [{$name}] not configured.");
}
// 通过驱动选择连接,这里提供了predis和phpredis选择
protected function connector()
{
switch ($this->driver) {
case 'predis':
return new Connectors\PredisConnector;
case 'phpredis':
return new Connectors\PhpRedisConnector;
}
}
}
interface ConnectionResolverInterface
{
public function connection($name = null);
}
class DatabaseManager implements ConnectionResolverInterface
{
public function connection($name = null)
{
[$database, $type] = $this->parseConnectionName($name);
$name = $name ?: $database;
// If we haven't created this connection, we'll create it based on the config
// provided in the application. Once we've created the connections we will
// set the "fetch mode" for PDO which determines the query return types.
if (! isset($this->connections[$name])) {
$this->connections[$name] = $this->configure(
$this->makeConnection($database), $type
);
}
return $this->connections[$name];
}
protected function makeConnection($name)
{
$config = $this->configuration($name);
// First we will check by the connection name to see if an extension has been
// registered specifically for that connection. If it has we will call the
// Closure and pass it the config allowing it to resolve the connection.
if (isset($this->extensions[$name])) {
return call_user_func($this->extensions[$name], $config, $name);
}
// Next we will check to see if an extension has been registered for a driver
// and will call the Closure if so, which allows us to have a more generic
// resolver for the drivers themselves which applies to all connections.
if (isset($this->extensions[$driver = $config['driver']])) {
return call_user_func($this->extensions[$driver], $config, $name);
}
return $this->factory->make($config, $name);
}
}
class ConnectionFactory
{
public function make(array $config, $name = null)
{
$config = $this->parseConfig($config, $name);
if (isset($config['read'])) {
return $this->createReadWriteConnection($config);
}
return $this->createSingleConnection($config);
}
public function createConnector(array $config)
{
if (! isset($config['driver'])) {
throw new InvalidArgumentException('A driver must be specified.');
}
if ($this->container->bound($key = "db.connector.{$config['driver']}")) {
return $this->container->make($key);
}
switch ($config['driver']) {
case 'mysql':
return new MySqlConnector;
case 'pgsql':
return new PostgresConnector;
case 'sqlite':
return new SQLiteConnector;
case 'sqlsrv':
return new SqlServerConnector;
}
throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]");
}
}
保证类只实例化一次,减少不必要的开销
class Container implements ArrayAccess, ContainerContract
{
protected static $instance;
public static function getInstance()
{
if (is_null(static::$instance)) {
static::$instance = new static;
}
return static::$instance;
}
}
类似于发布订阅
namespace Illuminate\Contracts\Events;
interface Dispatcher
{
public function listen($events, $listener);
public function dispatch($event, $payload = [], $halt = false);
}
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
class Dispatcher implements DispatcherContract
{
// 监听
public function listen($events, $listener)
{
foreach ((array) $events as $event) {
if (Str::contains($event, '*')) {
$this->setupWildcardListen($event, $listener);
} else {
$this->listeners[$event][] = $this->makeListener($listener);
}
}
}
// 发布
public function dispatch($event, $payload = [], $halt = false)
{
// When the given "event" is actually an object we will assume it is an event
// object and use the class as the event name and this event itself as the
// payload to the handler, which makes object based events quite simple.
[$event, $payload] = $this->parseEventAndPayload(
$event, $payload
);
if ($this->shouldBroadcast($payload)) {
$this->broadcastEvent($payload[0]);
}
$responses = [];
foreach ($this->getListeners($event) as $listener) {
$response = $listener($event, $payload);
// If a response is returned from the listener and event halting is enabled
// we will just return this response, and not call the rest of the event
// listeners. Otherwise we will add the response on the response list.
if ($halt && ! is_null($response)) {
return $response;
}
// If a boolean false is returned from a listener, we will stop propagating
// the event to any further listeners down in the chain, else we keep on
// looping through the listeners and firing every one in our sequence.
if ($response === false) {
break;
}
$responses[] = $response;
}
return $halt ? null : $responses;
}
}