Issues (115)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/AbstractDataMapper.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SimpleORM;
4
5
/**
6
 * Description of AbstractDataMapper
7
 *
8
 * @author Dmitriy
9
 */
10
abstract class AbstractDataMapper implements RepositoryInterface, MapperInterface
11
{
12
	
13
	use TraitDataMapperEvent;
14
	
15
	
16
	/**
17
	 * адаптер для работы с бд
18
	 * @var type 
19
	 */
20
	protected $adapter;
21
	
22
	/**
23
	 * таблица для сущности
24
	 * @var type 
25
	 */
26
    protected $entityTable;	
27
28
	/**
29
	 * первичный ключ
30
	 * @var type 
31
	 */
32
	protected $key;
33
		
34
	/**
35
	 * Использование join при выборке
36
	 * @var type 
37
	 */
38
	protected $use_joins = false;
39
	
40
	/**
41
	 * Использование мягкое удаление
42
	 * @var type 
43
	 */
44
	protected $use_delete = false;
45
46
	/**
47
	 * поле для мягкого удаления
48
	 * @var type 
49
	 */
50
	protected $soft_delete_key;
51
	
52
53
	/**
54
	 * поля сущности 
55
	 * @var type 
56
	 */
57
	protected $mapping_fields = [];
58
	
59
	/**
60
	 * псевдонимы полей сущности
61
	 * @var type 
62
	 */
63
	protected $mapping_fields_aliases = [];
64
	
65
	/**
66
	 * связи с другими мапперами
67
	 * @var type 
68
	 */
69
	protected $relations = [];
70
71
	/**
72
	 * Контейнер
73
	 * @var League\Container\Container
74
	 */
75
	protected $DI;
76
	
77
	/**
78
	 * Возврат данных в массиве
79
	 * @var type 
80
	 */
81
	protected $result_array = false;
82
			
83
	function __construct(\League\Container\Container $DI, QueryBuilderInterface $adapter, $db_name = null) {
84
		
85
		$this->DI = $DI;
86
		
87
		$this->setMappingFields();
88
		
89
		$this->setAdapter($adapter);
90
		
91
		$this->setEntityTable($db_name);
92
		
93
		if($this->getEntityTable()=='' || $this->getPrimaryKey()==''){
94
			throw new InvalidEntityPropertyException('Свойства entityTable или key не заданы');
95
		}		
96
		
97
	}
98
	
99
	abstract protected function setMappingFields();	
100
	
101
    public function getAdapter() {
102
        return $this->adapter;
103
    }
104
105
	public function setAdapter(QueryBuilderInterface $adapter){
106
		 $this->adapter = $adapter;
107
	}
108
			
109
	
110
	protected function getEntityTable() {
111
		return $this->entityTable;
112
	}
113
114
	/**
115
	 * Уставнока таблицы
116
	 */
117
	protected function setEntityTable($db_name) {
118
		$this->entityTable = !empty($db_name)? "$db_name.".$this->table : $this->table;
119
	}	
120
121
	/**
122
	 * Получение записи по ключу
123
	 * @param type $id
124
	 * @return type
125
	 */
126
    public function findById($id)
127
    {
128
		$Criteria = (new Specification())->setWhere($this->key , $id);
129
		
130
        return $this->findBySpecification($Criteria);
131
    }	
132
	
133
	/**
134
	 * Сохранение сущности без спобытий
135
	 * @param \SimpleORM\EntityInterface $Entity
136
	 */
137
	public function saveWithoutEvents(EntityInterface $Entity) {
138
		
139
		$data = $this->unbuildEntity($Entity);
140
		
141
		if(method_exists($this, 'onPrepareData' )) $this->onPrepareData( $Entity , $data );
142
		
143
		$id = $data[$this->getPrimaryKey()];
144
		unset($data[$this->getPrimaryKey()]);		
145
			
146
		//insert
147
		if (empty($id)) {
148
			
149
			unset($data[$this->setSoftDeleteKey()]);
150
			
151
			$this->getAdapter()->insert($this->getEntityTable(),$data);
152
			
153
			if (!$id = $this->getAdapter()->insert_id()) {
154
				return false;
155
			}
156
			
157
			$Entity->setId($id);
158
		}
159
		//update
160
		else {
161
			
162
			if(!$this->getAdapter()->update($this->getEntityTable(), $data, "{$this->getPrimaryKey()} = '{$id}'")){
163
				return false;
164
			}
165
166
		}	
167
		
168
		return true;
169
	}
170
	
171
	/**
172
	 * Cохранение сущности
173
	 * @param EntityInterface $Entity
174
	 */
175
	public function save(EntityInterface $Entity)
176
	{
177
		if(method_exists($this, 'onAfterSave' )) $this->onAfterSave( $Entity );
178
		
179
		if(!$this->saveWithoutEvents($Entity)){
180
			return false;
181
		}
182
		
183
		if(method_exists($this, 'onBeforeSave' )) $this->onBeforeSave( $Entity );
184
185
		return true;
186
	}
187
	
188
	
189
	/**
190
	 * получение мапперов в порядке их использования с учетом вложенности
191
	 * @return array
192
	 */
193
	protected function createListRelation(){
194
		$rel_list = [];
195
		
196
		$rel_map = $this->getRelations();
197
		
198
		$this->createListRelationReq($rel_map,$rel_list);
199
		
200
		return $rel_list;
201
	}
202
	
203
	/**
204
	 * Выстроивает порядок использования мапперов в иерархии
205
	 * @param array $rel_map
206
	 * @param type $rel_list
207
	 */
208
	protected function createListRelationReq(array $rel_map,&$rel_list,$obj_parent_link = null) {
209
		
210
		foreach ($rel_map as $rel){
211
			
212
			$obj_link = '#'.$rel['alias'].'()';
213
			
214
			if(count($rel['relations'])>0){
215
				$this->createListRelationReq($rel['relations'],$rel_list,$obj_parent_link.'get'.$rel['alias'].'()');
216
				$rel_list [$obj_parent_link.$obj_link]= $rel['name'];
217
			}
218
			else{
219
				$rel_list [$obj_parent_link.$obj_link] = $rel['name'];
220
			}
221
		}
222
	}
223
224
	
225
	/**
226
	 * получить связи
227
	 */
228
	protected function getRelations(){
229
		$rel_map = [];
230
		foreach ($this->mapping_fields as $field => $cfg){
231
			if(isset($cfg['relation'])){		
232
				$rels = $this->DI->get($cfg['relation'])->getRelations();
233
				$rel_map []= [
234
					'name'		=>	$cfg['relation'],
235
					'alias'		=>	$field,
236
					'relations'	=>	$rels
237
				];
238
			}
239
		}
240
		return $rel_map;
241
	}
242
243
	
244
245
246
247
248
249
	/**
250
	 * Подготавливаем конечный вариант Сущности
251
	 * 
252
	 * @param \Core\Infrastructure\EntityInterface $Entity
253
	 * @param array $row
254
	 * @return \Core\Infrastructure\EntityInterface
255
	 * @throws BadMethodCallException
256
	 */
257
	protected function buildEntity(EntityInterface $Entity, array $row){
258
	//ed($row);
259
        foreach ($this->mapping_fields as $alias => $cfg ) {
260
			
261
			$value = false;
262
			
263
			//автоопределени формата массива
264
			$field = $cfg['field'];
265
			
266
			$method_set = 'set' . ucfirst($alias);
267
			
268
			if(!method_exists($Entity, $method_set )){
269
				throw new BadMethodCallException("Метод {$method_set}  не определен");
270
			}			
271
			
272
			//событие на формирование поля
273
			if( isset($cfg['build']) && is_object($cfg['build']) ){
274
				
275
//				ed([
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% 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...
276
//					get_class($Entity),
277
//					$row
278
//				]);
279
		
280
				
281
				$value = call_user_func($cfg['build'], $row);
282
			}
283
			//на связь
284
			elseif(isset($cfg['relation'])){
285
				
286
				$mapper = $this->DI->get($cfg['relation']);
287
				
288
				if($this->use_joins===true || empty($row[$field])){
289
					
290
					$value = $mapper->createEntity($row);
291
					
292
//					ed([
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% 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...
293
//					get_class($mapper),
294
//						$row,
295
//						$value
296
//					]);
297
298
				}
299
				else{
300
					$fkey = isset($cfg['on']) ? $cfg['on'] :$mapper->key;
301
					$value = $mapper->findBySpecification((new Specification)->setWhere($fkey, $row[$field]));
302
				}				
303
				
304
			}
305
			elseif(is_string($field) && isset($row[strtolower($field)])){				
306
				$value = $row[strtolower($field)];
307
			}
308
						
309
			if($value!==false)
310
				$Entity->{$method_set}($value);
311
			
312
        }
313
				
314
        return $Entity;		
315
	}	
316
317
	
318
	/**
319
	 * из объекта формирует массив
320
	 * @param \Core\Infrastructure\EntityInterface $Entity
321
	 * @return \Core\Infrastructure\EntityInterface
322
	 * @throws BadMethodCallException
323
	 */
324
	protected function unbuildEntity(EntityInterface $Entity){
325
		
326
		$row = [];
327
328
        foreach ($this->mapping_fields as $alias => $cfg ) {
329
			
330
			//автоопределени формата массива
0 ignored issues
show
Unused Code Comprehensibility introduced by
52% 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...
331
//			if(isset($row[$this->key])){
332
//				$field = $cfg['field'];
333
//			}
334
//			else{
335
//				$field = $alias;
336
//			}			
337
			$field = $cfg['field'];
338
			
339
			$method_get = 'get' . ucfirst($alias);
340
			
341
			if(!method_exists($Entity, $method_get )){
342
				throw new BadMethodCallException("Метод {$method_get}  не определен");
343
			}		
344
			
345
			//--------------------------------------------------------------------
346
			if( isset($cfg['unbuild']) && is_object($cfg['unbuild']) ){
347
				$value = call_user_func($cfg['unbuild'], $Entity->{$method_get}() );
348
			}
349
			elseif(isset($cfg['relation']) && is_object($Entity->{$method_get}()) ){
350
				
351
				if(isset($cfg['on']))
352
					$fkey = $this->DI->get($cfg['relation'])->getFieldAlias($cfg['on']);
353
				else
354
					$fkey = 'id';
355
				
356
				$value = $Entity->{$method_get}()->{'get'.$fkey}();
357
				
358
			}			
359
			else{
360
				$value = $Entity->{$method_get}();
361
			}			
362
						
363
			$row[$field] = $value;
364
365
        }
366
			
367
        return $row;		
368
	}	
369
	
370
	/**
371
	 * Установка поля для маппинга
372
	 */
373 5
	protected function addMappingField($alias,$field = null){
374
		
375 5
		if(is_string($field)){
376 1
			$field = ['field'	=>	$field];
377 1
		}
378 4
		elseif( (is_array($field) && !isset($field['field'])) || empty($field)){
379 1
			$field['field']	= $alias;
380 1
		}
381
	
382 5
		$this->mapping_fields[$alias] = $field;
383
384 5
		if(isset($field['primary']) && $field['primary']===true){
385 1
			$this->key = $field['field'];
386 1
		}
387
		
388 5
		if(isset($field['softdelete']) && $field['softdelete']===true){
389 1
			$this->soft_delete_key = $field['field'];
390 1
		}
391
		
392 5
		$this->mapping_fields_aliases[$field['field']] = $alias;
393
		
394 5
		return $this;
395
	}	
396
	
397
398
	
399
	/**
400
	 * Установка ключа
401
	 */
402 1
	public function getPrimaryKey() {
403 1
		return $this->key;
404
	}	
405
	
406
	/**
407
	 * Устанвка поля для мягкого удаляения
408
	 */
409 1
	protected function setSoftDeleteKey() {
410 1
		return $this->soft_delete_key;
411
	}
412
413
414
	/**
415
	 * получение алиаса поля
416
	 * @param type $field
417
	 * @return type
418
	 */
419 1
	public function getFieldAlias($field){
420 1
		return $this->mapping_fields_aliases[$field];		
421
	}	
422
	
423
	/**
424
	 * получение поля по алиасу
425
	 * @param type $alias
426
	 * @return type
427
	 */
428
	public function getAliasField($alias)
429
	{
430
		return $this->mapping_fields[$alias]['field'];
431
	}
432
	
433
	
434
	/**
435
	 * 
436
	 * @param ISpecificationCriteria $specification
437
	 * @return type
438
	 */
439
	public function findBySpecification(ISpecificationCriteria $specification){
440
441
		//псеводо удаление
442
		$this->setSoftDelete($specification);
443
		
444
		$this->setRelations($specification);
445
		
446
		$specification->setLimit(1);
447
		
448
		//получение записей
449
		$res = $this->getAdapter()->getResultQuery($this->getEntityTable(),$specification);
450
451
        if (!$row = $res->row_array()) {
452
            return null;
453
        }
454
		
455
		if($this->result_array===true){
456
			return $row;
457
		}
458
		
459
		return $this->createEntity($row);	
460
		
461
	}
462
	
463
	/**
464
	 * Удаление записи
465
	 * @param EntityInterface $Entity
466
	 * @return boolean
467
	 */
468
	public function delete(EntityInterface $Entity)
469
	{
470
		$result = false;
471
		
472
		$delete_key = $this->setSoftDeleteKey();
473
		
474
		if (
475
				$delete_key > '' && 
476
				$Entity->getId() > 0){
477
				$result = $this->getAdapter()->update($this->getEntityTable(), [ $delete_key => 1 ], "{$this->getPrimaryKey()} = '{$Entity->getId()}'");
478
		}
479
		elseif($Entity->getId() > 0){
480
			
481
			if($result = $this->getAdapter()->delete($this->getEntityTable(), $this->getPrimaryKey()."  = ".$Entity->getId())){
482
				if(method_exists($this, 'onBeforeDelete' )){ $result = $this->onBeforeDelete( $Entity );}
483
			}
484
		}
485
		
486
		return $result;
487
	}
488
489
	public function findAllBySpecification(ISpecificationCriteria $specification)
490
	{
491
492
		$entities = [];
0 ignored issues
show
$entities is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
493
		
494
		//псеводо удаление
495
		$this->setSoftDelete($specification);		
496
		
497
		$this->setRelations($specification);
498
		
499
		$res = $this->getAdapter()->getResultQuery($this->getEntityTable(),$specification);
500
		
501
		if (!$rows = $res->result_array()) {
502
            return null;
503
        }	
504
		
505
		if($this->result_array===true){
506
			return $rows;
507
		}		
508
		
509
		foreach($rows as $k =>  $row){
510
			$rows[$k] = $this->createEntity($row);
511
		}
512
		
513
		return $rows;		
514
	}
515
516
	public function findAll()
517
	{
518
		return $this->findAllBySpecification(new Specification());
519
	}
520
	
521
	/**
522
	 * Выборка удаленных моделей
523
	 * @param ISpecificationCriteria $specification
524
	 */
525
	protected function setSoftDelete(ISpecificationCriteria $specification){
526
		if(
527
				$this->use_delete === false &&
528
				$this->setSoftDeleteKey()>'' 
529
				&& !isset($specification->getWhere()[$this->setSoftDeleteKey()])
530
				)
531
		$specification->setWhere($this->setSoftDeleteKey(),0);
532
	}
533
	
534
	/**
535
	 * Построение join-ов
536
	 * 
537
	 * @todo добавить типы связей 
538
	 * has_many - один к многим (пост и коммеентарии)
539
	 * belongs_to - многие к многим (пользователь имет множество оплат одного заказа)
540
	 * has_one - один к одному
541
	 */
542
	protected function setRelations(ISpecificationCriteria $Specification){
543
544
		$joins = [];
545
546
		foreach ($this->mapping_fields as $field => $cfg){
547
			if(isset($cfg['relation'])){
548
				
549
				$reltype = isset($cfg['reltype']) ? $cfg['reltype'] : 'belongs_to';
550
				
551
				$this->relations[$field] = [
552
					'mapper'	=>	$mapper = $this->DI->get($cfg['relation']),
553
					'reltype'	=>  $reltype
554
				];
555
556
				$table = $mapper->getEntityTable();
557
558
				$relation_key = isset($cfg['on'])? $cfg['on'] : $mapper->key;
559
				
560
				$joins[$table] = [
561
						'alias'	=> $field,
562
						'type'	=> $reltype != 'has_many' ? 'INNER' : 'LEFT OUTER',
563
						'on'	=> "`{$this->table}`.{$cfg['field']} = `{$field}`.{$relation_key}"
564
				];
565
566
			}
567
		}	
568
569
		if($this->use_joins===true){
570
			$Specification->setJoins($joins);
571
		}			
572
	}
573
	
574
	/**
575
	 * Использование join-ов
576
	 */
577
	public function useJoins()
578
	{
579
		$o = clone $this;
580
		$o->use_joins = true;
581
		return $o;
582
	}
583
	
584
	public function withDelete(){
585
		$o = clone $this;
586
		$o->use_delete = true;
587
		return $o;
588
	}
589
	
590
	/**
591
	 * Данные только в массиве
592
	 * @return \SimpleORM\AbstractDataMapper
593
	 */
594
	public function resultArray(){
595
		$o = clone $this;
596
		$o->result_array = true;
597
		return $o;
598
	}	
599
	
600
}
601