Completed
Push — develop ( 6bb9f8...c1e8ff )
by Narcotic
41:48 queued 41:41
created

ReadOnlyFieldConstraint::propertyExists()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 8.8571
c 0
b 0
f 0
ccs 0
cts 18
cp 0
cc 5
eloc 12
nc 5
nop 2
crap 30
1
<?php
2
/**
3
 * Schema constraint that validates if readOnly: true fields are manipulated and rejects changes on those.
4
 */
5
6
namespace Graviton\SchemaBundle\Constraint;
7
8
use Graviton\JsonSchemaBundle\Validator\Constraint\Event\ConstraintEventSchema;
9
use Symfony\Component\PropertyAccess\PropertyAccess;
10
11
/**
12
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
13
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
14
 * @link     http://swisscom.ch
15
 */
16
class ReadOnlyFieldConstraint
17
{
18
19
    /**
20
     * @var array
21
     */
22
    private $fieldMap;
23
24
    /**
25
     * ReadOnlyFieldConstraint constructor.
26
     *
27
     * @param ConstraintUtils $utils             Utils
28
     * @param array           $readOnlyFieldsMap field map from compiler pass
29
     */
30
    public function __construct(ConstraintUtils $utils, array $readOnlyFieldsMap)
31
    {
32
        $this->utils = $utils;
33
        $this->fieldMap = $readOnlyFieldsMap;
34
    }
35
36
    /**
37
     * Checks the readOnly fields and sets error in event if needed
38
     *
39
     * @param ConstraintEventSchema $event event class
40
     *
41
     * @return void
42
     */
43
    public function checkReadOnlyFields(ConstraintEventSchema $event)
44
    {
45
        $schema = $event->getSchema();
46
        $data = $event->getElement();
47
48
        if (!isset($schema->{'x-documentClass'}) || !isset($data->id)) {
49
            return;
50
        }
51
52
        $documentClass = $schema->{'x-documentClass'};
53
54
        if (!isset($this->fieldMap[$documentClass])) {
55
            return;
56
        }
57
58
        $readOnlyFields = $this->fieldMap[$documentClass];
59
60
        // get the current record
61
        $currentRecord = $this->utils->getCurrentEntity();
62
63
        if (is_null($currentRecord)) {
64
            return;
65
        }
66
67
        // compare fields in both objects, if it doesn't exists in DB it can be updated.
68
        $accessor = PropertyAccess::createPropertyAccessor();
69
        foreach ($readOnlyFields as $fieldName) {
70
            $storedValue = null;
71
            if ($this->propertyExists($currentRecord, $fieldName) &&
72
                $accessor->isReadable($currentRecord, $fieldName)) {
73
                $storedValue = $accessor->getValue($currentRecord, $fieldName);
74
            }
75
76
            if (is_object($storedValue)) {
77
                // skip objects as a whole, we will test their readOnly properties instead
78
                continue;
79
            }
80
81
            $setValue = null;
82
            if ($this->propertyExists($data, $fieldName) &&
83
                $accessor->isReadable($data, $fieldName)) {
84
                $setValue = $accessor->getValue($data, $fieldName);
85
            }
86
87
            if ($storedValue && ($storedValue != $setValue)) {
88
                $event->addError(
89
                    sprintf('The value %s is read only.', json_encode($storedValue)),
90
                    $fieldName
91
                );
92
            }
93
        }
94
    }
95
96
    /**
97
     * To validate before accessor brakes with not found field
98
     *
99
     * @param object $object    To be parsed
100
     * @param string $fieldName Field name, dot chained.
101
     * @return bool
102
     */
103
    private function propertyExists($object, $fieldName)
0 ignored issues
show
Coding Style introduced by
function propertyExists() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
104
    {
105
        if (property_exists($object, $fieldName)) {
106
            return true;
107
        }
108
109
        foreach (explode('.', $fieldName) as $field) {
110
            if (property_exists($object, $field)) {
111
                $object = $object->{$field};
112
                if (!is_object($object)) {
113
                    break;
114
                }
115
                continue;
116
            } else {
117
                return false;
118
            }
119
        }
120
        return true;
121
    }
122
}
123