Completed
Push — master ( 5bcb76...d93c36 )
by Patrick
03:31
created

LDAPServer::create()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 2 Features 2
Metric Value
cc 2
eloc 7
c 5
b 2
f 2
nc 2
nop 1
dl 0
loc 11
rs 9.4285
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
0 ignored issues
show
Bug introduced by
There is no parameter named $dn. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
10
 * @param string|array $ignore Set of characters to leave untouched
11
 * @return string The escaped string
12
 */
13
function ldap_escape($subject, $distinguishedName = 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($distinguishedName ? 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($distinguishedName)
42
    {
43
        $result = cleanupDN($result);
44
    }
45
46
    return $result;
47
}
48
49
function cleanupDN($distinguishedName)
50
{
51
    if($distinguishedName[0] == ' ')
52
    {
53
        $distinguishedName = '\\20'.substr($distinguishedName, 1);
54
    }
55
    if($distinguishedName[strlen($distinguishedName) - 1] == ' ')
56
    {
57
        $distinguishedName = substr($distinguishedName, 0, -1).'\\20';
58
    }
59
    return $distinguishedName;
60
}
61
62
class LDAPServer extends \Singleton
63
{
64
    protected $ldapLink;
65
    protected $connect;
66
    protected $binder;
67
    public $user_base;
68
    public $group_base;
69
70
    protected function __construct()
71
    {
72
        $this->ldapLink = null;
73
        $this->binder = null;
74
    }
75
76
    public function __destruct()
77
    {
78
    }
79
80
    public function __wakeup()
81
    {
82
        $this->ldapLink = ldap_connect($this->connect);
83
    }
84
85
    private function getConnectString($name, $proto = false)
86
    {
87
        if(strstr($name, ':') !== false)
88
        {
89
            return $name;
90
        }
91
        if($proto !== 'ldap')
92
        {
93
            return $proto.'://'.$name;
94
        }
95
        return $name;
96
    }
97
98
    public function connect($name, $proto = false)
99
    {
100
        $connectStr = $this->getConnectString($name, $proto);
101
        if($this->ldapLink !== null)
102
        {
103
            ldap_close($this->ldapLink);
104
        }
105
        $this->connect = $connectStr;
106
        $this->ldapLink = ldap_connect($this->connect);
107
        if($this->ldapLink === false)
108
        {
109
            $this->ldapLink = null;
110
            return false;
111
        }
112
        ldap_set_option($this->ldapLink, LDAP_OPT_PROTOCOL_VERSION, 3);
113
        return true;
114
    }
115
116
    public function disconnect()
117
    {
118
        if($this->ldapLink !== null)
119
        {
120
            ldap_close($this->ldapLink);
121
            $this->ldapLink = null;
122
        }
123
        $this->connect = false;
124
    }
125
126
    /**
127
     * Bind (login0 to the LDAP Server
128
     *
129
     * @param string $commonName The common name to bind with, null to bind anonymously
130
     * @param string $password The password to bind with, null to bind anonymously
131
     */
132
    public function bind($commonName = null, $password = null)
133
    {
134
        $res = false;
135
        if($this->ldapLink === null)
136
        {
137
            throw new \Exception('Not connected');
138
        }
139
        $this->binder = $commonName;
140
        if($commonName === null || $password === null)
141
        {
142
            return @ldap_bind($this->ldapLink);
143
        }
144
        try
145
        {
146
            $res = ldap_bind($this->ldapLink, $commonName, $password);
147
        }
148
        catch(\Exception $ex)
149
        {
150
            $this->ldapLink = ldap_connect($this->connect);
151
            $res = @ldap_bind($this->ldapLink, $commonName, $password);
152
        }
153
        return $res;
154
    }
155
156
    public function unbind()
157
    {
158
        if($this->ldapLink === null)
159
        {
160
            return true;
161
        }
162
        return @ldap_unbind($this->ldapLink);
163
    }
164
165
    private function fixChildArray(&$array, $key, &$entity)
166
    {
167
        $count = count($array);
168
        for($i = 0; $i < $count; $i++)
169
        {
170
            if(isset($array[$i]))
171
            {
172
                $entity[$key][$i] = $array[$i];
173
            }
174
        }
175
    }
176
177
    private function fixObject($object, &$delete = false)
178
    {
179
        $entity = $object;
180
        if(!is_array($object))
181
        {
182
            $entity = $object->to_array();
183
        }
184
        unset($entity['dn']);
185
        $keys = array_keys($entity);
186
        $count = count($keys);
187
        for($i = 0; $i < $count; $i++)
188
        {
189
            if(is_array($entity[$keys[$i]]))
190
            {
191
                $this->fixChildArray($entity[$keys[$i]], $keys[$i], $entity);
192
                //unset($entity[$keys[$i]]);
0 ignored issues
show
Unused Code Comprehensibility introduced by
100% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
193
            }
194
            else if($delete !== false && $entity[$keys[$i]] === null)
195
            {
196
                $delete[$keys[$i]] = array();
197
                unset($entity[$keys[$i]]);
198
            }
199
        }
200
        return $entity;
201
    }
202
203
    public function create($object)
204
    {
205
        $distinguishedName = ldap_escape($object['dn'], true);
206
        $entity = $this->fixObject($object);
207
        $ret = ldap_add($this->ldapLink, $distinguishedName, $entity);
208
        if($ret === false)
209
        {
210
            throw new \Exception('Failed to create object with dn='.$distinguishedName);
211
        }
212
        return $ret;
213
    }
214
215
    /**
216
     * Get the LDAP filter represented by the passed object
217
     *
218
     * @param boolean|string|\Data\Filter $filter The fiter to use
219
     *
220
     * @return string The filter in LDAP format
221
     */
222
    private function filterToString($filter)
223
    {
224
        if($filter === false)
225
        {
226
            return '(objectclass=*)';
227
        }
228
        if(is_string($filter))
229
        {
230
            return $filter;
231
        }
232
        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...
233
    }
234
235
    private function searchResultToArray($searchResult)
236
    {
237
        if($searchResult === false)
238
        {
239
            return false;
240
        }
241
        $res = ldap_get_entries($this->ldapLink, $searchResult);
242
        if(is_array($res))
243
        {
244
            $ldap = $res;
245
            $res = array();
246
            for($i = 0; $i < $ldap['count']; $i++)
247
            {
248
                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...
249
            }
250
        }
251
        return $res;
252
    }
253
254
    /**
255
     * Get data from the LDAP Server
256
     *
257
     * @param string $baseDN The distinguished name to start the search from
258
     * @param boolean|string|\Data\Filter $filter The fiter to use
259
     * @param boolean $single Read only the base DN
260
     * @param boolean|array $attributes The list of attributes to read
261
     *
262
     * @return boolean|array The results from the LDAP Server
263
     */
264
    public function read($baseDN, $filter = false, $single = false, $attributes = false)
265
    {
266
        $filterStr = $this->filterToString($filter);
267
        if($this->ldapLink === null)
268
        {
269
            throw new \Exception('Not connected');
270
        }
271
        try
272
        {
273
            if($single === true)
274
            {
275
                $searchResult = @ldap_read($this->ldapLink, $baseDN, $filterStr);
276
                return $this->searchResultToArray($searchResult);
277
            }
278
            if($attributes !== false)
279
            {
280
                $searchResult = @ldap_list($this->ldapLink, $baseDN, $filterStr, $attributes);
281
                return $this->searchResultToArray($searchResult);
282
            }
283
            $searchResult = @ldap_list($this->ldapLink, $baseDN, $filterStr);
284
            return $this->searchResultToArray($searchResult);
285
        }
286
        catch(\Exception $e)
287
        {
288
            throw new \Exception($e->getMessage().' '.$filterStr, $e->getCode(), $e);
289
        }
290
    }
291
292
    public function count($baseDN, $filter = false)
293
    {
294
        $filterStr = $this->filterToString($filter);
295
        if($this->ldapLink === null)
296
        {
297
            throw new \Exception('Not connected');
298
        }
299
        try
300
        {
301
            $searchResult = ldap_list($this->ldapLink, $baseDN, $filterStr, array('dn'));
302
        }
303
        catch(\Exception $e)
304
        {
305
            throw new \Exception($e->getMessage().' '.$filterStr, $e->getCode(), $e);
306
        }
307
        if($searchResult === false)
308
        {
309
            return 0;
310
        }
311
        return ldap_count_entries($this->ldapLink, $searchResult);
312
    }
313
314
    public function update($object)
315
    {
316
        $distinguishedName = ldap_escape($object['dn'], true);
317
        $delete = array();
318
        $entity = $this->fixObject($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...
319
        $ret = false;
320
        if(!empty($entity))
321
        {
322
            $ret = @ldap_mod_replace($this->ldapLink, $distinguishedName, $entity);
323
            if($ret === false)
324
            {
325
                $string = 'Failed to update object with dn='.$distinguishedName.' ('.ldap_errno($this->ldapLink).':'.ldap_error($this->ldapLink).') '.print_r($entity, true);
326
                throw new \Exception($string);
327
            }
328
        }
329
        if(!empty($delete))
330
        {
331
            $ret = @ldap_mod_del($this->ldapLink, $distinguishedName, $delete);
332
        }
333
        return $ret;
334
    }
335
336
    public function delete($distinguishedName)
337
    {
338
        return ldap_delete($this->ldapLink, $distinguishedName);
339
    }
340
}
341
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
342