Completed
Push — master ( c52182...0c0ab1 )
by Hong
02:32
created

Manager::getDriver()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
rs 9.4285
cc 2
eloc 8
nc 2
nop 1
1
<?php
2
/**
3
 * Phossa Project
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  Library
8
 * @package   Phossa2\Db
9
 * @copyright Copyright (c) 2016 phossa.com
10
 * @license   http://mit-license.org/ MIT License
11
 * @link      http://www.phossa.com/
12
 */
13
/*# declare(strict_types=1); */
14
15
namespace Phossa2\Db;
16
17
use Phossa2\Db\Message\Message;
18
use Phossa2\Shared\Base\ObjectAbstract;
19
use Phossa2\Db\Interfaces\DriverInterface;
20
use Phossa2\Db\Interfaces\ManagerInterface;
21
use Phossa2\Db\Exception\NotFoundException;
22
use Phossa2\Shared\Aware\TagAwareInterface;
23
24
/**
25
 * Manager
26
 *
27
 * Db driver manager implementation with tag support
28
 *
29
 * - Added 'weight factor' for round-robin fashion of load balancing
30
 *
31
 * - driver supports tags, may pick driver by tag
32
 *
33
 * ```php
34
 * $dbm = new Manager();
35
 *
36
 * // db writer with weighting factor 1
37
 * $dbm->addDriver($driver1->addTag('RW'), 1);
38
 *
39
 * // db reader with weighting factor 5
40
 * $dbm->addDriver($driver2->addTag('RO'), 5);
41
 *
42
 * // get whatever reader or writer
43
 * $db = $dbm->getDriver();
44
 *
45
 * // get driver by tag
46
 * $dbReader = $dbm->getDriver('RO');
47
 * ```
48
 *
49
 * @package Phossa2\Db
50
 * @author  Hong Zhang <[email protected]>
51
 * @see     ObjectAbstract
52
 * @see     ManagerInterface
53
 * @version 2.0.0
54
 * @since   2.0.0 added
55
 */
56
class Manager extends ObjectAbstract implements ManagerInterface
57
{
58
    /**
59
     * drivers
60
     *
61
     * @var    DriverInterface[]
62
     * @access protected
63
     */
64
    protected $drivers = [];
65
66
    /**
67
     * driver weight factor
68
     *
69
     * @var    array
70
     * @access protected
71
     */
72
    protected $factors = [];
73
74
    /**
75
     * Specify a weighting factor for the driver. normally 1 - 10
76
     *
77
     * {@inheritDoc}
78
     * @param  int $factor weight factor for round-robin
79
     */
80
    public function addDriver(DriverInterface $driver, /*# int */ $factor = 1)
81
    {
82
        // get unique driver id
83
        $id = $this->getDriverId($driver);
84
85
        // fix factor range: 1 - 10
86
        $this->factors[$id] = $this->fixFactor($factor);
87
88
        // add to the pool
89
        $this->drivers[$id] = $driver;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Get a driver with a tag matched
96
     *
97
     * {@inheritDoc}
98
     */
99
    public function getDriver(/*# string */ $tag = '')/*# : DriverInterface */
100
    {
101
        // match drivers
102
        $matched = $this->driverMatcher($tag);
103
104
        if (count($matched) > 0) {
105
            return $this->drivers[$matched[rand(1, count($matched)) - 1]];
106
107
        } else {
108
            throw new NotFoundException(
109
                Message::get(Message::DB_DRIVER_NOTFOUND),
110
                Message::DB_DRIVER_NOTFOUND
111
            );
112
        }
113
    }
114
115
    /**
116
     * Return a unique string id of the driver
117
     *
118
     * @param  DriverInterface $driver
119
     * @return string
120
     * @access protected
121
     */
122
    protected function getDriverId(DriverInterface $driver)/*# : string */
123
    {
124
        return spl_object_hash($driver);
125
    }
126
127
    /**
128
     * Make sure factor in the range of 1 - 10
129
     *
130
     * @param  int $factor
131
     * @return int
132
     * @access protected
133
     */
134
    protected function fixFactor(/*# int */ $factor)/*# : int */
135
    {
136
        $f = (int) $factor;
137
        return $f > 10 ? 10 : ($f < 1 ? 1 : $f);
138
    }
139
140
    /**
141
     * Match drivers with tag
142
     *
143
     * @param  string $tag tag to match
144
     * @return array
145
     * @access protected
146
     */
147
    protected function driverMatcher(/*# string */ $tag)/*# : array */
148
    {
149
        $matched = [];
150
        foreach ($this->drivers as $id => $driver) {
151
            if ($driver->ping() && $this->tagMatched($tag, $driver)) {
152
                $this->expandWithFactor($matched, $id);
153
            }
154
        }
155
        return $matched;
156
    }
157
158
    /**
159
     * Expand into $matched with factor weight
160
     *
161
     * @param  array &$matched
162
     * @param  string $id
163
     * @access protected
164
     */
165
    protected function expandWithFactor(array &$matched, /*# string */ $id)
166
    {
167
        $f = $this->factors[$id];
168
        for ($i = 0; $i < $f; ++$i) {
169
            $matched[] = $id;
170
        }
171
    }
172
173
    /**
174
     * Match driver with $tag
175
     *
176
     * @param  string $tag
177
     * @param  DriverInterface $driver
178
     * @return bool
179
     * @access protected
180
     */
181
    protected function tagMatched(
182
        /*# string */ $tag,
183
        DriverInterface $driver
184
    )/*# bool */ {
185
        // '' matches all
186
        if ('' === $tag) {
187
            return true;
188
        }
189
190
        // tag matched
191
        if ($driver instanceof TagAwareInterface && $driver->hasTag($tag)) {
192
            return true;
193
        }
194
195
        return false;
196
    }
197
}
198