Completed
Push — master ( 14d2bd...06e609 )
by mw
81:37 queued 59:24
created

includes/export/SMW_Serializer_RDFXML.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
/**
4
 * File holding the SMWRDFXMLSerializer class that provides basic functions for
5
 * serialising OWL data in RDF/XML syntax.
6
 *
7
 * @ingroup SMW
8
 *
9
 * @author Markus Krötzsch
10
 */
11
12
/**
13
 * Class for serializing exported data (encoded as SMWExpData object) in
14
 * RDF/XML.
15
 *
16
 * @ingroup SMW
17
 */
18
class SMWRDFXMLSerializer extends SMWSerializer{
19
	/**
20
	 * True if the $pre_ns_buffer contains the beginning of a namespace
21
	 * declaration block to which further declarations for the current
22
	 * context can be appended.
23
	 */
24
	protected $namespace_block_started;
25
	/**
26
	 * True if the namespaces that are added at the current serialization stage
27
	 * become global, i.e. remain available for all later contexts. This is the
28
	 * case in RDF/XML only as long as the header has not been streamed to the
29
	 * client (reflected herein by calling flushContent()). Later, namespaces
30
	 * can only be added locally to individual elements, thus requiring them to
31
	 * be re-added multiple times if used in many elements.
32
	 */
33
	protected $namespaces_are_global;
34
35 13
	public function clear() {
36 13
		parent::clear();
37 13
		$this->namespaces_are_global = false;
38 13
		$this->namespace_block_started = false;
39 13
	}
40
41 13
	protected function serializeHeader() {
42 13
		$this->namespaces_are_global = true;
43 13
		$this->namespace_block_started = true;
44 13
		$this->pre_ns_buffer =
45
			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" .
46
			"<!DOCTYPE rdf:RDF[\n" .
47 13
			"\t<!ENTITY rdf " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&rdf;' ) ) . ">\n" .
48 13
			"\t<!ENTITY rdfs " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&rdfs;' ) ) . ">\n" .
49 13
			"\t<!ENTITY owl " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&owl;' ) ) . ">\n" .
50 13
			"\t<!ENTITY swivt " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&swivt;' ) ) . ">\n" .
51
			// A note on "wiki": this namespace is crucial as a fallback when it would be illegal to start e.g. with a number.
52
			// In this case, one can always use wiki:... followed by "_" and possibly some namespace, since _ is legal as a first character.
53 13
			"\t<!ENTITY wiki "  . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&wiki;' ) ) . ">\n" .
54 13
			"\t<!ENTITY category " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&category;' ) ) . ">\n" .
55 13
			"\t<!ENTITY property " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&property;' ) ) . ">\n" .
56 13
			"\t<!ENTITY wikiurl " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&wikiurl;' ) ) . ">\n" .
57 13
			"]>\n\n" .
58 13
			"<rdf:RDF\n" .
59 13
			"\txmlns:rdf=\"&rdf;\"\n" .
60 13
			"\txmlns:rdfs=\"&rdfs;\"\n" .
61 13
			"\txmlns:owl =\"&owl;\"\n" .
62 13
			"\txmlns:swivt=\"&swivt;\"\n" .
63 13
			"\txmlns:wiki=\"&wiki;\"\n" .
64 13
			"\txmlns:category=\"&category;\"\n" .
65 13
			"\txmlns:property=\"&property;\"";
66 13
		$this->global_namespaces = array( 'rdf' => true, 'rdfs' => true, 'owl' => true, 'swivt' => true, 'wiki' => true, 'property' => true, 'category' => true );
67 13
		$this->post_ns_buffer .= ">\n\n";
68 13
	}
69
70 13
	protected function serializeFooter() {
71 13
		$this->post_ns_buffer .= "\t<!-- Created by Semantic MediaWiki, https://www.semantic-mediawiki.org/ -->\n";
72 13
		$this->post_ns_buffer .= '</rdf:RDF>';
73 13
	}
74
75 13
	public function serializeDeclaration( $uri, $typename ) {
76 13
		$this->post_ns_buffer .= "\t<$typename rdf:about=\"$uri\" />\n";
77 13
	}
78
79 13
	public function serializeExpData( SMWExpData $expData ) {
80 13
		$this->serializeNestedExpData( $expData, '' );
81 13
		$this->serializeNamespaces();
82 13
		if ( !$this->namespaces_are_global ) {
83 1
			$this->pre_ns_buffer .= $this->post_ns_buffer;
84 1
			$this->post_ns_buffer = '';
85 1
			$this->namespace_block_started = false;
86
		}
87 13
	}
88
89 13
	public function flushContent() {
90 13
		$result = parent::flushContent();
91 13
		$this->namespaces_are_global = false; // must not be done before calling the parent method (which may declare namespaces)
92 13
		$this->namespace_block_started = false;
93 13
		return $result;
94
	}
95
96 2
	protected function serializeNamespace( $shortname, $uri ) {
97 2
		if ( $this->namespaces_are_global ) {
98 2
			$this->global_namespaces[$shortname] = true;
99 2
			$this->pre_ns_buffer .= "\n\t";
100
		} else {
101
			$this->pre_ns_buffer .= ' ';
102
		}
103 2
		$this->pre_ns_buffer .= "xmlns:$shortname=\"$uri\"";
104 2
	}
105
106
	/**
107
	 * Serialize the given SMWExpData object, possibly recursively with
108
	 * increased indentation.
109
	 *
110
	 * @param $expData SMWExpData containing the data to be serialised.
111
	 * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
112
	 */
113 13
	protected function serializeNestedExpData( SMWExpData $expData, $indent ) {
114 13
		$this->recordDeclarationTypes( $expData );
115
116 13
		$type = $expData->extractMainType()->getQName();
117 13
		if ( !$this->namespace_block_started ) { // start new ns block
118 1
			$this->pre_ns_buffer .= "\t$indent<$type";
119 1
			$this->namespace_block_started = true;
120
		} else { // continue running block
121 13
			$this->post_ns_buffer .= "\t$indent<$type";
122
		}
123
124 13
		if ( ( $expData->getSubject() instanceof SMWExpResource ) &&
0 ignored issues
show
The class SMWExpResource does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
125 13
		      !$expData->getSubject()->isBlankNode() ) {
126 13
			 $this->post_ns_buffer .= ' rdf:about="' . $expData->getSubject()->getUri() . '"';
127
		} // else: blank node, no "rdf:about"
128
129 13
		if ( count( $expData->getProperties() ) == 0 ) { // nothing else to export
130
			$this->post_ns_buffer .= " />\n";
131
		} else { // process data
132 13
			$this->post_ns_buffer .= ">\n";
133
134 13
			foreach ( $expData->getProperties() as $property ) {
135 13
				$prop_decl_queued = false;
136 13
				$isClassTypeProp = $this->isOWLClassTypeProperty( $property );
137
138 13
				foreach ( $expData->getValues( $property ) as $valueElement ) {
139 13
					$this->requireNamespace( $property->getNamespaceID(), $property->getNamespace() );
140
141 13
					if ( $valueElement instanceof SMWExpLiteral ) {
0 ignored issues
show
The class SMWExpLiteral does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
142 13
						$prop_decl_type = SMW_SERIALIZER_DECL_APROP;
143 13
						$this->serializeExpLiteral( $property, $valueElement, "\t\t$indent" );
144
					} elseif ( $valueElement instanceof SMWExpResource ) {
0 ignored issues
show
The class SMWExpResource does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
145 13
						$prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
146 13
						$this->serializeExpResource( $property, $valueElement, "\t\t$indent", $isClassTypeProp );
147
					} elseif ( $valueElement instanceof SMWExpData ) {
148 1
						$prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
149
150 1
						$collection = $valueElement->getCollection();
151 1
						if ( $collection !== false ) { // RDF-style collection (list)
152
							$this->serializeExpCollection( $property, $collection, "\t\t$indent", $isClassTypeProp );
153 1
						} elseif ( count( $valueElement->getProperties() ) > 0 ) { // resource with data
154
							$this->post_ns_buffer .= "\t\t$indent<" . $property->getQName() . ">\n";
155
							$this->serializeNestedExpData( $valueElement, "\t\t$indent" );
156
							$this->post_ns_buffer .= "\t\t$indent</" . $property->getQName() . ">\n";
157
						} else { // resource without data
158 1
							$this->serializeExpResource( $property, $valueElement->getSubject(), "\t\t$indent", $isClassTypeProp );
159
						}
160
					} // else: no other types of export elements
161
162 13
					if ( !$prop_decl_queued ) {
163 13
						$this->requireDeclaration( $property, $prop_decl_type );
0 ignored issues
show
The variable $prop_decl_type does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
164 13
						$prop_decl_queued = true;
165
					}
166
				}
167
			}
168 13
			$this->post_ns_buffer .= "\t$indent</" . $type . ">\n";
169
		}
170 13
	}
171
172
	/**
173
	 * Add to the output a serialization of a property assignment where an
174
	 * SMWExpLiteral is the object. It is assumed that a suitable subject
175
	 * block has already been openend.
176
	 *
177
	 * @param $expResourceProperty SMWExpNsResource the property to use
178
	 * @param $expLiteral SMWExpLiteral the data value to use
179
	 * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
180
	 */
181 13
	protected function serializeExpLiteral( SMWExpNsResource $expResourceProperty, SMWExpLiteral $expLiteral, $indent ) {
182 13
		$this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName();
183 13
		if ( $expLiteral->getDatatype() !== '' ) {
184 13
			$this->post_ns_buffer .= ' rdf:datatype="' . $expLiteral->getDatatype() . '"';
185
		}
186 13
		$this->post_ns_buffer .= '>' . $this->makeAttributeValueString( $expLiteral->getLexicalForm() ) .
187 13
			'</' . $expResourceProperty->getQName() . ">\n";
188 13
	}
189
190
	/**
191
	 * Add to the output a serialization of a property assignment where an
192
	 * SMWExpResource is the object. It is assumed that a suitable subject
193
	 * block has already been openend.
194
	 *
195
	 * @param $expResourceProperty SMWExpNsResource the property to use
196
	 * @param $expResource SMWExpResource the data value to use
197
	 * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
198
	 * @param $isClassTypeProp boolean whether the resource must be declared as a class
199
	 */
200 13
	protected function serializeExpResource( SMWExpNsResource $expResourceProperty, SMWExpResource $expResource, $indent, $isClassTypeProp ) {
201 13
		$this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName();
202 13
		if ( !$expResource->isBlankNode() ) {
203 13
			if ( ( $expResource instanceof SMWExpNsResource ) && ( $expResource->getNamespaceID() == 'wiki' ) ) {
204
				// very common case, reduce bandwidth
205 8
				$this->post_ns_buffer .= ' rdf:resource="&wiki;' . $expResource->getLocalName() . '"';
206
			} else {
207 13
				$uriValue = $this->makeAttributeValueString( $expResource->getUri() );
208 13
				$this->post_ns_buffer .= ' rdf:resource="' . $uriValue . '"';
209
			}
210
		}
211 13
		$this->post_ns_buffer .= "/>\n";
212 13
		if ( $isClassTypeProp ) {
213 5
			$this->requireDeclaration( $expResource, SMW_SERIALIZER_DECL_CLASS );
214
		}
215 13
	}
216
217
	/**
218
	 * Add a serialization of the given SMWExpResource to the output,
219
	 * assuming that an opening property tag is alerady there.
220
	 *
221
	 * @param $expResourceProperty SMWExpNsResource the property to use
222
	 * @param $expResource array of (SMWExpResource or SMWExpData)
223
	 * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
224
	 * @param $isClassTypeProp boolean whether the resource must be declared as a class
225
	 *
226
	 * @bug The $isClassTypeProp parameter is not properly taken into account.
227
	 * @bug Individual resources are not serialised properly.
228
	 */
229
	protected function serializeExpCollection( SMWExpNsResource $expResourceProperty, array $collection, $indent, $isClassTypeProp ) {
230
		$this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName() . " rdf:parseType=\"Collection\">\n";
231
		foreach ( $collection as $expElement ) {
232
			if ( $expElement instanceof SMWExpData ) {
233
				$this->serializeNestedExpData( $expElement, $indent );
234
			} else {
235
				// FIXME: the below is not the right thing to do here
236
				//$this->serializeExpResource( $expResourceProperty, $expElement, $indent );
237
			}
238
			if ( $isClassTypeProp ) {
239
				// FIXME: $expResource is undefined
240
				//$this->requireDeclaration( $expResource, SMW_SERIALIZER_DECL_CLASS );
241
			}
242
		}
243
		$this->post_ns_buffer .= "$indent</" . $expResourceProperty->getQName() . ">\n";
244
	}
245
246
	/**
247
	 * Escape a string in the special form that is required for values in
248
	 * DTD entity declarations in XML. Namely, this require the percent sign
249
	 * to be replaced.
250
	 *
251
	 * @param $string string to be escaped
252
	 * @return string
253
	 */
254 13
	protected function makeValueEntityString( $string ) {
255 13
		return "'" . str_replace( '%', '&#37;', $string ) . "'";
256
	}
257
258
	/**
259
	 * Escape a string as required for using it in XML attribute values.
260
	 *
261
	 * @param $string string to be escaped
262
	 * @return string
263
	 */
264 13
	protected function makeAttributeValueString( $string ) {
265 13
		return str_replace( array( '&', '>', '<' ), array( '&amp;', '&gt;', '&lt;' ), $string );
266
	}
267
268
}
269