Esempio n. 1
0
    /**
     * The xmlSerialize metod is called during xml writing.
     *
     * Use the $writer argument to write its own xml serialization.
     *
     * An important note: do _not_ create a parent element. Any element
     * implementing XmlSerializble should only ever write what's considered
     * its 'inner xml'.
     *
     * The parent of the current element is responsible for writing a
     * containing element.
     *
     * This allows serializers to be re-used for different element names.
     *
     * If you are opening new elements, you must also close them again.
     *
     * @param Writer $writer
     * @return void
     */
    function xmlSerialize(Writer $writer)
    {
        $reader = new Reader();
        // Wrapping the xml in a container, so root-less values can still be
        // parsed.
        $xml = <<<XML
<?xml version="1.0"?>
<xml-fragment xmlns="http://sabre.io/ns">{$this->getXml()}</xml-fragment>
XML;
        $reader->xml($xml);
        $elementNamespace = null;
        while ($reader->read()) {
            if ($reader->depth < 1) {
                // Skipping the root node.
                continue;
            }
            switch ($reader->nodeType) {
                case Reader::ELEMENT:
                    $writer->startElement($reader->getClark());
                    $empty = $reader->isEmptyElement;
                    while ($reader->moveToNextAttribute()) {
                        switch ($reader->namespaceURI) {
                            case '':
                                $writer->writeAttribute($reader->localName, $reader->value);
                                break;
                            case 'http://www.w3.org/2000/xmlns/':
                                // Skip namespace declarations
                                break;
                            default:
                                $writer->writeAttribute($reader->getClark(), $reader->value);
                                break;
                        }
                    }
                    if ($empty) {
                        $writer->endElement();
                    }
                    break;
                case Reader::CDATA:
                case Reader::TEXT:
                    $writer->text($reader->value);
                    break;
                case Reader::END_ELEMENT:
                    $writer->endElement();
                    break;
            }
        }
    }
Esempio n. 2
0
 /**
  * This function behaves similar to Sabre\Xml\Reader::parseCurrentElement,
  * but instead of creating deep xml array structures, it will turn any
  * top-level element it doesn't recognize into either a string, or an
  * XmlFragment class.
  *
  * This method returns arn array with 2 properties:
  *   * name - A clark-notation XML element name.
  *   * value - The parsed value.
  *
  * @param Reader $reader
  * @return array
  */
 private static function parseCurrentElement(Reader $reader)
 {
     $name = $reader->getClark();
     if (array_key_exists($name, $reader->elementMap)) {
         $deserializer = $reader->elementMap[$name];
         if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) {
             $value = call_user_func([$deserializer, 'xmlDeserialize'], $reader);
         } elseif (is_callable($deserializer)) {
             $value = call_user_func($deserializer, $reader);
         } else {
             $type = gettype($deserializer);
             if ($type === 'string') {
                 $type .= ' (' . $deserializer . ')';
             } elseif ($type === 'object') {
                 $type .= ' (' . get_class($deserializer) . ')';
             }
             throw new \LogicException('Could not use this type as a deserializer: ' . $type);
         }
     } else {
         $value = Complex::xmlDeserialize($reader);
     }
     return ['name' => $name, 'value' => $value];
 }
Esempio n. 3
0
 /**
  * The deserialize method is called during xml parsing.
  *
  * This method is called staticly, this is because in theory this method
  * may be used as a type of constructor, or factory method.
  *
  * Often you want to return an instance of the current class, but you are
  * free to return other data as well.
  *
  * Important note 2: You are responsible for advancing the reader to the
  * next element. Not doing anything will result in a never-ending loop.
  *
  * If you just want to skip parsing for this element altogether, you can
  * just call $reader->next();
  *
  * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
  * the next element.
  *
  * @param Xml\Reader $reader
  * @return mixed
  */
 static function xmlDeserialize(Xml\Reader $reader)
 {
     // If there's no children, we don't do anything.
     if ($reader->isEmptyElement) {
         $reader->next();
         return [];
     }
     $values = [];
     $reader->read();
     do {
         if ($reader->nodeType === Xml\Reader::ELEMENT) {
             $clark = $reader->getClark();
             $values[$clark] = $reader->parseCurrentElement()['value'];
         } else {
             $reader->read();
         }
     } while ($reader->nodeType !== Xml\Reader::END_ELEMENT);
     $reader->read();
     return $values;
 }
