Completed
Push — master ( b029a9...ae7e79 )
by Patrick
03:01
created

LDAPServer   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 269
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 4
Bugs 4 Features 1
Metric Value
c 4
b 4
f 1
dl 0
loc 269
rs 8.5454
wmc 49
lcom 1
cbo 2

16 Methods

Rating   Name   Duplication   Size   Complexity  
A connect() 0 17 3
A __destruct() 0 3 1
A __wakeup() 0 4 1
A getConnectString() 0 12 3
A disconnect() 0 9 2
A __construct() 0 5 1
B bind() 0 23 5
A unbind() 0 8 2
A get_error() 0 4 1
C _fix_object() 0 33 8
A create() 0 11 2
A filterToString() 0 12 3
C read() 0 46 8
A count() 0 21 4
A update() 0 20 4
A delete() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like LDAPServer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use LDAPServer, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace LDAP;
3
4
/**
5
 * function ldap_escape
6
 * @author Chris Wright
7
 * @version 2.0
8
 * @param string $subject The subject string
9
 * @param bool $dn Treat subject as a DN if TRUE
10
 * @param string|array $ignore Set of characters to leave untouched
11
 * @return string The escaped string
12
 */
13
function ldap_escape($subject, $dn = FALSE, $ignore = NULL)
14
{
15
    // The base array of characters to escape
16
    // Flip to keys for easy use of unset()
17
    $search = array_flip($dn ? array('\\', '+', '<', '>', ';', '"', '#') : array('\\', '*', '(', ')', "\x00"));
18
19
    // Process characters to ignore
20
    if(is_array($ignore))
21
    {
22
        $ignore = array_values($ignore);
23
    }
24
    for($char = 0; isset($ignore[$char]); $char++)
25
    {
26
        unset($search[$ignore[$char]]);
27
    }
28
29
    // Flip $search back to values and build $replace array
30
    $search = array_keys($search); 
31
    $replace = array();
32
    foreach($search as $char)
33
    {
34
        $replace[] = sprintf('\\%02x', ord($char));
35
    }
36
37
    // Do the main replacement
38
    $result = str_replace($search, $replace, $subject);
39
40
    // Encode leading/trailing spaces in DN values
41
    if($dn)
42
    {
43
        if($result[0] == ' ')
44
        {
45
            $result = '\\20'.substr($result, 1);
46
        }
47
        if($result[strlen($result) - 1] == ' ')
48
        {
49
            $result = substr($result, 0, -1).'\\20';
50
        }
51
    }
52
53
    return $result;
54
}
55
56
class LDAPServer extends \Singleton
57
{
58
    protected $ds;
59
    protected $connect;
60
    protected $binder;
61
    public $user_base;
62
    public $group_base;
63
64
    protected function __construct()
65
    {
66
        $this->ds = null;
67
        $this->binder = null;
68
    }
69
70
    public function __destruct()
71
    {
72
    }
73
74
    public function __wakeup()
75
    {
76
        $this->ds = ldap_connect($this->connect);
77
    }
78
79
    private function getConnectString($name, $proto = false)
80
    {
81
        if(strstr($name, ':') !== false)
82
        {
83
            return $name;
84
        }
85
        if($proto !== 'ldap')
86
        {
87
            return $proto.'://'.$name;
88
        }
89
        return $name;
90
    }
91
92
    function connect($name, $proto = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
93
    {
94
        $connectStr = $this->getConnectString($name, $proto);
95
        if($this->ds !== null)
96
        {
97
            ldap_close($this->ds);
98
        }
99
        $this->connect = $connectStr;
100
        $this->ds      = ldap_connect($this->connect);
101
        if($this->ds === false)
102
        {
103
            $this->ds = null;
104
            return false;
105
        }
106
        ldap_set_option($this->ds, LDAP_OPT_PROTOCOL_VERSION, 3);
107
        return true;
108
    }
109
110
    public function disconnect()
111
    {
112
        if($this->ds !== null)
113
        {
114
            ldap_close($this->ds);
115
            $this->ds = null;
116
        }
117
        $this->connect = false;
118
    }
119
120
    /**
121
     * Bind (login0 to the LDAP Server
122
     *
123
     * @param string $cn The common name to bind with, null to bind anonymously
124
     * @param string $password The password to bind with, null to bind anonymously
125
     */
126
    public function bind($cn = null, $password = null)
127
    {
128
        $res = false;
129
        if($this->ds === null)
130
        {
131
            throw new \Exception('Not connected');
132
        }
133
        $this->binder = $cn;
134
        if($cn === null || $password === null)
135
        {
136
            return @ldap_bind($this->ds);
137
        }
138
        try
139
        {
140
            $res = ldap_bind($this->ds, $cn, $password);
141
        }
142
        catch(\Exception $ex)
143
        {
144
            $this->ds = ldap_connect($this->connect);
145
            $res = @ldap_bind($this->ds, $cn, $password);
146
        }
147
        return $res;
148
    }
149
150
    function unbind()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
151
    {
152
        if($this->ds === null)
153
        {
154
            return true;
155
        }
156
        return @ldap_unbind($this->ds);
157
    }
158
159
    function get_error()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
160
    {
161
        return ldap_error($this->ds);
162
    }
163
164
    private function _fix_object($object, &$delete = false)
165
    {
166
        $entity = $object;
167
        if(!is_array($object))
168
        {
169
            $entity = $object->to_array();
170
        }
171
        unset($entity['dn']);
172
        $keys = array_keys($entity);
173
        $count = count($keys);
174
        for($i = 0; $i < $count; $i++)
175
        {
176
            if(is_array($entity[$keys[$i]]))
177
            {
178
                $array = $entity[$keys[$i]];
179
                unset($entity[$keys[$i]]);
180
                $count1 = count($array);
181
                for($j = 0; $j < $count1; $j++)
182
                {
183
                    if(isset($array[$j]))
184
                    {
185
                        $entity[$keys[$i]][$j] = $array[$j];
186
                    }
187
                }
188
            }
189
            else if($delete !== false && $entity[$keys[$i]] === null)
190
            {
191
                $delete[$keys[$i]] = array();
192
                unset($entity[$keys[$i]]);
193
            }
194
        }
195
        return $entity;
196
    }
197
198
    function create($object)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
199
    {
200
        $dn = ldap_escape($object['dn'], true);
201
        $entity = $this->_fix_object($object);
202
        $ret = ldap_add($this->ds, $dn, $entity);
203
        if($ret === false)
204
        {
205
            throw new \Exception('Failed to create object with dn='.$dn);
206
        }
207
        return $ret;
208
    }
209
210
    /**
211
     * Get the LDAP filter represented by the passed object
212
     *
213
     * @param boolean|string|\Data\Filter $filter The fiter to use
214
     *
215
     * @return string The filter in LDAP format
216
     */
217
    private function filterToString($filter)
218
    {
219
        if($filter === false)
220
        {
221
            return '(objectclass=*)';
222
        }
223
        if(is_string($filter))
224
        {
225
            return $filter;
226
        }
227
        return $filter->to_ldap_string();
0 ignored issues
show
Bug introduced by
It seems like $filter is not always an object, but can also be of type boolean. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
228
    }
229
230
    function read($baseDN, $filter = false, $single = false, $attributes = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
231
    {
232
        $filterStr = $this->filterToString($filter);
233
        if($this->ds === null)
234
        {
235
            throw new \Exception('Not connected');
236
        }
237
        $sr = false;
238
        try
239
        {
240
            if($single === true)
241
            {
242
                $sr = @ldap_read($this->ds, $baseDN, $filterStr);
243
            }
244
            else
245
            {
246
                if($attributes !== false)
247
                {
248
                    $sr = @ldap_list($this->ds, $baseDN, $filterStr, $attributes);
249
                }
250
                else
251
                {
252
                    $sr = @ldap_list($this->ds, $baseDN, $filterStr);
253
                }
254
            }
255
        }
256
        catch(\Exception $e)
257
        {
258
            throw new \Exception($e->getMessage().' '.$filterStr, $e->getCode(), $e);
259
        }
260
        if($sr === false)
261
        {
262
            return false;
263
        }
264
        $res = ldap_get_entries($this->ds, $sr);
265
        if(is_array($res))
266
        {
267
            $ldap = $res;
268
            $res = array();
269
            for($i = 0; $i < $ldap['count']; $i++)
270
            {
271
                array_push($res, new LDAPObject($ldap[$i], $this));
0 ignored issues
show
Documentation introduced by
$this is of type this<LDAP\LDAPServer>, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
272
            }
273
        }
274
        return $res;
275
    }
276
277
    function count($baseDN, $filter = false)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
278
    {
279
        $filterStr = $this->filterToString($filter);
280
        if($this->ds === null)
281
        {
282
            throw new \Exception('Not connected');
283
        }
284
        try
285
        {
286
            $sr = ldap_list($this->ds, $baseDN, $filterStr, array('dn'));
287
        }
288
        catch(\Exception $e)
289
        {
290
            throw new \Exception($e->getMessage().' '.$filterStr, $e->getCode(), $e);
291
        }
292
        if($sr === false)
293
        {
294
            return false;
295
        }
296
        return ldap_count_entries($this->ds, $sr);
297
    }
298
299
    function update($object)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
300
    {
301
        $dn = ldap_escape($object['dn'], true);
302
        $delete = array();
303
        $entity = $this->_fix_object($object, $delete);
0 ignored issues
show
Documentation introduced by
$delete is of type array, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
304
        $ret = false;
305
        if(!empty($entity))
306
        {
307
            $ret = @ldap_mod_replace($this->ds, $dn, $entity);
308
            if($ret === false)
309
            {
310
                throw new \Exception('Failed to update object with dn='.$dn.'('.ldap_errno($this->ds).':'.ldap_error($this->ds).') '.print_r($entity, true));
311
            }
312
        }
313
        if(!empty($delete))
314
        {
315
            $ret = @ldap_mod_del($this->ds, $dn, $delete);
316
        }
317
        return $ret;
318
    }
319
320
    function delete($dn)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
321
    {
322
        return ldap_delete($this->ds, $dn);
323
    }
324
}
325
326