Completed
Push — master ( f38146...3141eb )
by Christopher
05:15
created

ActiveRecord::attributes()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 3
eloc 7
nc 3
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 chrmorandi\ldap\ActiveQuery;
12
use chrmorandi\ldap\Connection;
13
use Exception;
14
use ReflectionClass;
15
use ReflectionProperty;
16
use Yii;
17
use yii\db\BaseActiveRecord;
18
19
/**
20
 * ActiveRecord is the base class for classes representing relational data in terms of objects.
21
 *
22
 * This class implements the ActiveRecord pattern for the [ldap] protocol.
23
 *
24
 * For defining a record a subclass should at least implement the [[attributes()]] method to define
25
 * attributes or use some prepared traits for specific objects. A primary key can be defined via [[primaryKey()]] which defaults to `cn` if not specified.
26
 *
27
 * The following is an example model called `User`:
28
 *
29
 * ```php
30
 * class User extends \chrmorandi\ldap\ActiveRecord
31
 * {
32
 *     public function attributes()
33
 *     {
34
 *         return ['objectClass', 'cn', 'name'];
35
 *     }
36
 * }
37
 * ```
38
 *
39
 * Or simply
40
 *
41
 * ```php
42
 * use \chrmorandi\ldap\schemas\ADUserTrait;
43
 * ```
44
 *
45
 * @since 1.0.0
46
 */