Esempio n. 4
0
 /**
  * The deserialize method is called during xml parsing.
  *
  * This method is called statictly, this is because in theory this method
  * may be used as a type of constructor, or factory method.
  *
  * Often you want to return an instance of the current class, but you are
  * free to return other data as well.
  *
  * You are responsible for advancing the reader to the next element. Not
  * doing anything will result in a never-ending loop.
  *
  * If you just want to skip parsing for this element altogether, you can
  * just call $reader->next();
  *
  * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
  * the next element.
  *
  * @param Reader $reader
  * @return mixed
  */
 static function xmlDeserialize(Reader $reader)
 {
     $reader->pushContext();
     $reader->elementMap['{DAV:}propstat'] = 'Sabre\\Xml\\Element\\KeyValue';
     // We are overriding the parser for {DAV:}prop. This deserializer is
     // almost identical to the one for Sabre\Xml\Element\KeyValue.
     //
     // The difference is that if there are any child-elements inside of
     // {DAV:}prop, that have no value, normally any deserializers are
     // called. But we don't want this, because a singular element without
     // child-elements implies 'no value' in {DAV:}prop, so we want to skip
     // deserializers and just set null for those.
     $reader->elementMap['{DAV:}prop'] = function (Reader $reader) {
         if ($reader->isEmptyElement) {
             $reader->next();
             return [];
         }
         $values = [];
         $reader->read();
         do {
             if ($reader->nodeType === Reader::ELEMENT) {
                 $clark = $reader->getClark();
                 if ($reader->isEmptyElement) {
                     $values[$clark] = null;
                     $reader->next();
                 } else {
                     $values[$clark] = $reader->parseCurrentElement()['value'];
                 }
             } else {
                 $reader->read();
             }
         } while ($reader->nodeType !== Reader::END_ELEMENT);
         $reader->read();
         return $values;
     };
     $elems = $reader->parseInnerTree();
     $reader->popContext();
     $href = null;
     $propertyLists = [];
     $statusCode = null;
     foreach ($elems as $elem) {
         switch ($elem['name']) {
             case '{DAV:}href':
                 $href = $elem['value'];
                 break;
             case '{DAV:}propstat':
                 $status = $elem['value']['{DAV:}status'];
                 list(, $status, ) = explode(' ', $status, 3);
                 $properties = isset($elem['value']['{DAV:}prop']) ? $elem['value']['{DAV:}prop'] : [];
                 $propertyLists[$status] = $properties;
                 break;
             case '{DAV:}status':
                 list(, $statusCode, ) = explode(' ', $elem['value'], 3);
                 break;
         }
     }
     return new self($href, $propertyLists, $statusCode);
 }
