maikgreubel /
caribu-orm
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | declare(strict_types = 1); |
||
| 3 | |||
| 4 | namespace Nkey\Caribu\Orm; |
||
| 5 | |||
| 6 | use \Nkey\Caribu\Type\IType; |
||
| 7 | use \Nkey\Caribu\Model\AbstractModel; |
||
| 8 | /** |
||
| 9 | * The main object relational mapper class |
||
| 10 | * |
||
| 11 | * This class is part of Caribu package |
||
| 12 | * |
||
| 13 | * @author Maik Greubel <[email protected]> |
||
| 14 | */ |
||
| 15 | class Orm |
||
| 16 | { |
||
| 17 | /** |
||
| 18 | * Include the transaction related functionality |
||
| 19 | */ |
||
| 20 | use OrmTransaction; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * Include persisting related functionality |
||
| 24 | */ |
||
| 25 | use OrmPersister; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * Include the generics interpolation functionality |
||
| 29 | */ |
||
| 30 | use \Generics\Util\Interpolator; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * Singleton pattern |
||
| 34 | * |
||
| 35 | * @var Orm |
||
| 36 | */ |
||
| 37 | private static $instance = null; |
||
| 38 | |||
| 39 | /** |
||
| 40 | * Singleton pattern |
||
| 41 | * |
||
| 42 | * @return Orm The current instance |
||
| 43 | */ |
||
| 44 | 34 | public static function getInstance(): Orm |
|
| 45 | { |
||
| 46 | 34 | if (null === self::$instance) { |
|
| 47 | 34 | self::$instance = new self(); |
|
| 48 | } |
||
| 49 | |||
| 50 | 34 | return self::$instance; |
|
| 51 | } |
||
| 52 | |||
| 53 | /** |
||
| 54 | * Singleton pattern |
||
| 55 | */ |
||
| 56 | 34 | private function __construct() |
|
| 57 | { |
||
| 58 | // TODO:Implement console logging of sql for debugging purposes |
||
| 59 | 34 | } |
|
| 60 | |||
| 61 | /** |
||
| 62 | * Disable cloning |
||
| 63 | */ |
||
| 64 | private function __clone() |
||
| 65 | { |
||
| 66 | // |
||
| 67 | } |
||
| 68 | |||
| 69 | /** |
||
| 70 | * Retrieve particular dataset by given id |
||
| 71 | * |
||
| 72 | * @param mixed $id |
||
| 73 | * The primary key value of dataset row to retrieve |
||
| 74 | * |
||
| 75 | * @return \Nkey\Caribu\Model\AbstractModel |
||
| 76 | * |
||
| 77 | * @throws OrmException |
||
| 78 | */ |
||
| 79 | 11 | public static function get($id): \Nkey\Caribu\Model\AbstractModel |
|
| 80 | { |
||
| 81 | 11 | $className = get_called_class(); |
|
| 82 | |||
| 83 | 11 | $pkColumn = self::getPrimaryKeyCol($className); |
|
| 84 | |||
| 85 | 11 | $result = self::find(array( |
|
| 86 | 11 | $pkColumn => $id |
|
| 87 | )); |
||
| 88 | |||
| 89 | 11 | if (! $result || is_array($result)) { |
|
| 90 | 1 | throw new OrmException("More than one entity found (expected exactly one)"); |
|
| 91 | } |
||
| 92 | |||
| 93 | 10 | return $result; |
|
| 94 | } |
||
| 95 | |||
| 96 | /** |
||
| 97 | * Find data sets by given criteria |
||
| 98 | * |
||
| 99 | * @param array $criteria |
||
| 100 | * Array of criterias in form of "property" => "value" |
||
| 101 | * @param string $orderBy |
||
| 102 | * An order-by statement in form of "property ASC|DESC" |
||
| 103 | * @param int $limit |
||
| 104 | * The maximum amount of results |
||
| 105 | * @param int $startOffset |
||
| 106 | * The offset where to get results of |
||
| 107 | * @param bool $asList |
||
| 108 | * Fetch results as list, also if number of results is one |
||
| 109 | * |
||
| 110 | * @return AbstractModel|array|null Either an array of entities, a single entity (if only one was found) or null |
||
| 111 | * |
||
| 112 | * @throws OrmException |
||
| 113 | */ |
||
| 114 | 29 | public static function find(array $criteria, string $orderBy = "", int $limit = 0, int $startOffset = 0, bool $asList = false) |
|
| 115 | { |
||
| 116 | 29 | $results = null; |
|
| 117 | |||
| 118 | 29 | $instance = self::getInstance(); |
|
| 119 | |||
| 120 | 29 | $class = get_called_class(); |
|
| 121 | |||
| 122 | 29 | $table = self::getTableName($class); |
|
| 123 | |||
| 124 | 29 | $escapeSign = $instance->getDbType()->getEscapeSign(); |
|
| 125 | |||
| 126 | 28 | $query = self::createQuery($class, $table, $criteria, array( |
|
| 127 | 28 | sprintf("%s%s%s.*", $escapeSign, $table, $escapeSign) |
|
| 128 | 28 | ), $orderBy, $limit, $startOffset, $escapeSign); |
|
| 129 | 27 | $statement = null; |
|
| 130 | |||
| 131 | try { |
||
| 132 | 27 | $statement = $instance->startTX()->prepare($query); |
|
| 133 | |||
| 134 | 27 | foreach ($criteria as $criterion => $value) { |
|
| 135 | 24 | $placeHolder = str_replace('.', '_', $criterion); |
|
| 136 | 24 | $placeHolder = str_replace('OR ', 'OR_', $placeHolder); |
|
| 137 | 24 | $value = str_ireplace('LIKE ', '', $value); |
|
| 138 | 24 | $statement->bindValue(":" . $placeHolder, $value); |
|
| 139 | } |
||
| 140 | |||
| 141 | 27 | $statement->execute(); |
|
| 142 | |||
| 143 | 27 | $unmapped = array(); |
|
| 144 | 27 | while ($result = $statement->fetch(\PDO::FETCH_OBJ)) { |
|
| 145 | 26 | $unmapped[] = $result; |
|
| 146 | } |
||
| 147 | |||
| 148 | 27 | $statement->closeCursor(); |
|
| 149 | |||
| 150 | 27 | foreach ($unmapped as $result) { |
|
| 151 | 26 | $results[] = self::map($result, $class, $instance); |
|
| 152 | } |
||
| 153 | |||
| 154 | 26 | if (! $asList && count($results) == 1) { |
|
| 155 | 21 | $results = $results[0]; |
|
| 156 | } |
||
| 157 | |||
| 158 | 26 | $instance->commitTX(); |
|
| 159 | 1 | } catch (\Exception $exception) { |
|
| 160 | 1 | throw self::handleException($instance, $statement, $exception, "Finding data set failed", - 100); |
|
| 161 | } |
||
| 162 | |||
| 163 | 26 | return $results; |
|
| 164 | } |
||
| 165 | |||
| 166 | /** |
||
| 167 | * Find data sets by given criteria |
||
| 168 | * |
||
| 169 | * @param array $criteria |
||
| 170 | * Array of criterias in form of "property" => "value" |
||
| 171 | * @param string $orderBy |
||
| 172 | * An order-by statement in form of "property ASC|DESC" |
||
| 173 | * @param int $limit |
||
| 174 | * The maximum amount of results |
||
| 175 | * @param int $startOffset |
||
| 176 | * The offset where to get results of |
||
| 177 | * |
||
| 178 | * @return mixed Either an array of object, a single object (if only one was found) or null |
||
| 179 | * |
||
| 180 | * @throws OrmException |
||
| 181 | */ |
||
| 182 | 1 | public static function findAll(array $criteria = array(), string $orderBy = "", int $limit = 0, int $startOffset = 0) |
|
| 183 | { |
||
| 184 | 1 | return self::find($criteria, $orderBy, $limit, $startOffset, true); |
|
| 185 | } |
||
| 186 | |||
| 187 | /** |
||
| 188 | * Persist the object into database |
||
| 189 | * |
||
| 190 | * @throws OrmException |
||
| 191 | */ |
||
| 192 | 13 | public function persist() |
|
| 193 | { |
||
| 194 | 13 | $instance = self::getInstance(); |
|
| 195 | |||
| 196 | 13 | $escapeSign = $instance->getDbType()->getEscapeSign(); |
|
| 197 | |||
| 198 | 13 | $entity = $this; |
|
| 199 | 13 | assert($entity instanceof \Nkey\Caribu\Model\AbstractModel); |
|
| 200 | 13 | $class = get_class($entity); |
|
| 201 | |||
| 202 | 13 | $tableName = self::getTableName($class); |
|
| 203 | |||
| 204 | 13 | self::persistAnnotated($class, $entity); |
|
| 205 | |||
| 206 | 13 | $pk = self::getPrimaryKey($class, $entity); |
|
| 207 | 12 | if (is_null($pk)) { |
|
| 208 | throw new OrmException("No primary key column found!"); |
||
| 209 | } |
||
| 210 | 12 | $primaryKeyCol = array_keys($pk)[0]; |
|
| 211 | 12 | $primaryKeyValue = array_values($pk)[0]; |
|
| 212 | |||
| 213 | 12 | $pairs = self::getAnnotatedColumnValuePairs($class, $entity); |
|
| 214 | |||
| 215 | 12 | $query = self::createUpdateStatement($class, $pairs, $primaryKeyCol, $primaryKeyValue, $escapeSign); |
|
| 216 | |||
| 217 | 12 | $connection = $instance->startTX(); |
|
| 218 | 12 | $statement = null; |
|
| 219 | |||
| 220 | try { |
||
| 221 | 12 | $statement = $connection->prepare($query); |
|
| 222 | |||
| 223 | 11 | foreach ($pairs as $column => $value) { |
|
| 224 | 11 | if ($value instanceof \Nkey\Caribu\Model\AbstractModel) { |
|
| 225 | 4 | $value = self::getPrimaryKey(get_class($value), $value, true); |
|
| 226 | } |
||
| 227 | 11 | $statement->bindValue(":{$column}", self::convertToDatabaseType(self::getColumnType($instance, $tableName, $column), $value)); |
|
| 228 | } |
||
| 229 | 11 | if ($primaryKeyValue) { |
|
| 230 | 6 | $statement->bindValue(":{$primaryKeyCol}", $primaryKeyValue); |
|
| 231 | } |
||
| 232 | |||
| 233 | 11 | $instance->getDbType()->lock($tableName, IType::LOCK_TYPE_WRITE, $instance); |
|
| 234 | 11 | $statement->execute(); |
|
| 235 | 11 | if (! $primaryKeyValue) { |
|
| 236 | 10 | $pk = $connection->lastInsertId($instance->getDbType() |
|
| 237 | 10 | ->getSequenceNameForColumn($tableName, $primaryKeyCol, $instance)); |
|
| 238 | } |
||
| 239 | 11 | $instance->getDbType()->unlock($tableName, $instance); |
|
| 240 | |||
| 241 | 11 | unset($statement); |
|
| 242 | |||
| 243 | 11 | if (! $primaryKeyValue) { |
|
| 244 | 10 | $this->setPrimaryKey($class, $entity, $pk); |
|
| 245 | } |
||
| 246 | |||
| 247 | 11 | $this->persistMappedBy($class, $entity); |
|
| 248 | |||
| 249 | 11 | $instance->commitTX(); |
|
| 250 | 1 | } catch (\PDOException $exception) { |
|
| 251 | 1 | $instance->getDbType()->unlock($tableName, $instance); |
|
| 252 | 1 | throw self::handleException($instance, $statement, $exception, "Persisting data set failed", - 1000); |
|
| 253 | } |
||
| 254 | 11 | } |
|
| 255 | |||
| 256 | /** |
||
| 257 | * Removes the current persisted entity |
||
| 258 | * |
||
| 259 | * @throws OrmException |
||
| 260 | */ |
||
| 261 | 3 | public function delete() |
|
| 262 | { |
||
| 263 | 3 | $instance = self::getInstance(); |
|
| 264 | |||
| 265 | 3 | assert($this instanceof \Nkey\Caribu\Model\AbstractModel); |
|
| 266 | |||
| 267 | 3 | $class = get_class($this); |
|
| 268 | |||
| 269 | 3 | $tableName = $this->getTableName($class); |
|
| 270 | |||
| 271 | 3 | $escapeSign = $this->getDbType()->getEscapeSign(); |
|
| 272 | |||
| 273 | 3 | $pk = self::getPrimaryKey($class, $this, false); |
|
|
0 ignored issues
–
show
|
|||
| 274 | 3 | $primaryKeyCol = array_keys($pk)[0]; |
|
| 275 | 3 | $primaryKeyValue = array_values($pk)[0]; |
|
| 276 | |||
| 277 | 3 | if (is_null($primaryKeyValue)) { |
|
| 278 | throw new OrmException("Entity is not persisted or detached. Can not delete."); |
||
| 279 | } |
||
| 280 | |||
| 281 | 3 | $query = sprintf("DELETE FROM %s%s%s WHERE %s%s%s = :%s", $escapeSign, $tableName, $escapeSign, $escapeSign, $primaryKeyCol, $escapeSign, $primaryKeyCol); |
|
| 282 | |||
| 283 | 3 | $statement = null; |
|
| 284 | |||
| 285 | try { |
||
| 286 | 3 | $statement = $instance->startTX()->prepare($query); |
|
| 287 | 3 | $statement->bindValue(":{$primaryKeyCol}", $primaryKeyValue); |
|
| 288 | |||
| 289 | 3 | $instance->getDbType()->lock($tableName, IType::LOCK_TYPE_WRITE, $instance); |
|
| 290 | 3 | $statement->execute(); |
|
| 291 | 3 | $instance->getDbType()->unlock($tableName, $instance); |
|
| 292 | 3 | unset($statement); |
|
| 293 | 3 | $instance->commitTX(); |
|
| 294 | } catch (\PDOException $exception) { |
||
| 295 | $instance->getDbType()->unlock($tableName, $instance); |
||
| 296 | throw self::handleException($instance, $statement, $exception, "Persisting data set failed", - 1000); |
||
| 297 | } |
||
| 298 | 3 | } |
|
| 299 | |||
| 300 | /** |
||
| 301 | * Destroy the orm instance |
||
| 302 | */ |
||
| 303 | 34 | public static function passivate() |
|
| 304 | { |
||
| 305 | 34 | if (self::$instance) { |
|
| 306 | 33 | self::$instance->passivateConnection(); |
|
| 307 | 33 | self::$instance = null; |
|
| 308 | } |
||
| 309 | 34 | } |
|
| 310 | } |
||
| 311 |
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.
Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.