47
class ActiveRecord extends BaseActiveRecord
48
{
49
    use \chrmorandi\ldap\schemas\ADUserTrait;
50
    /**
51
     * The LDAP API references an LDAP object by its distinguished name (DN).
52
     * A DN is a sequence of relative distinguished names (RDN) connected by commas.
53
     *
54
     * @link https://msdn.microsoft.com/en-us/library/aa366101(v=vs.85).aspx
55
     * @var  string
56
     */
57
    public $dn;
58
    
59
    /**
60
     * Returns the LDAP connection used by this AR class.
61
     *
62
     * @return Connection the LDAP connection used by this AR class.
63
     */
64
    public static function getDb()
65
    {
66
        return Yii::$app->get('ldap');
67
    }
68
    
69
    /**
70
     * @inheritdoc
71
     * @return ActiveQuery the newly created [[ActiveQuery]] instance.
72
     */
73
    public static function find()
74
    {
75
        return Yii::createObject(ActiveQuery::class, [get_called_class()]);
76
    }
77
78
    /**
79
     * Returns the primary key name(s) for this AR class.
80
     * This method should be overridden by child classes to define the primary key.
81
     *
82
     * @return string[] the primary keys of this record.
83
     */
84
    public static function primaryKey()
85
    {
86
        return 'dn';
0 ignored issues
show
Bug Best Practice introduced by
The return type of return 'dn'; (string) is incompatible with the return type declared by the interface yii\db\ActiveRecordInterface::primaryKey of type string[].

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...
87
    }
88
    
89
    /**
90
     * Returns the list of attribute names.
91
     * This method returns all protected non-static properties of the class.
92
     * You may override this method to change the default behavior.
93
     *
94
     * @return array list of attribute names.
95
     */
96
    public function attributes()
97
    {
98
        $class = new ReflectionClass($this);
99
        $names = [];
100
        foreach ($class->getProperties(ReflectionProperty::IS_PROTECTED) as $property) {
101
            if (!$property->isStatic()) {
102
                $names[] = $property->getName();
103
            }
104
        }
105
106
        return $names;
107
    }
108
    
109
    /**
110
     * Inserts a row into the associated database table using the attribute values of this record.
111
     *
112
     * This method performs the following steps in order:
113
     *
114
     * 1. call [[beforeValidate()]] when `$runValidation` is true. If [[beforeValidate()]]
115
     *    returns `false`, the rest of the steps will be skipped;
116
     * 2. call [[afterValidate()]] when `$runValidation` is true. If validation
117
     *    failed, the rest of the steps will be skipped;
118
     * 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`,
119
     *    the rest of the steps will be skipped;
120
     * 4. insert the record into database. If this fails, it will skip the rest of the steps;
121
     * 5. call [[afterSave()]];
122
     *
123
     * In the above step 1, 2, 3 and 5, events [[EVENT_BEFORE_VALIDATE]],
124
     * [[EVENT_AFTER_VALIDATE]], [[EVENT_BEFORE_INSERT]], and [[EVENT_AFTER_INSERT]]
125
     * will be raised by the corresponding methods.
126
     *
127
     * Only the [[dirtyAttributes|changed attribute values]] will be inserted into database.
128
     *
129
     * If the table's primary key is auto-incremental and is null during insertion,
130
     * it will be populated with the actual value after insertion.
131
     *
132
     * For example, to insert a customer record:
133
     *
134
     * ```php
135
     * $customer = new Customer;
136
     * $customer->name = $name;
137
     * $customer->email = $email;
138
     * $customer->insert();
139
     * ```
140
     *
141
     * @param  boolean $runValidation whether to perform validation (calling [[validate()]])
142
     * before saving the record. Defaults to `true`. If the validation fails, the record
143
     * will not be saved to the database and this method will return `false`.
144
     * @param  array   $attributes    list of attributes that need to be saved. Defaults to null, meaning all attributes that are loaded from DB will be saved. meaning all attributes that are loaded from DB will be saved.
145
     * meaning all attributes that are loaded from DB will be saved.
146
     * @return boolean whether the attributes are valid and the record is inserted successfully.
147
     * @throws Exception in case insert failed.
148
     */
149
    public function insert($runValidation = true, $attributes = null)
150
    {
151
        if ($runValidation && !$this->validate($attributes)) {
152
            Yii::info('Model not inserted due to validation error.', __METHOD__);
153
            return false;
154
        }
155
156
        return $this->insertInternal($attributes);
157
    }
158
    
159
    /**
160
     * Inserts an ActiveRecord into LDAP without.
161
     *
162
     * @param  array $attributes list of attributes that need to be saved. Defaults to null,
163
     * meaning all attributes that are loaded will be saved.
164
     * @return boolean whether the record is inserted successfully.
165
     */
166
    protected function insertInternal($attributes = null)
167
    {
168
        if (!$this->beforeSave(true)) {
169
            return false;
170
        }
171
        
172
        $values = $this->getDirtyAttributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes defined by parameter $attributes on line 166 can also be of type array; however, yii\db\BaseActiveRecord::getDirtyAttributes() does only seem to accept array<integer,string>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
173
        $dn = $values[self::primaryKey()];
174
        unset($values[self::primaryKey()]);
175
        
176
        if (($primaryKeys = static::getDb()->execute('ldap_add', [$dn,$values])) === false) {
177
            return false;
178
        }
179
        $this->setAttribute(self::primaryKey(), $dn);
180
        $values[self::primaryKey()] = $dn;
181
182
        $changedAttributes = array_fill_keys(array_keys($values), null);
183
        $this->setOldAttributes($values);
184
        $this->afterSave(true, $changedAttributes);
185
186
        return true;
187
    }
188
    
189
    /**
190
     * Deletes rows in the table using the provided conditions.
191
     * WARNING: If you do not specify any condition, this method will delete ALL rows in the ldap directory.
192
     *
193
     * For example, to delete all customers whose status is 3:
194
     *
195
     * ```php
196
     * Customer::deleteAll('status = 3');
197
     * ```
198
     *
199
     * @param  string|array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
200
     * Please refer to [[Query::where()]] on how to specify this parameter.
201
     * @return integer the number of rows deleted
202
     */
203
    public static function deleteAll($condition = null)
204
    {
205
        $entries = (new Query())->select(self::primaryKey())->where($condition);
0 ignored issues
show
Bug introduced by
It seems like $condition defined by parameter $condition on line 203 can also be of type null; however, yii\db\QueryTrait::where() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
206
        $count = 0;
207
        
208
        foreach ($entries as $entry) {
0 ignored issues
show
Bug introduced by
The expression $entries of type object<chrmorandi\ldap\Query> is not traversable.
Loading history...
209
            $params = [
210
                $entry[self::primaryKey()]
211
            ];
212
            static::getDb()->execute('ldap_delete', $params);
213
            $count++;
214
        }
215
        return $count;
216
    }
217
}
218