Completed
Push — master ( e5a757...47ec18 )
by Robbie
14s
created

XMLDataFormatter::convertDataObjectSet()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 4
nop 2
1
<?php
2
3
namespace SilverStripe\RestfulServer\DataFormatter;
4
5
use SilverStripe\RestfulServer\DataFormatter;
6
use SilverStripe\ORM\DataObjectInterface;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\ORM\DataObject;
9
use SilverStripe\Control\Director;
10
use SilverStripe\Core\Convert;
11
use SilverStripe\ORM\SS_List;
12
13
/**
14
 * Formats a DataObject's member fields into an XML string
15
 */
16
class XMLDataFormatter extends DataFormatter
17
{
18
19
    /**
20
     * @config
21
     * @todo pass this from the API to the data formatter somehow
22
     */
23
    private static $api_base = "api/v1/";
24
25
    protected $outputContentType = 'text/xml';
26
27
    public function supportedExtensions()
28
    {
29
        return array(
30
            'xml'
31
        );
32
    }
33
34
    public function supportedMimeTypes()
35
    {
36
        return array(
37
            'text/xml',
38
            'application/xml',
39
        );
40
    }
41
42
    /**
43
     * Generate an XML representation of the given {@link DataObject}.
44
     *
45
     * @param DataObject $obj
46
     * @param $includeHeader Include <?xml ...?> header (Default: true)
0 ignored issues
show
Bug introduced by
The type SilverStripe\RestfulServer\DataFormatter\Include was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
47
     * @return String XML
48
     */
49
    public function convertDataObject(DataObjectInterface $obj, $fields = null)
50
    {
51
        $response = Controller::curr()->getResponse();
52
        if ($response) {
53
            $response->addHeader("Content-Type", "text/xml");
54
        }
55
56
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" . $this->convertDataObjectWithoutHeader($obj, $fields);
57
    }
58
59
    public function convertDataObjectWithoutHeader(DataObject $obj, $fields = null, $relations = null)
60
    {
61
        $className = $this->sanitiseClassName(get_class($obj));
62
        $id = $obj->ID;
63
        $objHref = Director::absoluteURL($this->config()->api_base . "$className/$obj->ID");
64
65
        $xml = "<$className href=\"$objHref.xml\">\n";
66
        foreach ($this->getFieldsForObj($obj) as $fieldName => $fieldType) {
67
            // Field filtering
68
            if ($fields && !in_array($fieldName, $fields)) {
69
                continue;
70
            }
71
            $fieldValue = $obj->obj($fieldName)->forTemplate();
72
            if (!mb_check_encoding($fieldValue, 'utf-8')) {
73
                $fieldValue = "(data is badly encoded)";
74
            }
75
76
            if (is_object($fieldValue) && is_subclass_of($fieldValue, 'Object') && $fieldValue->hasMethod('toXML')) {
77
                $xml .= $fieldValue->toXML();
78
            } else {
79
                if ('HTMLText' == $fieldType) {
80
                    // Escape HTML values using CDATA
81
                    $fieldValue = sprintf('<![CDATA[%s]]>', str_replace(']]>', ']]]]><![CDATA[>', $fieldValue));
82
                } else {
83
                    $fieldValue = Convert::raw2xml($fieldValue);
84
                }
85
                $xml .= "<$fieldName>$fieldValue</$fieldName>\n";
86
            }
87
        }
88
89
        if ($this->relationDepth > 0) {
90
            foreach ($obj->hasOne() as $relName => $relClass) {
91
                if (!singleton($relClass)->stat('api_access')) {
92
                    continue;
93
                }
94
95
                // Field filtering
96
                if ($fields && !in_array($relName, $fields)) {
97
                    continue;
98
                }
99
                if ($this->customRelations && !in_array($relName, $this->customRelations)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->customRelations of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
100
                    continue;
101
                }
102
103
                $fieldName = $relName . 'ID';
104 View Code Duplication
                if ($obj->$fieldName) {
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...
105
                    $href = Director::absoluteURL($this->config()->api_base . "$relClass/" . $obj->$fieldName);
106
                } else {
107
                    $href = Director::absoluteURL($this->config()->api_base . "$className/$id/$relName");
108
                }
109
                $xml .= "<$relName linktype=\"has_one\" href=\"$href.xml\" id=\"" . $obj->$fieldName
110
                    . "\"></$relName>\n";
111
            }
112
113 View Code Duplication
            foreach ($obj->hasMany() as $relName => $relClass) {
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...
114
                //remove dot notation from relation names
115
                $parts = explode('.', $relClass);
116
                $relClass = array_shift($parts);
117
                if (!singleton($relClass)->stat('api_access')) {
118
                    continue;
119
                }
120
                // backslashes in FQCNs kills both URIs and XML
121
                $relClass = $this->sanitiseClassName($relClass);
122
123
                // Field filtering
124
                if ($fields && !in_array($relName, $fields)) {
125
                    continue;
126
                }
127
                if ($this->customRelations && !in_array($relName, $this->customRelations)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->customRelations of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
128
                    continue;
129
                }
130
131
                $xml .= "<$relName linktype=\"has_many\" href=\"$objHref/$relName.xml\">\n";
132
                $items = $obj->$relName();
133
                if ($items) {
134
                    foreach ($items as $item) {
135
                        $href = Director::absoluteURL($this->config()->api_base . "$relClass/$item->ID");
136
                        $xml .= "<$relClass href=\"$href.xml\" id=\"{$item->ID}\"></$relClass>\n";
137
                    }
138
                }
139
                $xml .= "</$relName>\n";
140
            }
141
142 View Code Duplication
            foreach ($obj->manyMany() as $relName => $relClass) {
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...
143
                //remove dot notation from relation names
144
                $parts = explode('.', $relClass);
145
                $relClass = array_shift($parts);
146
                if (!singleton($relClass)->stat('api_access')) {
147
                    continue;
148
                }
149
                // backslashes in FQCNs kills both URIs and XML
150
                $relClass = $this->sanitiseClassName($relClass);
151
152
                // Field filtering
153
                if ($fields && !in_array($relName, $fields)) {
154
                    continue;
155
                }
156
                if ($this->customRelations && !in_array($relName, $this->customRelations)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->customRelations of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
157
                    continue;
158
                }
159
160
                $xml .= "<$relName linktype=\"many_many\" href=\"$objHref/$relName.xml\">\n";
161
                $items = $obj->$relName();
162
                if ($items) {
163
                    foreach ($items as $item) {
164
                        $href = Director::absoluteURL($this->config()->api_base . "$relClass/$item->ID");
165
                        $xml .= "<$relClass href=\"$href.xml\" id=\"{$item->ID}\"></$relClass>\n";
166
                    }
167
                }
168
                $xml .= "</$relName>\n";
169
            }
170
        }
171
172
        $xml .= "</$className>";
173
174
        return $xml;
175
    }
176
177
    /**
178
     * Generate an XML representation of the given {@link SS_List}.
179
     *
180
     * @param SS_List $set
181
     * @return String XML
182
     */
183
    public function convertDataObjectSet(SS_List $set, $fields = null)
184
    {
185
        Controller::curr()->getResponse()->addHeader("Content-Type", "text/xml");
186
        $className = $this->sanitiseClassName(get_class($set));
187
188
        $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
189
        $xml .= (is_numeric($this->totalSize)) ? "<$className totalSize=\"{$this->totalSize}\">\n" : "<$className>\n";
190
        foreach ($set as $item) {
191
            $xml .= $this->convertDataObjectWithoutHeader($item, $fields);
192
        }
193
        $xml .= "</$className>";
194
195
        return $xml;
196
    }
197
198
    public function convertStringToArray($strData)
199
    {
200
        return Convert::xml2array($strData);
201
    }
202
}
203