Completed
Push — master ( defc9d...f38146 )
by Christopher
02:50
created

Connection::getResource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * @link      https://github.com/chrmorandi/yii2-ldap for the canonical source repository
4
 * @package   yii2-ldap
5
 * @author    Christopher Mota <[email protected]>
6
 * @license   MIT License - view the LICENSE file that was distributed with this source code.
7
 */
8
9
namespace chrmorandi\ldap;
10
11
use yii\base\Component;
12
13
/**
14
 * 
15
 *
16
 * @author Christopher Mota <[email protected]>
17
 * @since 1.0
18
 */
19
class Connection extends Component
20
{
21
    use LdapFunctionTrait;
22
23
    /**
24
     * LDAP protocol string.
25
     *
26
     * @var string
27
     */
28
    const PROTOCOL = 'ldap://';
29
30
    /**
31
     * LDAP port number.
32
     *
33
     * @var string
34
     */
35
    const PORT = '389';
36
    
37
    /**
38
     * @event Event an event that is triggered after a DB connection is established
39
     */
40
    const EVENT_AFTER_OPEN = 'afterOpen';
41
42
    /**
43
     * @var string the LDAP base dn.
44
     */
45
    public $baseDn;
46
47
    /**
48
     * https://msdn.microsoft.com/en-us/library/ms677913(v=vs.85).aspx
49
     *
50
     * @var bool the integer to instruct the LDAP connection whether or not to follow referrals.
51
     */
52
    public $followReferrals = false;
53
54
    /**
55
     * @var string The LDAP port to use when connecting to the domain controllers.
56
     */
57
    public $port = self::PORT;
58
59
    /**
60
     * @var bool Determines whether or not to use TLS with the current LDAP connection.
61
     */
62
    public $useTLS = false;
63
64
    /**
65
     * @var array the domain controllers to connect to.
66
     */
67
    public $dc = [];
68
69
    /**
70
     * @var string the LDAP account suffix.
71
     */
72
    protected $accountSuffix;
73
74
    /**
75
     * @var string the LDAP account prefix.
76
     */
77
    protected $accountPrefix;
78
    
79
    /**
80
     * @var string the username for establishing LDAP connection. Defaults to `null` meaning no username to use.
81
     */
82
    public $username;
83
    
84
    /**
85
     * @var string the password for establishing DB connection. Defaults to `null` meaning no password to use.
86
     */
87
    public $password;
88
    
89
    /**
90
     * @var bool stores the bool whether or not the current connection is bound.
91
     */
92
    protected $bound = false;    
93
        
94
    /**
95
     * @var Connection
96
     */
97
    protected $resource;
98
    
99
    /**
100
     * Get the current resource of connection.
101
     *
102
     * @return mixed
103
     */
104
    public function getResource()
105
    {
106
        return $this->resource;
107
    }
108
    
109
    /**
110
     * Connects and Binds to the Domain Controller with a administrator credentials.
111
     *
112
     * @throws LdapException
113
     */
114
    public function open($anonymous = false)
115
    {
116
        // Connect to the LDAP server.
117
        if ($this->connect($this->dc, $this->port)) {
118
            if ($anonymous) {
119
                $this->bound = ($this->resource);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->resource of type object<chrmorandi\ldap\Connection> is incompatible with the declared type boolean of property $bound.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
120
            } else {
121
                $this->bound = ldap_bind($this->resource, $this->username, $this->password);
122
            }
123
        } else {
124
            throw new LdapException(sprintf('Unable to connect to server: %s', $this->lastError), $this->errNo);
0 ignored issues
show
Documentation introduced by
The property lastError does not exist on object<chrmorandi\ldap\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property errNo does not exist on object<chrmorandi\ldap\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
125
        }
126
    }
127
128
    /**
129
     * Returns true / false if the current
130
     * connection is bound.
131
     *
132
     * @return bool
133
     */
134
    public function isBound()
135
    {
136
        return $this->bound;
137
    }
138
139
    /**
140
     * Retrieve the last error on the current
141
     * connection.
142
     *
143
     * @return string
144
     */
145
    public function connect($hostname = [], $port = '389')
146
    {
147
        $protocol = $this::PROTOCOL;
148
149
        if (is_array($hostname)) {
150
            $hostname = $protocol.implode(' '.$protocol, $hostname);
151
        }
152
        $this->resource = ldap_connect($hostname, $port);
0 ignored issues
show
Documentation Bug introduced by
It seems like ldap_connect($hostname, $port) of type resource is incompatible with the declared type object<chrmorandi\ldap\Connection> of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
153
        
154
        if(!$this->resource){            
155
            return false;          
156
        }
157
        
158
        $followReferrals = $this->followReferrals;
159
160
        // Set the LDAP options.
161
        $this->setOption(LDAP_OPT_PROTOCOL_VERSION, 3);
162
        $this->setOption(LDAP_OPT_REFERRALS, $followReferrals);
163
164
        if ($this->useTLS && !$this->startTLS()) {
165
            throw new LdapException($this->lastError, $this->getErrNo());
0 ignored issues
show
Documentation introduced by
The property lastError does not exist on object<chrmorandi\ldap\Connection>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
166
        }
167
        
168
        $this->trigger(self::EVENT_AFTER_OPEN);
169
        
170
        return is_resource($this->resource);
171
    }    
172
173
    /**
174
     * Closes the current connection.
175
     *
176
     * @return mixed
177
     */
178
    public function close()
179
    {
180
        if (is_resource($this->resource)) {
181
            ldap_close($this->resource);
182
        }
183
        return true;
184
    }
185
        
186
    /**
187
     * Execute ldap functions like.
188
     * 
189
     * http://php.net/manual/en/ref.ldap.php
190
     * 
191
     * @param  string $function php LDAP function
192
     * @param  array $params params for execute ldap function
193
     * @return bool|resource
194
     * @throws LdapException
195
     */
196
    public function execute($function, $params)
197
    {
198
        $this->open();
199
200
        $result = call_user_func($function, $this->resource, ...$params);
201
        if (!$result) {
202
            throw new LdapException($this->getLastError(), $this->getErrNo());
203
        }
204
        
205
        if(is_resource($result)){
206
            return new DataReader($this, $result);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \chrmorandi\l...Reader($this, $result); (chrmorandi\ldap\DataReader) is incompatible with the return type documented by chrmorandi\ldap\Connection::execute of type boolean|resource.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
207
        }
208
209
        return $result;
210
    }
211
    
212
    /**
213
     * Close the connection before serializing.
214
     * @return array
215
     */
216
    public function __sleep()
217
    {
218
        $this->close();
219
        return array_keys((array) $this);
220
    }
221
222
}