Passed
Push — master ( cd5206...92cf14 )
by Tom
02:02
created

Maphper::offsetGet()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 1
Ratio 14.29 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 3
nop 1
dl 1
loc 7
rs 9.2
c 0
b 0
f 0
1
<?php
2
namespace Maphper;
3
class Maphper implements \Countable, \ArrayAccess, \IteratorAggregate {
4
	const FIND_EXACT 		= 	0x1;
5
	const FIND_LIKE 		= 	0x2;
6
	const FIND_STARTS 		= 	0x4;
7
	const FIND_ENDS 		= 	0x8;
8
	const FIND_BIT 			= 	0x10;
9
	const FIND_GREATER 		= 	0x20;
10
	const FIND_LESS 		=	0x40;
11
	const FIND_EXPRESSION 	= 	0x80;
12
	const FIND_AND 			= 	0x100;
13
	const FIND_OR 			= 	0x200;
14
	const FIND_NOT 			= 	0x400;
15
	const FIND_BETWEEN		= 	0x800;
16
	const FIND_NOCASE		= 	0x1000;
17
18
	private $dataSource;
19
	private $relations = [];
20
	private $settings = ['filter' => [], 'sort' => null, 'limit' => null, 'offset' => null, 'resultClass' => '\\stdClass'];
21
	private $iterator = 0;
22
23
	public function __construct(DataSource $dataSource, array $settings = [], array $relations = []) {
24
		$this->dataSource = $dataSource;
25
		$this->settings = array_replace($this->settings, $settings);
26
		$this->relations = $relations;
27
	}
28
29
	public function addRelation($name, Relation $relation) {
30
		$this->relations[$name] = $relation;
31
	}
32
33
	public function count($group = null) {
34
		return $this->dataSource->findAggregate('count', $group == null ? $this->dataSource->getPrimaryKey() : $group, $group, $this->settings['filter']);
35
	}
36
37
	//Allow filter(['user' => $user]) where $user is an object instead of
38
	//filter(['userId' => $user->id])
39
	private function wrapFilter() {
40
		foreach ($this->settings['filter'] as $name => $value) {
41
			if (isset($this->relations[$name])) {
42
				$filter = $this->relations[$name]->getFilter($value);
43
				$this->settings['filter'] = array_merge($this->settings['filter'], $filter);
44
				unset($this->settings['filter'][$name]);
45
			}
46
		}
47
	}
48
49
	private function getResults() {
50
		$this->wrapFilter();
51
		foreach ($this->settings['filter'] as $name => &$filter) {
52
			if (isset($this->relations[$name])) {
53
				$this->relations[$name]->overwrite($filter, $filter[$name]);
54
			}
55
		}
56
		$results = $this->dataSource->findByField($this->settings['filter'],
57
			['order' => $this->settings['sort'], 'limit' => $this->settings['limit'], 'offset' => $this->settings['offset'] ]);
58
59
		$siblings = new \ArrayObject();
60
61
		$results = array_map([$this, 'createNew'], $results);
62
		array_walk($results, [$this, 'wrap'], $siblings);
63
64
		return $results;
65
	}
66
67
	public function item($n) {
68
		$array = $this->getResults();
69
		return isset($array[$n]) ? $array[$n] : null;
70
	}
71
72
	public function getIterator() {
73
		return new Iterator($this->getResults(), $this->dataSource->getPrimaryKey());
74
	}
75
76
	private function processFilters($value) {
77
		//When saving to a mapper with filters, write the filters back into the object being stored
78
		foreach ($this->settings['filter'] as $key => $filterValue) {
79
			if (empty($value->$key) && !is_array($filterValue)) $value->$key = $filterValue;
80
		}
81
		return $value;
82
	}
83
84
	public function offsetSet($offset, $valueObj) {
85
		if ($valueObj instanceof \Maphper\Relation) throw new \Exception();
86
87
		//Extract private properties from the object
88
		$propertyReader = new \Maphper\Lib\VisibilityOverride();
89
		$value = $propertyReader->getProperties($valueObj);
90
91
		$value = $this->processFilters($value);
92
		$pk = $this->dataSource->getPrimaryKey();
93
		if ($offset !== null) $value->{$pk[0]} = $offset;
94
		$valueCopy = clone $value;
95
		$value = $this->wrap($value);
96
		$this->dataSource->save($value);
97
		$value = $this->wrap((object) array_merge((array)$value, (array)$valueCopy));
98
        $this->createNew($value, $valueObj);
99
	}
100
101
	public function offsetExists($offset) {
102 View Code Duplication
		if (count($this->dataSource->getPrimaryKey()) > 1) return new MultiPk($this, $offset, $this->dataSource->getPrimaryKey());
0 ignored issues
show
Bug Best Practice introduced by
The expression return new Maphper\Multi...ource->getPrimaryKey()) returns the type Maphper\MultiPk which is incompatible with the return type mandated by ArrayAccess::offsetExists() of boolean.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
103
        if (!empty($this->settings['filter'])) {
104
            $data = $this->dataSource->findByField(array_merge($this->settings['filter'], [$this->dataSource->getPrimaryKey()[0] => $offset]));
0 ignored issues
show
Bug introduced by
The call to Maphper\DataSource::findByField() has too few arguments starting with options. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

104
            /** @scrutinizer ignore-call */ 
105
            $data = $this->dataSource->findByField(array_merge($this->settings['filter'], [$this->dataSource->getPrimaryKey()[0] => $offset]));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
105
            return isset($data[0]);
106
        }
107
		return (bool) $this->dataSource->findById($offset);
108
	}