Esempio n. 5
0
 /**
  * Parse the curriculum definition file.
  *
  * By passing the official curriculum definition file (XML), this method
  * will parse it and return a curriculum definition it can understand and
  * treat. It mainly needs a "dictionary" of term types.
  *
  * @param string $curriculumXml
  *    The curriculum definition file, in XML.
  * @param string $variant
  *    (optional) The variant of the curriculum to parse. Defaults to 'V_EF'.
  *
  * @return array
  *    An object with 2 properties:
  *    - curriculum: A parsed and prepared curriculum tree. It uses
  *      Educa\DSB\Client\Curriculum\Term\LP21Term elements to define
  *      the curriculum tree.
  *    - dictionary: A dictionary of term identifiers, with name and type
  *      information for each one of them.
  *
  * @see \Educa\DSB\Client\Curriculum\LP21Curriculum::setCurriculumDictionary()
  */
 public static function parseCurriculumXml($curriculumXml, $variant = 'V_EF')
 {
     $reader = new Reader();
     // Prepare custom handlers for reading an XML node. See the Sabre\Xml
     // documentation for more information.
     $baseHandler = function ($reader) use($variant) {
         $node = new \stdClass();
         // Fetch the attributes. We want the UUID attribute.
         $attributes = $reader->parseAttributes();
         $node->uuid = trim($attributes['uuid']);
         // We derive the type from the node name.
         $node->type = strtolower(str_replace('{}', '', trim($reader->getClark())));
         // Give a default description.
         $node->description = (object) array('de' => '');
         // Fetch the descendants.
         $children = $reader->parseInnerTree();
         if (!empty($children)) {
             $node->children = array();
             foreach ($children as $child) {
                 // Look for child types that are relevant for us. Some must
                 // be parsed as child types of their own, others should be
                 // treated as being part of the current node.
                 if (in_array($child['name'], array('{}fach', '{}kompetenzbereich', '{}handlungs-themenaspekt', '{}kompetenz', '{}kompetenzstufe'))) {
                     $node->children[] = $child;
                 } elseif ($child['name'] == '{}bezeichnung') {
                     $node->description = (object) array_reduce($child['value'], function ($carry, $item) {
                         $langcode = strtolower(str_replace('{}', '', $item['name']));
                         $carry[$langcode] = $item['value'];
                         return $carry;
                     }, array());
                 } elseif ($child['name'] == '{}kantone') {
                     $node->cantons = array_map('trim', explode(',', $child['value']));
                 }
             }
         }
         if (!empty($node->cantons) && !in_array($variant, $node->cantons)) {
             return null;
         }
         return $node;
     };
     $kompetenzstufeHandler = function ($reader) use($variant) {
         $nodes = array();
         $cycle = $url = $version = $code = null;
         // Fetch the descendants.
         $children = $reader->parseInnerTree();
         if (!empty($children)) {
             foreach ($children as $child) {
                 if ($child['name'] == '{}absaetze') {
                     $nodes = $child['value'];
                 } elseif ($child['name'] == '{}zyklus') {
                     $cycle = trim($child['value']);
                 } elseif ($child['name'] == '{}lehrplanversion') {
                     $version = trim($child['value']);
                 } elseif ($child['name'] == '{}kanton' && $child['attributes']['id'] == $variant) {
                     foreach ($child['value'] as $grandChild) {
                         if ($grandChild['name'] == '{}code') {
                             $code = trim($grandChild['value']);
                         } elseif ($grandChild['name'] == '{}url') {
                             $url = trim($grandChild['value']);
                         }
                     }
                 }
             }
         }
         // Map all the Kompetenzstufe properties to the child Absaetzen.
         return array_map(function ($node) use($cycle, $url, $version, $code) {
             if (isset($cycle)) {
                 $node->cycle = $cycle;
             }
             if (isset($url)) {
                 $node->url = $url;
             }
             if (isset($version)) {
                 $node->version = $version;
             }
             if (isset($code)) {
                 $node->code = $code;
             }
             return $node;
         }, $nodes);
     };
     $absaetzeHandler = function ($reader) {
         $nodes = array();
         // Fetch the descendants.
         $children = $reader->parseInnerTree();
         if (!empty($children)) {
             foreach ($children as $child) {
                 if ($child['name'] == '{}bezeichnung') {
                     $node = new \stdClass();
                     // We treat it as a "Kompetenzstufe".
                     $node->type = 'kompetenzstufe';
                     $node->description = (object) array_reduce($child['value'], function ($carry, $item) {
                         $langcode = strtolower(str_replace('{}', '', $item['name']));
                         $carry[$langcode] = $item['value'];
                         return $carry;
                     }, array());
                     // The UUID is on the child Bezeichnung element, not our
                     // own node.
                     $node->uuid = $child['attributes']['uuid'];
                     $nodes[] = $node;
                 }
             }
         }
         return $nodes;
     };
     // Register our handler for the following node types. All others will be
     // treated with the default one provided by Sabre\Xml, but we don't
     // really care.
     $reader->elementMap = ['{}fachbereich' => $baseHandler, '{}fach' => $baseHandler, '{}kompetenzbereich' => $baseHandler, '{}handlungs-themenaspekt' => $baseHandler, '{}kompetenz' => $baseHandler, '{}kompetenzstufe' => $kompetenzstufeHandler, '{}absaetze' => $absaetzeHandler];
     // Parse the data.
     $reader->xml($curriculumXml);
     $data = $reader->parse();
     // Prepare the dictionary.
     $dictionary = array();
     // Prepare our root element.
     $root = new LP21Term('root', 'root');
     // Now, recursively parse the tree, transforming it into a tree of
     // LP21Term instances.
     $recurse = function ($tree, $parent) use(&$recurse, &$dictionary) {
         foreach ($tree as $item) {
             // Fetch our nodes.
             $nodes = $item['value'];
             if (!is_array($nodes)) {
                 $nodes = [$nodes];
             }
             // Double check the format. Is this one of our nodes?
             foreach ($nodes as $node) {
                 if (isset($node->uuid) && isset($node->type) && isset($node->description)) {
                     $term = new LP21Term($node->type, $node->uuid, $node->description);
                     $parent->addChild($term);
                     // Add it to our dictionary.
                     $dictionary[$node->uuid] = (object) array('name' => $node->description, 'type' => $node->type);
                     // Do we have an objective code?
                     if (!empty($node->code)) {
                         $term->setCode($node->code);
                         $dictionary[$node->uuid]->code = $node->code;
                     }
                     // Do we have any cantons information?
                     if (!empty($node->cantons)) {
                         $term->setCantons($node->cantons);
                         $dictionary[$node->uuid]->cantons = $node->cantons;
                     }
                     // Do we have curriculum version information?
                     if (!empty($node->version)) {
                         $term->setVersion($node->version);
                         $dictionary[$node->uuid]->version = $node->version;
                     }
                     // Do we have URL information?
                     if (!empty($node->url)) {
                         $term->setUrl($node->url);
                         $dictionary[$node->uuid]->url = $node->url;
                     }
                     // Do we have cycle information?
                     if (!empty($node->cycle)) {
                         $cycles = str_split($node->cycle);
                         $term->setCycles($cycles);
                         $dictionary[$node->uuid]->cycles = $cycles;
                     }
                     if (!empty($node->children)) {
                         $recurse($node->children, $term);
                     }
                 }
             }
         }
     };
     $recurse($data['value'], $root);
     // Return the parsed data.
     return (object) array('curriculum' => $root, 'dictionary' => $dictionary);
 }
