Completed
Push — work-fleets ( addb18...c7cb6b )
by SuperNova.WS
06:04
created

Entity::bindFieldImport()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 21
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 4
eloc 8
nc 3
nop 2
dl 0
loc 21
rs 9.0534
c 2
b 0
f 1
ccs 0
cts 13
cp 0
crap 20
1
<?php
2
3
/**
4
 * Class Entity
5
 *
6
 * @property int|float $dbId Buddy record DB ID
7
 */
8
class Entity implements \Common\IMagicProperties {
9
  /**
10
   * Name of table for this entity
11
   *
12
   * @var string $tableName
13
   */
14
  protected static $tableName = '_table';
15
  /**
16
   * Name of key field field in this table
17
   *
18
   * @var string $idField
19
   */
20
  protected static $idField = 'id';
21
  /**
22
   * Container for property values
23
   *
24
   * @var PropertyHider $_container
25
   */
26
  protected $_container;
27
  protected static $_containerName = 'PropertyHiderInArray';
28
29
  /**
30
   * Property list and description
31
   *
32
   * propertyName => array(
33
   *    P_DB_FIELD => 'dbFieldName', - directly converts property to field and vice versa
34
   *    P_DB_ROW_IMPORT => function ($that, &$row) - Imports DB field value from dbRow to object's property
35
   *    P_DB_ROW_EXPORT => function ($that, &$row) - Exports object's property to DB field
36
   * )
37
   *
38
   * @var array[]
39
   */
40
  protected static $_properties = array();
41
42
  /**
43
   * @var array
44
   */
45
  protected static $_propertyToField = array();
46
  protected static $_propertyImportExportIsBind = false;
47
48
  /**
49
   * Link to DB which used by this Entity
50
   *
51
   * @var db_mysql $dbStatic
52
   * deprecated - replace with container ID like 'db' or 'dbAuth'
53
   */
54
  public static $dbStatic;
55
56
  /**
57
   * Service to work with rows
58
   *
59
   * @var \DbRowDirectOperator $rowOperator
60
   */
61
  protected static $rowOperator;
62
63
  protected function bindFieldExport(&$propertyData, $propertyName) {
64
    if(!empty($propertyData[P_DB_ROW_EXPORT])) {
65
      return;
66
    }
67
68
    // Last resort - binding export function to DB field name
69
    if (!empty($propertyData[P_DB_FIELD])) {
70
      // Property is mapped 1-to-1 to field
71
      /**
72
       * @param static $that
73
       */
74
      // Alas! No bindTo() method in 5.3 closures! So we should use what we have
75
      // Also no auto bound of $this until 5.4
76
      $propertyData[P_DB_ROW_EXPORT] = function ($that, &$row) use ($propertyName, $propertyData) {
77
        $row[$propertyData[P_DB_FIELD]] = $that->$propertyName;
78
      };
79
    }
80
  }
81
82
  protected function bindFieldImport(&$propertyData, $propertyName) {
83
    if(!empty($propertyData[P_DB_ROW_IMPORT])) {
84
      return;
85
    }
86
87
    // Last resort - binding import function to DB field name
88
    if (!empty($propertyData[P_DB_FIELD])) {
89
      // Property is mapped 1-to-1 to field
90
      /**
91
       * @param static $that
92
       */
93
      // Alas! No bindTo() method in 5.3 closures! So we should use what we have
94
      // Also no auto bound of $this until 5.4
95
      $propertyData[P_DB_ROW_IMPORT] = function ($that, &$row) use ($propertyName, $propertyData) {
96
        $that->$propertyName = \Common\Types::castAs(
97
          !empty($propertyData[P_DB_TYPE]) ? $propertyData[P_DB_TYPE] : TYPE_EMPTY,
98
          $row[$propertyData[P_DB_FIELD]]
99
        );
100
      };
101
    }
102
  }
103
104
  /**
105
   * Fills property-to-field table which used to generate result array
106
   */
107
  protected function fillPropertyToField() {
108
    if (static::$_propertyImportExportIsBind) {
109
      return;
110
    }
111
112
    // Filling property-to-field relation array
113
    foreach (static::$_properties as $propertyName => &$propertyData) {
114
      $this->bindFieldExport($propertyData, $propertyName);
115
      $this->bindFieldImport($propertyData, $propertyName);
116
    }
117
118
    static::$_propertyImportExportIsBind = true;
119
  }
120
121
  /**
122
   * Entity constructor.
123
   *
124
   * @param \Common\GlobalContainer $gc
125
   */
126
  public function __construct($gc) {
127
    empty(static::$dbStatic) && !empty($gc->db) ? static::$dbStatic = $gc->db : false;
0 ignored issues
show
Documentation Bug introduced by
It seems like $gc->db can also be of type object<Closure>. However, the property $dbStatic is declared as type object<db_mysql>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
128
    static::$rowOperator = $gc->dbRowOperator;
0 ignored issues
show
Documentation Bug introduced by
It seems like $gc->dbRowOperator can also be of type object<Closure>. However, the property $rowOperator is declared as type object<DbRowDirectOperator>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
129
130
    $this->_container = new static::$_containerName();
131
    $this->_container->setProperties(static::$_properties);
132
133
    $this->fillPropertyToField();
134
  }
135
136
137
  /**
138
   * Invoke row transformation operation on object
139
   *
140
   * Uses in to save/load data from DB row into/from object
141
   *
142
   * @param array  $row
143
   * @param string $operation
144
   * @param string $desc
145
   *
146
   * @throws Exception
147
   */
148
  protected function rowInvokeOperation(&$row, $operation, $desc) {
0 ignored issues
show
Unused Code introduced by
The parameter $desc is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
149
    foreach (static::$_properties as $propertyName => $propertyData) {
150
151
      if (is_callable($propertyData[$operation])) {
152
        // Some black magic here
153
        // Closure is a class - so have __invoke() magic method
154
        // It means we can invoke it by directly call __invoke()
155
        $propertyData[$operation]->__invoke($this, $row);
156
        // TODO - however for a sake of uniformity may be we should consider use call_user_func
157
//      call_user_func($propertyData[P_DB_ROW_EXPORT], $this, $row);
158
      } else {
159
        throw new \Exception('There is no valid DB [' . $operation . '] row operation for ' . get_called_class() . '::' . $propertyName);
160
      }
161
    }
162
  }
163
164
  /**
165
   * Import DB row state into object properties
166
   *
167
   * @param array $row
168
   */
169
  public function importDbRow($row) {
170
    $this->rowInvokeOperation($row, P_DB_ROW_IMPORT, 'IMPORTER');
171
  }
172
173
  const ENTITY_DB_ID_INCLUDE = true;
174
  const ENTITY_DB_ID_EXCLUDE = true;
175
176
  /**
177
   * Export data from object properties into DB row for further use
178
   *
179
   * @param bool $withDbId - Should dbId too be returned. Useful for INSERT statements
180
   *
181
   * @return array
182
   */
183
  protected function exportDbRow($withDbId = self::ENTITY_DB_ID_INCLUDE) {
184
    $row = array();
185
186
    $this->rowInvokeOperation($row, P_DB_ROW_EXPORT, 'EXPORTER');
187
188
    if ($withDbId == self::ENTITY_DB_ID_EXCLUDE) {
189
      unset($row[$this->getIdFieldName()]);
190
    }
191
192
    return $row;
193
  }
194
195
  /**
196
   * @return array
197
   */
198
  public function exportRowWithoutId() {
199
    return $this->exportDbRow(self::ENTITY_DB_ID_EXCLUDE);
200
  }
201
202
  /**
203
   * @return array
204
   */
205
  public function exportRowWithId() {
206
    return $this->exportDbRow(self::ENTITY_DB_ID_INCLUDE);
207
  }
208
209
  public function getTableName() {
210
    return static::$tableName;
211
  }
212
213
  public function getIdFieldName() {
214
    return static::$idField;
215
  }
216
217
  public function isContainerEmpty() {
218
    return $this->_container->isContainerEmpty();
219
  }
220
221
  public function isNew() {
222
    return empty($this->dbId);
223
  }
224
225
  /**
226
   * @return db_mysql
227
   */
228
  public function getDbStatic() {
229
    return static::$dbStatic;
230
  }
231
232
  public function __get($name) {
233
    return $this->_container->$name;
234
  }
235
236
  public function __set($name, $value) {
237
    $this->_container->$name = $value;
238
  }
239
240
  public function __isset($name) {
241
    return isset($this->_container->$name);
242
  }
243
244
  public function __unset($name) {
245
    unset($this->_container->$name);
246
  }
247
248
}
249