109
110
	public function offsetUnset($id) {
111
		$this->dataSource->deleteById($id);
112
	}
113
114
	public function offsetGet($offset) {
115 View Code Duplication
		if (count($this->dataSource->getPrimaryKey()) > 1) return new MultiPk($this, $offset, $this->dataSource->getPrimaryKey());
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
116
        if (!empty($this->settings['filter'])) {
117
            $data = $this->dataSource->findByField(array_merge($this->settings['filter'], [$this->dataSource->getPrimaryKey()[0] => $offset]));
0 ignored issues
show
Bug introduced by
The call to Maphper\DataSource::findByField() has too few arguments starting with options. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

117
            /** @scrutinizer ignore-call */ 
118
            $data = $this->dataSource->findByField(array_merge($this->settings['filter'], [$this->dataSource->getPrimaryKey()[0] => $offset]));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
118
            return $this->wrap($this->createNew(isset($data[0]) ? $data[0] : null));
119
        }
120
		return $this->wrap($this->createNew($this->dataSource->findById($offset)));
121
	}
122
123
	private function createNew($data = [], $obj = null) {
124
		if (!$obj) $obj = (is_callable($this->settings['resultClass'])) ? call_user_func($this->settings['resultClass']) : new $this->settings['resultClass'];
125
		$writer = new Lib\PropertyWriter($obj);
126
		$writer->write($data);
127
		return $obj;
128
	}
129
130
	private function wrap($object, $key = null, $siblings = null) {
131
		//see if any relations need overwriting
132
		foreach ($this->relations as $name => $relation) {
133
			if (isset($object->$name) && !($object->$name instanceof \Maphper\Relation) ) {
134
				//After overwriting the relation, does the parent object ($object) need overwriting as well?
135
				if ($relation->overwrite($object, $object->$name)) $this[] = $object;
136
			}
137
138
			$object->$name = $relation->getData($object, $siblings);
139
			//var_dump($siblings);
140
		}
141
		return $object;
142
	}
143
144
	public function __call($method, $args) {
145
		if (array_key_exists($method, $this->settings)) {
146
			$maphper = new Maphper($this->dataSource, $this->settings, $this->relations);
147
			if (is_array($maphper->settings[$method])) $maphper->settings[$method] = $args[0] + $maphper->settings[$method];
148
			else $maphper->settings[$method] = $args[0];
149
			return $maphper;
150
		}
151
		else throw new \Exception('Method Maphper::' . $method . ' does not exist');
152
	}
153
154
	public function findAggregate($function, $field, $group = null) {
155
		return $this->dataSource->findAggregate($function, $field, $group, $this->settings['filter']);
156
	}
157
158
	public function delete() {
159
		$this->dataSource->deleteByField($this->settings['filter'], ['order' => $this->settings['sort'], 'limit' => $this->settings['limit'], 'offset' => $this->settings['offset']]);
0 ignored issues
show
Unused Code introduced by
The call to Maphper\DataSource::deleteByField() has too many arguments starting with array('order' => $this->...is->settings['offset']). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

159
		$this->dataSource->/** @scrutinizer ignore-call */ 
160
                     deleteByField($this->settings['filter'], ['order' => $this->settings['sort'], 'limit' => $this->settings['limit'], 'offset' => $this->settings['offset']]);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
160
	}
161
}
162