Esempio n. 6
0
 /**
  * The deserialize method is called during xml parsing.
  *
  * This method is called statictly, this is because in theory this method
  * may be used as a type of constructor, or factory method.
  *
  * Often you want to return an instance of the current class, but you are
  * free to return other data as well.
  *
  * Important note 2: You are responsible for advancing the reader to the
  * next element. Not doing anything will result in a never-ending loop.
  *
  * If you just want to skip parsing for this element altogether, you can
  * just call $reader->next();
  *
  * $reader->parseSubTree() will parse the entire sub-tree, and advance to
  * the next element.
  *
  * @param Xml\Reader $reader
  * @return mixed
  */
 static function xmlDeserialize(Xml\Reader $reader)
 {
     // If there's no children, we don't do anything.
     if ($reader->isEmptyElement) {
         $reader->next();
         return [];
     }
     $reader->read();
     $currentDepth = $reader->depth;
     $values = [];
     do {
         if ($reader->nodeType === Xml\Reader::ELEMENT) {
             $values[] = $reader->getClark();
         }
     } while ($reader->depth >= $currentDepth && $reader->next());
     $reader->next();
     return $values;
 }
Esempio n. 7
0
/**
 * The 'enum' deserializer parses elements into a simple list
 * without values or attributes.
 *
 * For example, Elements will parse:
 *
 * <?xml version="1.0"? >
 * <s:root xmlns:s="http://sabredav.org/ns">
 *   <s:elem1 />
 *   <s:elem2 />
 *   <s:elem3 />
 *   <s:elem4>content</s:elem4>
 *   <s:elem5 attr="val" />
 * </s:root>
 *
 * Into:
 *
 * [
 *   "{http://sabredav.org/ns}elem1",
 *   "{http://sabredav.org/ns}elem2",
 *   "{http://sabredav.org/ns}elem3",
 *   "{http://sabredav.org/ns}elem4",
 *   "{http://sabredav.org/ns}elem5",
 * ];
 *
 * This is useful for 'enum'-like structures.
 *
 * If the $namespace argument is specified, it will strip the namespace
 * for all elements that match that.
 *
 * For example,
 *
 * enum($reader, 'http://sabredav.org/ns')
 *
 * would return:
 *
 * [
 *   "elem1",
 *   "elem2",
 *   "elem3",
 *   "elem4",
 *   "elem5",
 * ];
 *
 * @param Reader $reader
 * @param string $namespace
 * @return string[]
 */
function enum(Reader $reader, $namespace = null)
{
    // If there's no children, we don't do anything.
    if ($reader->isEmptyElement) {
        $reader->next();
        return [];
    }
    $reader->read();
    $currentDepth = $reader->depth;
    $values = [];
    do {
        if ($reader->nodeType !== Reader::ELEMENT) {
            continue;
        }
        if (!is_null($namespace) && $namespace === $reader->namespaceURI) {
            $values[] = $reader->localName;
        } else {
            $values[] = $reader->getClark();
        }
    } while ($reader->depth >= $currentDepth && $reader->next());
    $reader->next();
    return $values;
}