/** * @return string PHP code */ public function __toString() { $parameters = array(); foreach ($this->parameters as $param) { $variadic = $this->variadic && $param === end($this->parameters); $hint = in_array($param->getTypeHint(), array('array', '')) ? $param->getTypeHint() : ($this->namespace ? $this->namespace->unresolveName($param->getTypeHint()) : $param->getTypeHint()); $parameters[] = ($hint ? $hint . ' ' : '') . ($param->isReference() ? '&' : '') . ($variadic ? '...' : '') . '$' . $param->getName() . ($param->isOptional() && !$variadic ? ' = ' . Helpers::dump($param->defaultValue) : ''); } $uses = array(); foreach ($this->uses as $param) { $uses[] = ($param->isReference() ? '&' : '') . '$' . $param->getName(); } return ($this->documents ? str_replace("\n", "\n * ", "/**\n" . implode("\n", (array) $this->documents)) . "\n */\n" : '') . ($this->abstract ? 'abstract ' : '') . ($this->final ? 'final ' : '') . ($this->visibility ? $this->visibility . ' ' : '') . ($this->static ? 'static ' : '') . 'function' . ($this->returnReference ? ' &' : '') . ($this->name ? ' ' . $this->name : '') . '(' . implode(', ', $parameters) . ')' . ($this->uses ? ' use (' . implode(', ', $uses) . ')' : '') . ($this->abstract || $this->body === FALSE ? ';' : ($this->name ? "\n" : ' ') . "{\n" . Nette\Utils\Strings::indent(trim($this->body), 1) . "\n}"); }
protected function createGrid() { $context = $this->getItems(); $grid = $this->createPreparedGrid(); $grid->setModel($context); $grid->addColumnNumber('id', 'ID')->setSortable()->setFilterNumber(); $grid->addColumnText('name', 'Název')->setCustomRender(function ($row) { return Strings::indent($row['name'], $row['level'] * 5, ' '); })->setSortable()->setFilterText(); $grid->addColumnText('type_name', 'Typ'); $activeCol = $grid->addColumnText('show', 'Zobrazit'); $this->helpers->setupAsBool($activeCol); $grid->addActionEvent('left', '<', $this->clickLeft); $grid->addActionEvent('right', '>', $this->clickRight); $grid->addActionEvent('up', '/\\', $this->clickUp); $grid->addActionEvent('down', '\\/', $this->clickDown); $grid->addActionHref('add', 'Add'); $this->helpers->addEditAction($grid); $this->helpers->addDeleteEvent($grid, $this->deleteRow); return $grid; }
/** @return string PHP code */ public function __toString() { $consts = array(); foreach ($this->consts as $name => $value) { $consts[] = "const {$name} = " . Helpers::dump($value) . ";\n"; } $properties = array(); foreach ($this->properties as $property) { $properties[] = ($property->documents ? str_replace("\n", "\n * ", "/**\n" . implode("\n", (array) $property->documents)) . "\n */\n" : '') . $property->visibility . ($property->static ? ' static' : '') . ' $' . $property->name . ($property->value === null ? '' : ' = ' . Helpers::dump($property->value)) . ";\n"; } return Nette\Utils\Strings::normalize(($this->documents ? str_replace("\n", "\n * ", "/**\n" . implode("\n", (array) $this->documents)) . "\n */\n" : '') . ($this->abstract ? 'abstract ' : '') . ($this->final ? 'final ' : '') . $this->type . ' ' . $this->name . ' ' . ($this->extends ? 'extends ' . implode(', ', (array) $this->extends) . ' ' : '') . ($this->implements ? 'implements ' . implode(', ', (array) $this->implements) . ' ' : '') . "\n{\n\n" . Nette\Utils\Strings::indent(($this->traits ? "use " . implode(', ', (array) $this->traits) . ";\n\n" : '') . ($this->consts ? implode('', $consts) . "\n\n" : '') . ($this->properties ? implode("\n", $properties) . "\n\n" : '') . implode("\n\n\n", $this->methods), 1) . "\n\n}") . "\n"; }
/** * @return string PHP code */ public function __toString() { $consts = array(); foreach ($this->consts as $name => $value) { $consts[] = "const {$name} = " . Helpers::dump($value) . ";\n"; } $properties = array(); foreach ($this->properties as $property) { $doc = str_replace("\n", "\n * ", implode("\n", $property->getDocuments())); $properties[] = ($property->getDocuments() ? strpos($doc, "\n") === FALSE ? "/** {$doc} */\n" : "/**\n * {$doc}\n */\n" : '') . $property->getVisibility() . ($property->isStatic() ? ' static' : '') . ' $' . $property->getName() . ($property->value === NULL ? '' : ' = ' . Helpers::dump($property->value)) . ";\n"; } $namespace = $this->namespace; $mapper = function (array $arr) use($namespace) { return $namespace ? array_map(array($namespace, 'unresolveName'), $arr) : $arr; }; return Strings::normalize(($this->documents ? str_replace("\n", "\n * ", "/**\n" . implode("\n", $this->documents)) . "\n */\n" : '') . ($this->abstract ? 'abstract ' : '') . ($this->final ? 'final ' : '') . $this->type . ' ' . $this->name . ' ' . ($this->extends ? 'extends ' . implode(', ', $mapper((array) $this->extends)) . ' ' : '') . ($this->implements ? 'implements ' . implode(', ', $mapper($this->implements)) . ' ' : '') . "\n{\n" . Strings::indent(($this->traits ? 'use ' . implode(";\nuse ", $mapper($this->traits)) . ";\n\n" : '') . ($this->consts ? implode('', $consts) . "\n" : '') . ($this->properties ? implode("\n", $properties) . "\n" : '') . ($this->methods ? "\n" . implode("\n\n\n", $this->methods) . "\n\n" : ''), 1) . '}') . "\n"; }
public function formatBody($body, $request = TRUE) { if (empty($body)) { return ''; } $body = $this->tryFormatJson($body, $request); $result = Strings::indent($body, 8, ' '); $result = Strings::indent("+ Body" . PHP_EOL . PHP_EOL . $result, 4, ' '); return PHP_EOL . $result; }
/** * @return string PHP code */ public function __toString() { $consts = array(); foreach ($this->consts as $name => $value) { $consts[] = "const {$name} = " . Helpers::dump($value) . ";\n"; } $properties = array(); foreach ($this->properties as $property) { $doc = str_replace("\n", "\n * ", implode("\n", (array) $property->getDocuments())); $properties[] = ($property->getDocuments() ? strpos($doc, "\n") === FALSE ? "/** {$doc} */\n" : "/**\n * {$doc}\n */\n" : '') . $property->getVisibility() . ($property->isStatic() ? ' static' : '') . ' $' . $property->getName() . ($property->value === NULL ? '' : ' = ' . Helpers::dump($property->value)) . ";\n"; } $extends = $implements = $traits = array(); if ($this->namespace) { foreach ((array) $this->extends as $name) { $extends[] = $this->namespace->unresolveName($name); } foreach ((array) $this->implements as $name) { $implements[] = $this->namespace->unresolveName($name); } foreach ((array) $this->traits as $name) { $traits[] = $this->namespace->unresolveName($name); } } else { $extends = (array) $this->extends; $implements = (array) $this->implements; $traits = (array) $this->traits; } foreach ($this->methods as $method) { $method->setNamespace($this->namespace); } return Strings::normalize(($this->documents ? str_replace("\n", "\n * ", "/**\n" . implode("\n", (array) $this->documents)) . "\n */\n" : '') . ($this->abstract ? 'abstract ' : '') . ($this->final ? 'final ' : '') . $this->type . ' ' . $this->name . ' ' . ($this->extends ? 'extends ' . implode(', ', $extends) . ' ' : '') . ($this->implements ? 'implements ' . implode(', ', $implements) . ' ' : '') . "\n{\n\n" . Strings::indent(($this->traits ? 'use ' . implode(', ', $traits) . ";\n\n" : '') . ($this->consts ? implode('', $consts) . "\n\n" : '') . ($this->properties ? implode("\n", $properties) . "\n\n" : '') . implode("\n\n\n", $this->methods), 1) . "\n\n}") . "\n"; }
/** * @return string PHP code */ public function __toString() : string { $uses = []; asort($this->uses); foreach ($this->uses as $alias => $name) { $useNamespace = Helpers::extractNamespace($name); if ($this->name !== $useNamespace) { if ($alias === $name || substr($name, -(strlen($alias) + 1)) === '\\' . $alias) { $uses[] = "use {$name};"; } else { $uses[] = "use {$name} as {$alias};"; } } } $body = ($uses ? implode("\n", $uses) . "\n\n" : '') . implode("\n", $this->classes); if ($this->bracketedSyntax) { return 'namespace' . ($this->name ? ' ' . $this->name : '') . " {\n\n" . Strings::indent($body) . "\n}\n"; } else { return ($this->name ? "namespace {$this->name};\n\n" : '') . $body; } }
/** * Indents the HTML content from the left. * @param string UTF-8 encoding or 8-bit * @param int * @param string * @return string */ public static function indent($s, $level = 1, $chars = "\t") { if ($level >= 1) { $s = Strings::replace($s, '#<(textarea|pre).*?</\\1#si', function ($m) { return strtr($m[0], " \t\r\n", ""); }); $s = Strings::indent($s, $level, $chars); $s = strtr($s, "", " \t\r\n"); } return $s; }
/** @return string PHP code */ public function __toString() { $parameters = array(); foreach ($this->parameters as $param) { $parameters[] = ($param->typeHint ? $param->typeHint . ' ' : '') . ($param->reference ? '&' : '') . '$' . $param->name . ($param->optional ? ' = ' . Helpers::dump($param->defaultValue) : ''); } $uses = array(); foreach ($this->uses as $param) { $uses[] = ($param->reference ? '&' : '') . '$' . $param->name; } return ($this->documents ? str_replace("\n", "\n * ", "/**\n" . implode("\n", (array) $this->documents)) . "\n */\n" : '') . ($this->abstract ? 'abstract ' : '') . ($this->final ? 'final ' : '') . ($this->visibility ? $this->visibility . ' ' : '') . ($this->static ? 'static ' : '') . 'function' . ($this->returnReference ? ' &' : '') . ($this->name ? ' ' . $this->name : '') . '(' . implode(', ', $parameters) . ')' . ($this->uses ? ' use (' . implode(', ', $uses) . ')' : '') . ($this->abstract || $this->body === FALSE ? ';' : ($this->name ? "\n" : ' ') . "{\n" . Nette\Utils\Strings::indent(trim($this->body), 1) . "\n}"); }
/** * @return string */ public function __toString() { $this->setBody(''); if (strtolower($this->getName()) === '__construct') { $this->addParameter('_kdyby_aopContainer')->setTypeHint('\\Nette\\DI\\Container'); $this->addBody('$this->_kdyby_aopContainer = $_kdyby_aopContainer;'); } $this->addBody('$__arguments = func_get_args(); $__exception = $__result = NULL;'); if ($this->before) { foreach ($this->before as $before) { $this->addBody($before); } } if ($this->afterThrowing || $this->after) { $this->addBody('try {'); } if (!$this->around) { $parentCall = Code\Helpers::format('$__result = call_user_func_array("parent::?", $__arguments);', $this->getName()); } else { $parentCall = Code\Helpers::format('$__around = new \\Kdyby\\Aop\\JoinPoint\\AroundMethod($this, __FUNCTION__, $__arguments);'); foreach ($this->around as $around) { $parentCall .= "\n" . $around; } $parentCall .= "\n" . Code\Helpers::format('$__result = $__around->proceed();'); } $this->addBody($this->afterThrowing || $this->after ? Nette\Utils\Strings::indent($parentCall) : $parentCall); if ($this->afterThrowing || $this->after) { $this->addBody('} catch (\\Exception $__exception) {'); } if ($this->afterThrowing) { foreach ($this->afterThrowing as $afterThrowing) { $this->addBody(Nette\Utils\Strings::indent($afterThrowing)); } } if ($this->afterThrowing || $this->after) { $this->addBody('}'); } if ($this->afterReturning) { if ($this->afterThrowing || $this->after) { $this->addBody('if (empty($__exception)) {'); } foreach ($this->afterReturning as $afterReturning) { $this->addBody($this->afterThrowing || $this->after ? Nette\Utils\Strings::indent($afterReturning) : $afterReturning); } if ($this->afterThrowing || $this->after) { $this->addBody('}'); } } if ($this->after) { foreach ($this->after as $after) { $this->addBody($after); } } if ($this->afterThrowing || $this->after) { $this->addBody('if ($__exception) { throw $__exception; }'); } $this->addBody('return $__result;'); return parent::__toString(); }
/** * @return string PHP code */ public function __toString() : string { $parameters = []; foreach ($this->parameters as $param) { $variadic = $this->variadic && $param === end($this->parameters); $hint = $this->namespace ? $this->namespace->unresolveName((string) $param->getTypeHint()) : $param->getTypeHint(); $parameters[] = ($hint ? $hint . ' ' : '') . ($param->isReference() ? '&' : '') . ($variadic ? '...' : '') . '$' . $param->getName() . ($param->isOptional() && !$variadic ? ' = ' . Helpers::dump($param->defaultValue) : ''); } $uses = []; foreach ($this->uses as $param) { $uses[] = ($param->isReference() ? '&' : '') . '$' . $param->getName(); } $returnType = $this->namespace ? $this->namespace->unresolveName((string) $this->returnType) : $this->returnType; return ($this->comment ? str_replace("\n", "\n * ", "/**\n" . $this->comment) . "\n */\n" : '') . ($this->abstract ? 'abstract ' : '') . ($this->final ? 'final ' : '') . ($this->visibility ? $this->visibility . ' ' : '') . ($this->static ? 'static ' : '') . 'function' . ($this->returnReference ? ' &' : '') . ' ' . $this->name . '(' . implode(', ', $parameters) . ')' . ($this->uses ? ' use (' . implode(', ', $uses) . ')' : '') . ($returnType ? ': ' . $returnType : '') . ($this->abstract || $this->body === FALSE ? ';' : ($this->name ? "\n" : ' ') . "{\n" . Nette\Utils\Strings::indent(ltrim(rtrim($this->body) . "\n"), 1) . '}'); }
public function onGenerate(AbstractMetaSpec $spec, MetaSpecMatcher $matcher, Type $type, ClassType $class) { $ns = $class->getNamespace(); $ns->addUse("Skrz\\Meta\\XML\\XmlMetaInterface"); $ns->addUse($type->getName(), null, $typeAlias); $class->addImplement("Skrz\\Meta\\XML\\XmlMetaInterface"); $groups = array(); $i = 0; $valueGroupIdMask = 0; foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlAnnotationInterface") as $xmlAnnotation) { /** @var XmlAnnotationInterface $xmlAnnotation */ if (!isset($groups[$xmlAnnotation->getGroup()])) { $groups[$xmlAnnotation->getGroup()] = 1 << $i++; } if ($xmlAnnotation instanceof XmlValue) { $valueGroupIdMask |= $groups[$xmlAnnotation->getGroup()]; } } } $class->addProperty("xmlGroups", $groups)->setStatic(true); // fromXml() $fromXml = $class->addMethod("fromXml"); $fromXml->setStatic(true); $fromXml->addParameter("xml"); $fromXml->addParameter("group")->setOptional(true); $fromXml->addParameter("object")->setOptional(true); $fromXml->addComment("Creates \\{$type->getName()} from XML")->addComment("")->addComment("@param \\XMLReader|\\DOMElement \$xml")->addComment("@param string \$group")->addComment("@param {$typeAlias} \$object")->addComment("")->addComment("@throws \\InvalidArgumentException")->addComment("")->addComment("@return {$typeAlias}"); $fromXml->addBody("if (!isset(self::\$xmlGroups[\$group])) {")->addBody("\tthrow new \\InvalidArgumentException('Group \\'' . \$group . '\\' not supported for ' . " . var_export($type->getName(), true) . " . '.');")->addBody("} else {")->addBody("\t\$id = self::\$xmlGroups[\$group];")->addBody("}")->addBody("")->addBody("if (\$object === null) {")->addBody("\t\$object = new {$typeAlias}();")->addBody("} elseif (!(\$object instanceof {$typeAlias})) {")->addBody("\tthrow new \\InvalidArgumentException('You have to pass object of class {$type->getName()}.');")->addBody("}")->addBody("")->addBody("if (\$xml instanceof \\XMLReader) {")->addBody("\treturn self::fromXmlReader(\$xml, \$group, \$id, \$object);")->addBody("} elseif (\$xml instanceof \\DOMElement) {")->addBody("\treturn self::fromXmlElement(\$xml, \$group, \$id, \$object);")->addBody("} else {")->addBody("\tthrow new \\InvalidArgumentException('Expected XMLReader or DOMElement, got ' . gettype(\$xml) . (is_object(\$xml) ? ' of class ' . get_class(\$xml) : '') . '.');")->addBody("}"); $fromXmlReader = $class->addMethod("fromXmlReader"); $fromXmlReader->setStatic(true)->setVisibility("private"); $fromXmlReader->addParameter("xml")->setTypeHint("\\XMLReader"); $fromXmlReader->addParameter("group"); $fromXmlReader->addParameter("id"); $fromXmlReader->addParameter("object")->setTypeHint($type->getName()); $fromXmlReader->addBody("if (\$xml->nodeType !== \\XMLReader::ELEMENT) {")->addBody("\tthrow new \\InvalidArgumentException('Expects XMLReader to be positioned on ELEMENT node.');")->addBody("}")->addBody(""); $attributesByName = array(); foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlAttribute") as $xmlAttribute) { /** @var XmlAttribute $xmlAttribute */ $groupId = $groups[$xmlAttribute->group]; $name = strtolower($xmlAttribute->name); if (!isset($attributesByName[$name])) { $attributesByName[$name] = ""; } $attributesByName[$name] .= "if ((\$id & {$groupId}) > 0 && \$xml->namespaceURI === " . var_export($xmlAttribute->namespace, true) . ") {\n"; $attributesByName[$name] .= Strings::indent($this->assignObjectProperty($xmlAttribute, $property, "\$xml->value"), 1, "\t") . "\n"; $attributesByName[$name] .= "}\n"; } } if (!empty($attributesByName)) { $fromXmlReader->addBody("if (\$xml->moveToFirstAttribute()) {")->addBody("\tdo {")->addBody("\t\tswitch (strtolower(\$xml->localName)) {"); $i = 0; foreach ($attributesByName as $name => $code) { $fromXmlReader->addBody("\t\t\tcase " . var_export($name, true) . ":")->addBody(Strings::indent($code, 4, "\t"))->addBody("\t\t\t\tbreak;"); if ($i < count($attributesByName) - 1) { $fromXmlReader->addBody(""); } ++$i; } $fromXmlReader->addBody("\t\t}")->addBody("\t} while (\$xml->moveToNextAttribute());")->addBody("")->addBody("\t\$xml->moveToElement();")->addBody("}")->addBody(""); } $fromXmlReader->addBody("if ((\$id & {$valueGroupIdMask}) > 0) {"); $valueCount = 0; foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlValue") as $xmlValue) { /** @var XmlValue $xmlValue */ $groupId = $groups[$xmlValue->group]; $fromXmlReader->addBody("\tif ((\$id & {$groupId}) > 0) {")->addBody("\t\t\$value = self::xmlReadValue(\$xml);")->addBody(Strings::indent($this->assignObjectProperty($xmlValue, $property, "\$value"), 2, "\t"))->addBody("\t}")->addBody(""); ++$valueCount; } } if (!$valueCount) { $fromXmlReader->addBody("\t// @XmlValue not specified"); } $fromXmlReader->addBody("} else {"); $elementsByName = array(); $endElementsByName = array(); $wrappers = []; foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElementWrapper") as $xmlElementWrapper) { /** @var XmlElementWrapper $xmlElementWrapper */ $groupId = $groups[$xmlElementWrapper->group]; $name = strtolower($xmlElementWrapper->name); $wrapperId = $xmlElementWrapper->group . ":" . $property->getName(); if (!isset($wrappers[$wrapperId])) { $wrappers[$wrapperId] = 1 << count($wrappers); } if (!isset($elementsByName[$name])) { $elementsByName[$name] = ""; } $elementsByName[$name] .= "if ((\$id & {$groupId}) > 0 && \$xml->namespaceURI === " . var_export($xmlElementWrapper->namespace, true) . " && \$depth === 2) {\n"; $elementsByName[$name] .= "\t\$wrapped |= {$wrappers[$wrapperId]};\n"; $elementsByName[$name] .= "}\n"; if (!isset($endElementsByName[$name])) { $endElementsByName[$name] = ""; } $endElementsByName[$name] .= "if ((\$id & {$groupId}) > 0 && \$xml->namespaceURI === " . var_export($xmlElementWrapper->namespace, true) . " && \$depth === 2) {\n"; $endElementsByName[$name] .= "\t\$wrapped &= ~{$wrappers[$wrapperId]};\n"; $endElementsByName[$name] .= "}\n"; } foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElement") as $xmlElement) { /** @var XmlElement $xmlElement */ $groupId = $groups[$xmlElement->group]; $name = strtolower($xmlElement->name); $wrapperId = $xmlElement->group . ":" . $property->getName(); if (!isset($elementsByName[$name])) { $elementsByName[$name] = ""; } $isArray = false; $propertyType = $property->getType(); if ($propertyType instanceof ArrayType) { $isArray = true; $propertyType = $propertyType->getBaseType(); } if ($propertyType instanceof ArrayType) { throw new MetaException("fromXml() cannot process multi-dimensional arrays ({$type->getName()}::\${$property->getName()})."); } $elementsByName[$name] .= "if ((\$id & {$groupId}) > 0 && \$xml->namespaceURI === " . var_export($xmlElement->namespace, true) . " && "; if (isset($wrappers[$wrapperId])) { $elementsByName[$name] .= "(\$depth === 2 || (\$depth === 3 && (\$wrapped & {$wrappers[$wrapperId]}) > 0))"; } else { $elementsByName[$name] .= "\$depth === 2"; } $elementsByName[$name] .= ") {\n"; if ($propertyType instanceof Type) { $propertyTypeMetaClassName = $spec->createMetaClassName($propertyType); $ns->addUse($propertyTypeMetaClassName, null, $propertyTypeMetaClassNameAlias); $elementsByName[$name] .= "\t\$value = {$propertyTypeMetaClassNameAlias}::fromXml(\$xml, \$group" . ($isArray ? "" : ", isset(\$object->{$property->getName()}) ? \$object->{$property->getName()} : null") . ");\n"; } else { $elementsByName[$name] .= "\t\$value = self::xmlReadValue(\$xml);\n"; } $elementsByName[$name] .= Strings::indent($this->assignObjectProperty($xmlElement, $property, "\$value", $isArray), 1, "\t") . "\n"; $elementsByName[$name] .= "}\n"; } } if (empty($elementsByName)) { $fromXmlReader->addBody("\t// @XmlElement not specified"); } else { $fromXmlReader->addBody("\t\$depth = intval(!\$xml->isEmptyElement);")->addBody("\t\$wrapped = 0;")->addBody("\twhile (\$depth > 0 && \$xml->read()) {")->addBody("\t\tif (\$xml->nodeType === \\XMLReader::ELEMENT) {")->addBody("\t\t\t++\$depth;")->addBody("\t\t\tswitch (strtolower(\$xml->localName)) {"); $i = 0; foreach ($elementsByName as $name => $code) { $fromXmlReader->addBody("\t\t\t\tcase " . var_export($name, true) . ":")->addBody(Strings::indent($code, 5, "\t"))->addBody("\t\t\t\t\tbreak;"); if ($i < count($elementsByName) - 1) { $fromXmlReader->addBody(""); } ++$i; } $fromXmlReader->addBody("\t\t\t}")->addBody("\t\t}")->addBody("")->addBody("\t\tif (\$xml->nodeType === \\XMLReader::END_ELEMENT || (\$xml->nodeType === \\XMLReader::ELEMENT && \$xml->isEmptyElement)) {"); if (!empty($endElementsByName)) { $fromXmlReader->addBody("\t\t\tswitch (strtolower(\$xml->localName)) {"); $i = 0; foreach ($endElementsByName as $name => $code) { $fromXmlReader->addBody("\t\t\t\tcase " . var_export($name, true) . ":")->addBody(Strings::indent($code, 5, "\t"))->addBody("\t\t\t\t\tbreak;"); if ($i < count($endElementsByName) - 1) { $fromXmlReader->addBody(""); } ++$i; } $fromXmlReader->addBody("\t\t\t}"); } $fromXmlReader->addBody("\t\t\t--\$depth;")->addBody("\t\t}")->addBody("\t}"); } $fromXmlReader->addBody("}")->addBody(""); $fromXmlReader->addBody("return \$object;"); $xmlReadValue = $class->addMethod("xmlReadValue"); $xmlReadValue->setStatic(true)->setVisibility("private"); $xmlReadValue->addParameter("xml")->setTypeHint("\\XMLReader"); $xmlReadValue->addBody("\$value = null;")->addBody("\$valueDepth = intval(!\$xml->isEmptyElement);")->addBody("while (\$valueDepth > 0 && \$xml->read()) {")->addBody("\tif (\$xml->nodeType === \\XMLReader::ELEMENT && !\$xml->isEmptyElement) {")->addBody("\t\t++\$valueDepth;")->addBody("\t} elseif (\$xml->nodeType === \\XMLReader::END_ELEMENT) {")->addBody("\t\t--\$valueDepth;")->addBody("\t} elseif (\$xml->nodeType === \\XMLReader::TEXT || \$xml->nodeType === \\XMLReader::CDATA) {")->addBody("\t\t\$value .= \$xml->value;")->addBody("\t}")->addBody("}")->addBody("return \$value;"); $fromXmlElement = $class->addMethod("fromXmlElement"); $fromXmlElement->setStatic(true)->setVisibility("private"); $fromXmlElement->addParameter("xml")->setTypeHint("\\DOMElement"); $fromXmlElement->addParameter("group"); $fromXmlElement->addParameter("id"); $fromXmlElement->addParameter("object")->setTypeHint($type->getName()); foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlAttribute") as $xmlAttribute) { /** @var XmlAttribute $xmlAttribute */ $groupId = $groups[$xmlAttribute->group]; if ($xmlAttribute->namespace) { $fromXmlElement->addBody("if ((\$id & {$groupId}) > 0 && " . "\$xml->hasAttributeNS(" . var_export($xmlAttribute->namespace, true) . ", " . var_export($xmlAttribute->name, true) . ")) {"); $expr = "\$xml->getAttributeNS(" . var_export($xmlAttribute->namespace, true) . ", " . var_export($xmlAttribute->name, true) . ")"; } else { $fromXmlElement->addBody("if ((\$id & {$groupId}) > 0 && " . "\$xml->hasAttribute(" . var_export($xmlAttribute->name, true) . ")) {"); $expr = "\$xml->getAttribute(" . var_export($xmlAttribute->name, true) . ")"; } $fromXmlElement->addBody(Strings::indent($this->assignObjectProperty($xmlAttribute, $property, $expr), 1, "\t"))->addBody("}")->addBody(""); } } $fromXmlElement->addBody("if ((\$id & {$valueGroupIdMask}) > 0) {"); $valueCount = 0; foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlValue") as $xmlValue) { /** @var XmlValue $xmlValue */ $groupId = $groups[$xmlValue->group]; $fromXmlElement->addBody("\tif ((\$id & {$groupId}) > 0) {")->addBody(Strings::indent($this->assignObjectProperty($xmlValue, $property, "\$xml->textContent"), 2, "\t"))->addBody("\t}")->addBody(""); ++$valueCount; } } if (!$valueCount) { $fromXmlElement->addBody("\t// @XmlValue not specified"); } $fromXmlElement->addBody("} elseif (\$xml->childNodes->length > 0) {"); $elementsByName = array(); $wrappers = array(); foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElementWrapper") as $xmlElementWrapper) { /** @var XmlElementWrapper $xmlElementWrapper */ $groupId = $groups[$xmlElementWrapper->group]; $name = strtolower($xmlElementWrapper->name); $wrapperId = $xmlElementWrapper->group . ":" . $property->getName(); if (!isset($wrappers[$wrapperId])) { $wrappers[$wrapperId] = 1 << count($wrappers); } if (!isset($elementsByName[$name])) { $elementsByName[$name] = ""; } $elementsByName[$name] .= "if ((\$id & {$groupId}) > 0 && \$xml->namespaceURI === " . var_export(empty($xmlElementWrapper->namespace) ? null : $xmlElementWrapper->namespace, true) . " && \$sp === 0 && \$node->childNodes->length > 0) {\n"; $elementsByName[$name] .= "\t\$wrapped |= {$wrappers[$wrapperId]};\n"; $elementsByName[$name] .= "\t\$push = true;\n"; $elementsByName[$name] .= "}\n"; } foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElement") as $xmlElement) { /** @var XmlElement $xmlElement */ $groupId = $groups[$xmlElement->group]; $name = strtolower($xmlElement->name); $wrapperId = $xmlElement->group . ":" . $property->getName(); if (!isset($elementsByName[$name])) { $elementsByName[$name] = ""; } $isArray = false; $propertyType = $property->getType(); if ($propertyType instanceof ArrayType) { $isArray = true; $propertyType = $propertyType->getBaseType(); } if ($propertyType instanceof ArrayType) { throw new MetaException("fromXml() cannot process multi-dimensional arrays ({$type->getName()}::\${$property->getName()})."); } $elementsByName[$name] .= "if ((\$id & {$groupId}) > 0 && \$xml->namespaceURI === " . var_export(empty($xmlElement->namespace) ? null : $xmlElement->namespace, true) . " && "; if (isset($wrappers[$wrapperId])) { $elementsByName[$name] .= "(\$sp === 0 || (\$sp === 1 && (\$wrapped & {$wrappers[$wrapperId]}) > 0))"; } else { $elementsByName[$name] .= "\$sp === 0"; } $elementsByName[$name] .= ") {\n"; if ($propertyType instanceof Type) { $propertyTypeMetaClassName = $spec->createMetaClassName($propertyType); $ns->addUse($propertyTypeMetaClassName, null, $propertyTypeMetaClassNameAlias); $elementsByName[$name] .= "\t\$value = {$propertyTypeMetaClassNameAlias}::fromXml(\$node, \$group" . ($isArray ? "" : ", isset(\$object->{$property->getName()}) ? \$object->{$property->getName()} : null") . ");\n"; } else { $elementsByName[$name] .= "\t\$value = \$node->textContent;\n"; } $elementsByName[$name] .= Strings::indent($this->assignObjectProperty($xmlElement, $property, "\$value", $isArray), 1, "\t") . "\n"; $elementsByName[$name] .= "}\n"; } } if (empty($elementsByName)) { $fromXmlElement->addBody("\t// @XmlElement not specified"); } else { $fromXmlElement->addBody("\t\$stack = [[\$xml->childNodes, 0]];")->addBody("\t\$sp = 0;")->addBody("\t\$wrapped = 0;")->addBody("\t\$push = false;")->addBody("\twhile (!empty(\$stack)) {")->addBody("\t\t\$node = \$stack[\$sp][0]->item(\$stack[\$sp][1]);")->addBody("\t\tif (\$node->nodeType !== XML_ELEMENT_NODE) {")->addBody("\t\t\tcontinue;")->addBody("\t\t}")->addBody("")->addBody("\t\tswitch (strtolower(\$node->localName)) {"); $i = 0; foreach ($elementsByName as $name => $code) { $fromXmlElement->addBody("\t\t\tcase " . var_export($name, true) . ":")->addBody(Strings::indent($code, 4, "\t"))->addBody("\t\t\t\tbreak;"); if ($i < count($elementsByName) - 1) { $fromXmlElement->addBody(""); } ++$i; } $fromXmlElement->addBody("\t\t}")->addBody("\t\t++\$stack[\$sp][1];")->addBody("\t\tif (\$stack[\$sp][1] >= \$stack[\$sp][0]->length) {")->addBody("\t\t\tunset(\$stack[\$sp]);")->addBody("\t\t\t--\$sp;")->addBody("\t\t\t\$wrapped = 0;")->addBody("\t\t}")->addBody("\t\tif (\$push) {")->addBody("\t\t\t\$push = false;")->addBody("\t\t\t\$stack[++\$sp] = [\$node->childNodes, 0];")->addBody("\t\t}")->addBody("\t}"); } $fromXmlElement->addBody("}")->addBody(""); $fromXmlElement->addBody("return \$object;"); // toXml() $toXml = $class->addMethod("toXml"); $toXml->setStatic(true); $toXml->addParameter("object"); $toXml->addParameter("group")->setOptional(true); $toXml->addParameter("filterOrXml"); $toXml->addParameter("xml")->setOptional(true); $toXml->addParameter("el")->setOptional(true); $toXml->addComment("Serializes \\{$type->getName()} to XML")->addComment("")->addComment("@param {$typeAlias} \$object")->addComment("@param string \$group")->addComment("@param array|\\XMLWriter|\\DOMDocument \$filterOrXml")->addComment("@param \\XMLWriter|\\DOMDocument|\\DOMElement \$xml")->addComment("@param \\DOMElement \$el")->addComment("")->addComment("@throws \\InvalidArgumentException")->addComment("")->addComment("@return \\DOMElement|void"); $ns->addUse("Skrz\\Meta\\Stack", null, $stackAlias); $toXml->addBody("if (\$object === null) {")->addBody("\treturn null;")->addBody("}")->addBody("")->addBody("if (!isset(self::\$xmlGroups[\$group])) {")->addBody("\tthrow new \\InvalidArgumentException('Group \\'' . \$group . '\\' not supported for ' . " . var_export($type->getName(), true) . " . '.');")->addBody("} else {")->addBody("\t\$id = self::\$xmlGroups[\$group];")->addBody("}")->addBody("")->addBody("if (!(\$object instanceof {$typeAlias})) {")->addBody("\tthrow new \\InvalidArgumentException('You have to pass object of class {$type->getName()}.');")->addBody("}")->addBody("")->addBody("if ({$stackAlias}::\$objects === null) {")->addBody("\t{$stackAlias}::\$objects = new \\SplObjectStorage();")->addBody("}")->addBody("")->addBody("if ({$stackAlias}::\$objects->contains(\$object)) {")->addBody("\treturn null;")->addBody("}")->addBody("")->addBody("{$stackAlias}::\$objects->attach(\$object);")->addBody("")->addBody("if (\$filterOrXml instanceof \\XMLWriter || \$filterOrXml instanceof \\DOMDocument) {")->addBody("\t\$filter = null;")->addBody("\t\$el = \$xml;")->addBody("\t\$xml = \$filterOrXml;")->addBody("} else {")->addBody("\t\$filter = \$filterOrXml;")->addBody("}")->addBody("")->addBody("try {")->addBody("\tif (\$xml instanceof \\XMLWriter) {")->addBody("\t\tself::toXmlWriter(\$object, \$group, \$id, \$filter, \$xml);")->addBody("\t\t\$ret = null;")->addBody("\t} elseif (\$xml instanceof \\DOMDocument) {")->addBody("\t\t\$ret = self::toXmlElement(\$object, \$group, \$id, \$filter, \$xml, \$el);")->addBody("\t} else {")->addBody("\t\tthrow new \\InvalidArgumentException('You have to supply either XMLWriter, or DOMDocument.');")->addBody("\t}")->addBody("} catch (\\Exception \$e) {")->addBody("\t{$stackAlias}::\$objects->detach(\$object);")->addBody("\tthrow \$e;")->addBody("}")->addBody("")->addBody("{$stackAlias}::\$objects->detach(\$object);")->addBody("")->addBody("return \$ret;"); $toXmlWriter = $class->addMethod("toXmlWriter"); $toXmlWriter->setStatic(true)->setVisibility("private"); $toXmlWriter->addParameter("object")->setTypeHint($type->getName()); $toXmlWriter->addParameter("group"); $toXmlWriter->addParameter("id"); $toXmlWriter->addParameter("filter"); $toXmlWriter->addParameter("xml")->setTypeHint("\\XMLWriter"); $toXmlWriter->addBody("if (count({$stackAlias}::\$objects) < 2) {"); foreach ($type->getAnnotations("Skrz\\Meta\\XML\\XmlElement") as $xmlElement) { /** @var XmlElement $xmlElement */ $groupId = $groups[$xmlElement->group]; $toXmlWriter->addBody("\tif ((\$id & {$groupId}) > 0) {"); if ($xmlElement->namespace) { $toXmlWriter->addBody("\t\t\$xml->startElementNS(null, " . var_export($xmlElement->name, true) . ", " . var_export($xmlElement->namespace, true) . ");"); } else { $toXmlWriter->addBody("\t\t\$xml->startElement(" . var_export($xmlElement->name, true) . ");"); } $toXmlWriter->addBody("\t}"); } $toXmlWriter->addBody("}")->addBody(""); foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlAttribute") as $xmlAttribute) { /** @var XmlAttribute $xmlAttribute */ $groupId = $groups[$xmlAttribute->group]; $toXmlWriter->addBody("if ((\$id & {$groupId}) > 0 && isset(\$object->{$property->getName()}) && (\$filter === null || isset(\$filter[" . var_export("@" . $xmlAttribute->name, true) . "]))) {"); $matchingPropertySerializer = null; foreach ($this->propertySerializers as $propertySerializer) { if ($propertySerializer->matchesSerialize($property, $xmlAttribute->getGroup())) { $matchingPropertySerializer = $propertySerializer; break; } } if ($matchingPropertySerializer) { $sevo = $matchingPropertySerializer->serialize($property, $xmlAttribute->group, "\$object->{$property->getName()}"); } elseif ($property->getType() instanceof ScalarType) { $sevo = StatementAndExpressionVO::withExpression("(string)\$object->{$property->getName()}"); } else { throw new MetaException("Unsupported property type " . get_class($property->getType()) . "."); } if ($sevo->getStatement()) { $toXmlWriter->addBody(Strings::indent($sevo->getStatement(), 2, "\t")); } if ($xmlAttribute->namespace) { $toXmlWriter->addBody("\t\$xml->writeAttributeNS(null, " . var_export($xmlAttribute->name, true) . ", " . var_export($xmlAttribute->namespace, true) . ", {$sevo->getExpression()});"); } else { $toXmlWriter->addBody("\t\$xml->writeAttribute(" . var_export($xmlAttribute->name, true) . ", {$sevo->getExpression()});"); } $toXmlWriter->addBody("}")->addBody(""); } } $toXmlWriter->addBody("if ((\$id & {$valueGroupIdMask}) > 0) {"); $valueCount = 0; foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlValue") as $xmlValue) { /** @var XmlValue $xmlValue */ $groupId = $groups[$xmlValue->group]; $toXmlWriter->addBody("\tif ((\$id & {$groupId}) > 0 && isset(\$object->{$property->getName()})) {"); $matchingPropertySerializer = null; foreach ($this->propertySerializers as $propertySerializer) { if ($propertySerializer->matchesSerialize($property, $xmlValue->getGroup())) { $matchingPropertySerializer = $propertySerializer; break; } } if ($matchingPropertySerializer) { $sevo = $matchingPropertySerializer->serialize($property, $xmlValue->group, "\$object->{$property->getName()}"); if ($sevo->getStatement()) { $toXmlWriter->addBody(Strings::indent($sevo->getStatement(), 2, "\t")); } $toXmlWriter->addBody("\t\t\$xml->text({$sevo->getExpression()});"); } elseif ($property->getType() instanceof ScalarType) { $toXmlWriter->addBody("\t\t\$xml->text((string)\$object->{$property->getName()});"); } else { throw new MetaException("Unsupported property type " . get_class($property->getType()) . "."); } $toXmlWriter->addBody("\t}")->addBody(""); ++$valueCount; } } if (!$valueCount) { $toXmlWriter->addBody("\t// @XmlValue not specified"); } $toXmlWriter->addBody("} else {"); $elementCount = 0; $wrappers = array(); foreach ($type->getProperties() as $property) { $wrappedGroups = array(); foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElementWrapper") as $xmlElementWrapper) { $nameKey = $xmlElementWrapper->namespace . ":" . $xmlElementWrapper->name; $wrappedGroups[$xmlElementWrapper->group] = true; if (!isset($wrappers[$nameKey])) { $wrappers[$nameKey] = []; } if (!isset($wrappers[$nameKey][$xmlElementWrapper->group])) { $wrappers[$nameKey][$xmlElementWrapper->group] = [$xmlElementWrapper, []]; } // select first XmlElementWrapper per group if (!isset($wrappers[$nameKey][1][$property->getName()])) { $wrappers[$nameKey][$xmlElementWrapper->group][1][$property->getName()] = $property; ++$elementCount; } } foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElement") as $xmlElement) { if (isset($wrappedGroups[$xmlElement->group])) { continue; } ++$elementCount; $groupId = $groups[$xmlElement->group]; $elementNamespaceStr = var_export($xmlElement->namespace, true); $elementNameStr = var_export($xmlElement->name, true); $toXmlWriter->addBody("\tif ((\$id & {$groupId}) > 0 && isset(\$object->{$property->getName()}) && (\$filter === null || isset(\$filter[{$elementNameStr}]))) {"); $matchingPropertySerializer = null; foreach ($this->propertySerializers as $propertySerializer) { if ($propertySerializer->matchesSerialize($property, $xmlElement->group)) { $matchingPropertySerializer = $propertySerializer; break; } } $baseType = $property->getType(); $isArray = false; if ($baseType instanceof ArrayType) { $isArray = true; $baseType = $baseType->getBaseType(); } if ($baseType instanceof ArrayType) { throw new MetaException("toXml() cannot process multi-dimensional arrays ({$type->getName()}::\${$property->getName()})."); } $indent = "\t\t"; $value = "\$object->{$property->getName()}"; if ($isArray) { $toXmlWriter->addBody("\t\tforeach ({$value} instanceof \\Traversable ? {$value} : (array){$value} as \$item) {"); $indent = "\t\t\t"; $value = "\$item"; } if ($xmlElement->namespace) { $toXmlWriter->addBody("{$indent}\$xml->startElementNS(null, {$elementNameStr}, {$elementNamespaceStr});"); } else { $toXmlWriter->addBody("{$indent}\$xml->startElement({$elementNameStr});"); } if ($matchingPropertySerializer) { $sevo = $matchingPropertySerializer->serialize($property, $xmlElement->group, $value); if ($sevo->getStatement()) { $toXmlWriter->addBody(Strings::indent($sevo->getStatement(), 2, "\t")); } $toXmlWriter->addBody("{$indent}\$xml->text({$sevo->getExpression()});"); } elseif ($baseType instanceof ScalarType) { $toXmlWriter->addBody("{$indent}\$xml->text((string){$value});"); } elseif ($baseType instanceof Type) { $propertyTypeMetaClassName = $spec->createMetaClassName($baseType); $ns->addUse($propertyTypeMetaClassName, null, $propertyTypeMetaClassNameAlias); $toXmlWriter->addBody("{$indent}{$propertyTypeMetaClassNameAlias}::toXml({$value}, " . "\$group, " . "\$filter === null ? null : \$filter[{$elementNameStr}], " . "\$xml" . ");"); } else { throw new MetaException("Unsupported property type " . get_class($property->getType()) . "."); } $toXmlWriter->addBody("{$indent}\$xml->endElement();"); if ($isArray) { $toXmlWriter->addBody("\t\t}"); } $toXmlWriter->addBody("\t}"); } } foreach ($wrappers as $wrapper) { foreach ($wrapper as $groupWrapper) { list($xmlElementWrapper, $properties) = $groupWrapper; /** @var Property[] $properties */ $groupId = $groups[$xmlElementWrapper->group]; $namespaceStr = var_export($xmlElementWrapper->namespace, true); $nameStr = var_export($xmlElementWrapper->name, true); $propertiesIssets = []; foreach ($properties as $property) { $propertiesIssets[] = "isset(\$object->{$property->getName()})"; } $toXmlWriter->addBody("\tif ((\$id & {$groupId}) > 0 && (" . implode(" || ", $propertiesIssets) . ") && (\$filter === null || isset(\$filter[{$nameStr}]))) {"); if ($xmlElementWrapper->namespace) { $toXmlWriter->addBody("\t\t\$xml->startElementNS(null, {$nameStr}, {$namespaceStr});"); } else { $toXmlWriter->addBody("\t\t\$xml->startElement({$nameStr});"); } foreach ($properties as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElement") as $xmlElement) { if ($xmlElement->group !== $xmlElementWrapper->group) { // important! continue; } $elementNamespaceStr = var_export($xmlElement->namespace, true); $elementNameStr = var_export($xmlElement->name, true); // no need to check group ID, already checked by wrapper $toXmlWriter->addBody("\t\tif (isset(\$object->{$property->getName()}) && (\$filter === null || isset(\$filter[{$nameStr}][{$elementNameStr}]))) {"); $matchingPropertySerializer = null; foreach ($this->propertySerializers as $propertySerializer) { if ($propertySerializer->matchesSerialize($property, $xmlElement->group)) { $matchingPropertySerializer = $propertySerializer; break; } } $baseType = $property->getType(); $isArray = false; if ($baseType instanceof ArrayType) { $isArray = true; $baseType = $baseType->getBaseType(); } if ($baseType instanceof ArrayType) { throw new MetaException("toXml() cannot process multi-dimensional arrays ({$type->getName()}::\${$property->getName()})."); } $indent = "\t\t\t"; $value = "\$object->{$property->getName()}"; if ($isArray) { $toXmlWriter->addBody("\t\t\tforeach ({$value} instanceof \\Traversable ? {$value} : (array){$value} as \$item) {"); $indent = "\t\t\t\t"; $value = "\$item"; } if ($xmlElement->namespace) { $toXmlWriter->addBody("{$indent}\$xml->startElementNS(null, {$elementNameStr}, {$elementNamespaceStr});"); } else { $toXmlWriter->addBody("{$indent}\$xml->startElement({$elementNameStr});"); } if ($matchingPropertySerializer) { $sevo = $matchingPropertySerializer->serialize($property, $xmlElement->group, $value); if ($sevo->getStatement()) { $toXmlWriter->addBody(Strings::indent($sevo->getStatement(), 2, "\t")); } $toXmlWriter->addBody("{$indent}\$xml->text({$sevo->getExpression()});"); } elseif ($baseType instanceof ScalarType) { $toXmlWriter->addBody("{$indent}\$xml->text((string)\$object->{$property->getName()});"); } elseif ($baseType instanceof Type) { $propertyTypeMetaClassName = $spec->createMetaClassName($baseType); $ns->addUse($propertyTypeMetaClassName, null, $propertyTypeMetaClassNameAlias); $toXmlWriter->addBody("{$indent}{$propertyTypeMetaClassNameAlias}::toXml({$value}, " . "\$group, " . "\$filter === null ? null : \$filter[{$nameStr}][{$elementNameStr}], " . "\$xml" . ");"); } else { throw new MetaException("Unsupported property type " . get_class($property->getType()) . "."); } $toXmlWriter->addBody("{$indent}\$xml->endElement();"); if ($isArray) { $toXmlWriter->addBody("\t\t\t}"); } $toXmlWriter->addBody("\t\t}"); } } $toXmlWriter->addBody("\t\t\$xml->endElement();"); $toXmlWriter->addBody("\t}"); } } if (!$elementCount) { $toXmlWriter->addBody("\t// @XmlElement not specified"); } $toXmlWriter->addBody("}")->addBody(""); $toXmlWriter->addBody("if (count({$stackAlias}::\$objects) < 2) {")->addBody("\t\$xml->endElement();")->addBody("}"); $toXmlElement = $class->addMethod("toXmlElement"); $toXmlElement->setStatic(true)->setVisibility("private"); $toXmlElement->addParameter("object")->setTypeHint($type->getName()); $toXmlElement->addParameter("group"); $toXmlElement->addParameter("id"); $toXmlElement->addParameter("filter"); $toXmlElement->addParameter("xml")->setTypeHint("\\DOMDocument"); $toXmlElement->addParameter("el")->setTypeHint("\\DOMElement")->setOptional(true); foreach ($type->getAnnotations("Skrz\\Meta\\XML\\XmlElement") as $xmlElement) { /** @var XmlElement $xmlElement */ $groupId = $groups[$xmlElement->group]; $toXmlElement->addBody("if (!isset(\$el) && (\$id & {$groupId}) > 0) {"); if ($xmlElement->namespace) { $toXmlElement->addBody("\t\$el = \$xml->createElementNS(" . var_export($xmlElement->namespace, true) . ", " . var_export($xmlElement->name, true) . ");"); } else { $toXmlElement->addBody("\t\$el = \$xml->createElement(" . var_export($xmlElement->name, true) . ");"); } $toXmlElement->addBody("}")->addBody(""); } $toXmlElement->addBody("if (!isset(\$el)) {")->addBody("\tthrow new \\LogicException('Element has to exist by now.');")->addBody("}")->addBody(""); $attributeCount = 0; foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlAttribute") as $xmlAttribute) { ++$attributeCount; $groupId = $groups[$xmlAttribute->group]; $attributeNamespaceStr = var_export($xmlAttribute->namespace, true); $attributeNameStr = var_export($xmlAttribute->name, true); $attributeFilterStr = var_export("@" . $xmlAttribute->name, true); $toXmlElement->addBody("if ((\$id & {$groupId}) > 0 && isset(\$object->{$property->getName()}) && (\$filter === null || isset(\$filter[{$attributeFilterStr}]))) {"); $matchingPropertySerializer = null; foreach ($this->propertySerializers as $propertySerializer) { if ($propertySerializer->matchesSerialize($property, $xmlAttribute->getGroup())) { $matchingPropertySerializer = $propertySerializer; break; } } if ($matchingPropertySerializer) { $sevo = $matchingPropertySerializer->serialize($property, $xmlAttribute->group, "\$object->{$property->getName()}"); } elseif ($property->getType() instanceof ScalarType) { $sevo = StatementAndExpressionVO::withExpression("(string)\$object->{$property->getName()}"); } else { throw new MetaException("Unsupported property type " . get_class($property->getType()) . "."); } if ($sevo->getStatement()) { $toXmlElement->addBody(Strings::indent($sevo->getStatement(), 1, "\t")); } if ($xmlAttribute->namespace) { $toXmlElement->addBody("\t\$el->setAttributeNS({$attributeNamespaceStr}, {$attributeNameStr}, {$sevo->getExpression()});"); } else { $toXmlElement->addBody("\t\$el->setAttribute({$attributeNameStr}, {$sevo->getExpression()});"); } $toXmlElement->addBody("}"); } } if ($attributeCount) { $toXmlElement->addBody(""); } $toXmlElement->addBody("if ((\$id & {$valueGroupIdMask}) > 0) {"); $valueCount = 0; foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlValue") as $xmlValue) { /** @var XmlValue $xmlValue */ $groupId = $groups[$xmlValue->group]; $toXmlElement->addBody("\tif ((\$id & {$groupId}) > 0 && isset(\$object->{$property->getName()})) {"); $matchingPropertySerializer = null; foreach ($this->propertySerializers as $propertySerializer) { if ($propertySerializer->matchesSerialize($property, $xmlValue->getGroup())) { $matchingPropertySerializer = $propertySerializer; break; } } if ($matchingPropertySerializer) { $sevo = $matchingPropertySerializer->serialize($property, $xmlValue->group, "\$object->{$property->getName()}"); if ($sevo->getStatement()) { $toXmlElement->addBody(Strings::indent($sevo->getStatement(), 2, "\t")); } $toXmlElement->addBody("\t\t\$el->appendChild(new \\DOMText({$sevo->getExpression()}));"); } elseif ($property->getType() instanceof ScalarType) { $toXmlElement->addBody("\t\t\$el->appendChild(new \\DOMText((string)\$object->{$property->getName()}));"); } else { throw new MetaException("Unsupported property type " . get_class($property->getType()) . "."); } $toXmlElement->addBody("\t}"); ++$valueCount; } } if (!$valueCount) { $toXmlElement->addBody("\t// @XmlValue not specified"); } $toXmlElement->addBody("} else {"); $wrappers = array(); foreach ($type->getProperties() as $property) { $wrappedGroups = array(); foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElementWrapper") as $xmlElementWrapper) { $nameKey = $xmlElementWrapper->namespace . ":" . $xmlElementWrapper->name; $wrappedGroups[$xmlElementWrapper->group] = true; if (!isset($wrappers[$nameKey])) { $wrappers[$nameKey] = []; } if (!isset($wrappers[$nameKey][$xmlElementWrapper->group])) { $wrappers[$nameKey][$xmlElementWrapper->group] = [$xmlElementWrapper, []]; } // select first XmlElementWrapper per group if (!isset($wrappers[$nameKey][1][$property->getName()])) { $wrappers[$nameKey][$xmlElementWrapper->group][1][$property->getName()] = $property; ++$elementCount; } } foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElement") as $xmlElement) { if (isset($wrappedGroups[$xmlElement->group])) { continue; } ++$elementCount; $groupId = $groups[$xmlElement->group]; $elementNamespaceStr = var_export($xmlElement->namespace, true); $elementNameStr = var_export($xmlElement->name, true); $toXmlElement->addBody("\tif ((\$id & {$groupId}) > 0 && isset(\$object->{$property->getName()}) && (\$filter === null || isset(\$filter[{$elementNameStr}]))) {"); $matchingPropertySerializer = null; foreach ($this->propertySerializers as $propertySerializer) { if ($propertySerializer->matchesSerialize($property, $xmlElement->group)) { $matchingPropertySerializer = $propertySerializer; break; } } $baseType = $property->getType(); $isArray = false; if ($baseType instanceof ArrayType) { $isArray = true; $baseType = $baseType->getBaseType(); } if ($baseType instanceof ArrayType) { throw new MetaException("toXml() cannot process multi-dimensional arrays ({$type->getName()}::\${$property->getName()})."); } $indent = "\t\t"; $value = "\$object->{$property->getName()}"; if ($isArray) { $toXmlElement->addBody("\t\tforeach ({$value} instanceof \\Traversable ? {$value} : (array){$value} as \$item) {"); $indent = "\t\t\t"; $value = "\$item"; } if ($xmlElement->namespace) { $toXmlElement->addBody("{$indent}\$subEl = \$xml->createElementNS({$elementNamespaceStr}, {$elementNameStr});"); } else { $toXmlElement->addBody("{$indent}\$subEl = \$xml->createElement({$elementNameStr});"); } if ($matchingPropertySerializer) { $sevo = $matchingPropertySerializer->serialize($property, $xmlElement->group, $value); if ($sevo->getStatement()) { $toXmlElement->addBody(Strings::indent($sevo->getStatement(), 2, "\t")); } $toXmlElement->addBody("{$indent}\$subEl->appendChild(new \\DOMText({$sevo->getExpression()}));"); } elseif ($baseType instanceof ScalarType) { $toXmlElement->addBody("{$indent}\$subEl->appendChild(new \\DOMText((string){$value}));"); } elseif ($baseType instanceof Type) { $propertyTypeMetaClassName = $spec->createMetaClassName($baseType); $ns->addUse($propertyTypeMetaClassName, null, $propertyTypeMetaClassNameAlias); $toXmlElement->addBody("{$indent}{$propertyTypeMetaClassNameAlias}::toXml({$value}, " . "\$group, " . "\$filter === null ? null : \$filter[{$elementNameStr}], " . "\$xml, " . "\$subEl" . ");"); } else { throw new MetaException("Unsupported property type " . get_class($property->getType()) . "."); } $toXmlElement->addBody("{$indent}\$el->appendChild(\$subEl);"); if ($isArray) { $toXmlElement->addBody("\t\t}"); } $toXmlElement->addBody("\t}"); } } foreach ($wrappers as $wrapper) { foreach ($wrapper as $groupWrapper) { list($xmlElementWrapper, $properties) = $groupWrapper; /** @var Property[] $properties */ $groupId = $groups[$xmlElementWrapper->group]; $namespaceStr = var_export($xmlElementWrapper->namespace, true); $nameStr = var_export($xmlElementWrapper->name, true); $propertiesIssets = []; foreach ($properties as $property) { $propertiesIssets[] = "isset(\$object->{$property->getName()})"; } $toXmlElement->addBody("\tif ((\$id & {$groupId}) > 0 && (" . implode(" || ", $propertiesIssets) . ") && (\$filter === null || isset(\$filter[{$nameStr}]))) {"); if ($xmlElementWrapper->namespace) { $toXmlElement->addBody("\t\t\$wrapperEl = \$xml->createElementNS({$namespaceStr}, {$nameStr});"); } else { $toXmlElement->addBody("\t\t\$wrapperEl = \$xml->createElement({$nameStr});"); } foreach ($properties as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\XML\\XmlElement") as $xmlElement) { if ($xmlElement->group !== $xmlElementWrapper->group) { // important! continue; } $elementNamespaceStr = var_export($xmlElement->namespace, true); $elementNameStr = var_export($xmlElement->name, true); // no need to check group ID, already checked by wrapper $toXmlElement->addBody("\t\tif (isset(\$object->{$property->getName()}) && (\$filter === null || isset(\$filter[{$nameStr}][{$elementNameStr}]))) {"); $matchingPropertySerializer = null; foreach ($this->propertySerializers as $propertySerializer) { if ($propertySerializer->matchesSerialize($property, $xmlElement->group)) { $matchingPropertySerializer = $propertySerializer; break; } } $baseType = $property->getType(); $isArray = false; if ($baseType instanceof ArrayType) { $isArray = true; $baseType = $baseType->getBaseType(); } if ($baseType instanceof ArrayType) { throw new MetaException("toXml() cannot process multi-dimensional arrays ({$type->getName()}::\${$property->getName()})."); } $indent = "\t\t\t"; $value = "\$object->{$property->getName()}"; if ($isArray) { $toXmlElement->addBody("\t\t\tforeach ({$value} instanceof \\Traversable ? {$value} : (array){$value} as \$item) {"); $indent = "\t\t\t\t"; $value = "\$item"; } if ($xmlElement->namespace) { $toXmlElement->addBody("{$indent}\$subEl = \$xml->createElementNS({$elementNamespaceStr}, {$elementNameStr});"); } else { $toXmlElement->addBody("{$indent}\$subEl = \$xml->createElement({$elementNameStr});"); } if ($matchingPropertySerializer) { $sevo = $matchingPropertySerializer->serialize($property, $xmlElement->group, $value); if ($sevo->getStatement()) { $toXmlElement->addBody(Strings::indent($sevo->getStatement(), 2, "\t")); } $toXmlElement->addBody("{$indent}\$subEl->appendChild(new \\DOMText({$sevo->getExpression()}));"); } elseif ($baseType instanceof ScalarType) { $toXmlElement->addBody("{$indent}\$subEl->appendChild(new \\DOMText((string)\$object->{$property->getName()}));"); } elseif ($baseType instanceof Type) { $propertyTypeMetaClassName = $spec->createMetaClassName($baseType); $ns->addUse($propertyTypeMetaClassName, null, $propertyTypeMetaClassNameAlias); $toXmlElement->addBody("{$indent}{$propertyTypeMetaClassNameAlias}::toXml({$value}, " . "\$group, " . "\$filter === null ? null : \$filter[{$nameStr}][{$elementNameStr}], " . "\$xml, " . "\$subEl" . ");"); } else { throw new MetaException("Unsupported property type " . get_class($property->getType()) . "."); } $toXmlElement->addBody("{$indent}\$wrapperEl->appendChild(\$subEl);"); if ($isArray) { $toXmlElement->addBody("\t\t\t}"); } $toXmlElement->addBody("\t\t}"); } } $toXmlElement->addBody("\t\t\$el->appendChild(\$wrapperEl);"); $toXmlElement->addBody("\t}"); } } if (!$elementCount) { $toXmlElement->addBody("\t// @XmlElement not specified"); } $toXmlElement->addBody("}")->addBody(""); $toXmlElement->addBody("return \$el;"); }
/** * Indents the HTML content from the left. * @param string UTF-8 encoding or 8-bit * @param int * @param string * @return string */ public static function indent($s, $level = 1, $chars = "\t") { if ($level >= 1) { $s = Strings::replace($s, '#<(textarea|pre).*?</\\1#si', /*5.2* callback(*/function($m) { return strtr($m[0], " \t\r\n", "\x1F\x1E\x1D\x1A"); }/*5.2* )*/); $s = Strings::indent($s, $level, $chars); $s = strtr($s, "\x1F\x1E\x1D\x1A", " \t\r\n"); } return $s; }
public function onGenerate(AbstractMetaSpec $spec, MetaSpecMatcher $matcher, Type $type, ClassType $class) { $groups = array(); $inputOutputClasses = array($type->getName() => true); $i = 0; foreach ($this->defaultGroups as $defaultGroup) { $groups[$defaultGroup] = 1 << $i++; } $ns = $class->getNamespace(); $ns->addUse("Skrz\\Meta\\PHP\\PhpMetaInterface"); $ns->addUse($type->getName(), null, $typeAlias); $ns->addUse("Skrz\\Meta\\Stack", null, $stackAlias); $class->addImplement("Skrz\\Meta\\PHP\\PhpMetaInterface"); // get groups foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\PHP\\PhpArrayOffset") as $arrayOffset) { /** @var PhpArrayOffset $arrayOffset */ if (!isset($groups[$arrayOffset->group])) { $groups[$arrayOffset->group] = 1 << $i++; } } } // get discriminator $discriminatorOffsetMap = array(); $discriminatorClassMap = array(); $discriminatorMetaMap = array(); foreach ($type->getAnnotations("Skrz\\Meta\\PHP\\PhpDiscriminatorOffset") as $discriminatorOffset) { /** @var PhpDiscriminatorOffset $discriminatorOffset */ if (!isset($groups[$discriminatorOffset->group])) { $groups[$discriminatorOffset->group] = 1 << $i++; } $discriminatorOffsetMap[$groups[$discriminatorOffset->group]] = $discriminatorOffset->offset; } foreach ($type->getAnnotations("Skrz\\Meta\\PHP\\PhpDiscriminatorMap") as $discriminatorMap) { /** @var PhpDiscriminatorMap $discriminatorMap */ if (!isset($groups[$discriminatorMap->group])) { $groups[$discriminatorMap->group] = 1 << $i++; } if (isset($discriminatorMetaMap[$groups[$discriminatorMap->group]])) { throw new MetaException("More @PhpDiscriminatorMap annotations with same group '{$discriminatorMap->group}'."); } $discriminatorClassMap[$groups[$discriminatorMap->group]] = array(); $discriminatorMetaMap[$groups[$discriminatorMap->group]] = array(); $currentClassMap =& $discriminatorClassMap[$groups[$discriminatorMap->group]]; $currentMetaMap =& $discriminatorMetaMap[$groups[$discriminatorMap->group]]; foreach ($discriminatorMap->map as $value => $className) { $currentClassMap[$value] = $className; $inputOutputClasses[$className] = true; $currentMetaMap[$value] = $spec->createMetaClassName(Type::fromString($className)); } } // add groups property $groupsProperty = $class->addProperty("groups"); $groupsProperty->setStatic(true)->setValue($groups)->setVisibility("private"); $groupsProperty->addComment("Mapping from group name to group ID for fromArray() and toArray()")->addComment("")->addComment("@var string[]"); // create input/output type hint $inputOutputTypeHint = array(); $inputOutputClasses = array_keys($inputOutputClasses); sort($inputOutputClasses); foreach ($inputOutputClasses as $inputOutputClass) { $ns->addUse($inputOutputClass, null, $alias); $inputOutputTypeHint[] = $alias; } $inputOutputTypeHint = implode("|", $inputOutputTypeHint); foreach (array("Array", "Object") as $what) { // from*() method $from = $class->addMethod("from{$what}"); $from->setStatic(true); $from->addParameter("input"); $from->addParameter("group")->setOptional(true); $from->addParameter("object")->setOptional(true); $from->addComment("Creates \\{$type->getName()} object from " . strtolower($what))->addComment("")->addComment("@param " . strtolower($what) . " \$input")->addComment("@param string \$group")->addComment("@param {$inputOutputTypeHint} \$object")->addComment("")->addComment("@throws \\Exception")->addComment("")->addComment("@return {$inputOutputTypeHint}"); if ($what === "Object") { $from->addBody("\$input = (array)\$input;\n"); } // TODO: more groups - include/exclude $from->addBody("if (!isset(self::\$groups[\$group])) {")->addBody("\tthrow new \\InvalidArgumentException('Group \\'' . \$group . '\\' not supported for ' . " . var_export($type->getName(), true) . " . '.');")->addBody("} else {")->addBody("\t\$id = self::\$groups[\$group];")->addBody("}")->addBody(""); if (!empty($discriminatorMetaMap)) { foreach ($discriminatorMetaMap as $groupId => $groupDiscriminatorMetaMap) { if (isset($discriminatorOffsetMap[$groupId])) { $groupDiscriminatorOffset = $discriminatorOffsetMap[$groupId]; foreach ($groupDiscriminatorMetaMap as $value => $metaClass) { $ns->addUse($metaClass, null, $alias); $from->addBody("if ((\$id & {$groupId}) > 0 && " . "isset(\$input[" . var_export($groupDiscriminatorOffset, true) . "]) && " . "\$input[" . var_export($groupDiscriminatorOffset, true) . "] === " . var_export($value, true) . ") {")->addBody("\treturn {$alias}::from{$what}(\$input, \$group, \$object);")->addBody("}")->addBody(""); } } else { foreach ($groupDiscriminatorMetaMap as $value => $metaClass) { $ns->addUse($metaClass, null, $alias); $from->addBody("if ((\$id & {$groupId}) > 0 && " . "isset(\$input[" . var_export($value, true) . "])) {")->addBody("\treturn {$alias}::from{$what}(\$input[" . var_export($value, true) . "], \$group, \$object);")->addBody("}")->addBody(""); } } } } $from->addBody("if (\$object === null) {")->addBody("\t\$object = new {$typeAlias}();")->addBody("} elseif (!(\$object instanceof {$typeAlias})) {")->addBody("\tthrow new \\InvalidArgumentException('You have to pass object of class {$type->getName()}.');")->addBody("}")->addBody(""); foreach ($type->getProperties() as $property) { foreach ($property->getAnnotations("Skrz\\Meta\\PHP\\PhpArrayOffset") as $arrayOffset) { /** @var PhpArrayOffset $arrayOffset */ $groupId = $groups[$arrayOffset->group]; $arrayKey = var_export($arrayOffset->offset, true); $baseArrayPath = $arrayPath = "\$input[{$arrayKey}]"; $baseObjectPath = $objectPath = "\$object->{$property->getName()}"; $from->addBody("if ((\$id & {$groupId}) > 0 && isset({$arrayPath})) {"); // FIXME: group group IDs by offset $baseType = $property->getType(); $indent = "\t"; $before = ""; $after = ""; for ($i = 0; $baseType instanceof ArrayType; ++$i) { $arrayType = $baseType; $baseType = $arrayType->getBaseType(); $before .= "{$indent}if (!(isset({$objectPath}) && is_array({$objectPath}))) {\n"; $before .= "{$indent}\t{$objectPath} = array();\n"; $before .= "{$indent}}\n"; $before .= "{$indent}foreach ({$arrayPath} instanceof \\Traversable ? {$arrayPath} : (array){$arrayPath} as \$k{$i} => \$v{$i}) {\n"; $after = "{$indent}}\n" . $after; $indent .= "\t"; $arrayPath = "\$v{$i}"; $objectPath .= "[\$k{$i}]"; } if (!empty($before)) { $from->addBody(rtrim($before)); } $matchingPropertySerializer = $this->getMatchingPropertySerializer($property, $arrayOffset); if ($matchingPropertySerializer !== null) { $sevo = $matchingPropertySerializer->deserialize($property, $arrayOffset->group, $arrayPath); if ($sevo->getStatement()) { $from->addBody(Strings::indent($sevo->getStatement(), 1, $indent)); } $from->addBody("{$indent}{$objectPath} = {$sevo->getExpression()};"); } elseif ($baseType instanceof ScalarType) { $from->addBody("{$indent}{$objectPath} = {$arrayPath};"); } elseif ($baseType instanceof Type) { $propertyTypeMetaClassName = $spec->createMetaClassName($baseType); $ns->addUse($propertyTypeMetaClassName, null, $propertyTypeMetaClassNameAlias); $from->addBody("{$indent}{$objectPath} = {$propertyTypeMetaClassNameAlias}::from{$what}(" . "{$arrayPath}, " . "\$group, " . "isset({$objectPath}) ? {$objectPath} : null" . ");"); } else { throw new MetaException("Unsupported property type " . get_class($baseType) . " ({$type->getName()}::\${$property->getName()})."); } if (!empty($after)) { $from->addBody(rtrim($after)); } $from->addBody("} elseif ((\$id & {$groupId}) > 0 && array_key_exists({$arrayKey}, \$input) && {$baseArrayPath} === null) {")->addBody("\t{$baseObjectPath} = null;")->addBody("}"); } $from->addBody(""); } $from->addBody("return \$object;"); // to*() method $to = $class->addMethod("to{$what}"); $to->setStatic(true); $to->addParameter("object"); $to->addParameter("group")->setOptional(true); $to->addParameter("filter")->setOptional(true); $to->addComment("Serializes \\{$type->getName()} to " . strtolower($what))->addComment("")->addComment("@param {$inputOutputTypeHint} \$object")->addComment("@param string \$group")->addComment("@param array \$filter")->addComment("")->addComment("@throws \\Exception")->addComment("")->addComment("@return " . strtolower($what)); $to->addBody("if (\$object === null) {")->addBody("\treturn null;")->addBody("}"); // TODO: more groups - include/exclude $to->addBody("if (!isset(self::\$groups[\$group])) {")->addBody("\tthrow new \\InvalidArgumentException('Group \\'' . \$group . '\\' not supported for ' . " . var_export($type->getName(), true) . " . '.');")->addBody("} else {")->addBody("\t\$id = self::\$groups[\$group];")->addBody("}")->addBody(""); if (!empty($discriminatorClassMap)) { foreach ($discriminatorClassMap as $groupId => $groupDiscriminatorClassMap) { $groupDiscriminatorOffset = null; if (isset($discriminatorOffsetMap[$groupId])) { $groupDiscriminatorOffset = $discriminatorOffsetMap[$groupId]; } foreach ($groupDiscriminatorClassMap as $value => $className) { $metaClassName = $discriminatorMetaMap[$groupId][$value]; $ns->addUse($className, null, $alias); $ns->addUse($metaClassName, null, $metaAlias); $to->addBody("if ((\$id & {$groupId}) > 0 && \$object instanceof {$alias}) {")->addBody("\t\$output = {$metaAlias}::to{$what}(\$object, \$group);"); if ($groupDiscriminatorOffset === null) { $to->addBody("\t\$output = " . ($what === "Object" ? "(object)" : "") . "array(" . var_export($value, true) . " => " . ($what === "Object" ? "(object)" : "") . "\$output);"); } else { if ($what === "Object") { $to->addBody("\t\$output->{$groupDiscriminatorOffset} = " . var_export($value, true) . ";"); // FIXME: might compile to incorrect PHP code } else { $to->addBody("\t\$output[" . var_export($groupDiscriminatorOffset, true) . "] = " . var_export($value, true) . ";"); } } $to->addBody("\treturn \$output;")->addBody("}")->addBody(""); } } } $to->addBody("if (!(\$object instanceof {$typeAlias})) {")->addBody("\tthrow new \\InvalidArgumentException('You have to pass object of class {$type->getName()}.');")->addBody("}")->addBody(""); $to->addBody("if ({$stackAlias}::\$objects === null) {")->addBody("\t{$stackAlias}::\$objects = new \\SplObjectStorage();")->addBody("}")->addBody("")->addBody("if ({$stackAlias}::\$objects->contains(\$object)) {")->addBody("\treturn null;")->addBody("}")->addBody("")->addBody("{$stackAlias}::\$objects->attach(\$object);")->addBody(""); $to->addBody("try {")->addBody("\t\$output = array();")->addBody(""); foreach ($type->getProperties() as $property) { $propertyGroups = []; foreach ($property->getAnnotations("Skrz\\Meta\\PHP\\PhpArrayOffset") as $arrayOffset) { if (isset($propertyGroups[$arrayOffset->group])) { continue; } $propertyGroups[$arrayOffset->group] = true; /** @var PhpArrayOffset $arrayOffset */ $groupId = $groups[$arrayOffset->group]; $if = "\tif ((\$id & {$groupId}) > 0"; if ($arrayOffset->ignoreNull) { $if .= " && ((isset(\$object->{$property->getName()}) && \$filter === null)"; } else { $if .= " && (\$filter === null"; } $if .= " || isset(\$filter[" . var_export($arrayOffset->offset, true) . "]))) {"; // FIXME: group group IDs by offset $to->addBody($if); $objectPath = "\$object->{$property->getName()}"; $arrayPath = "\$output[" . var_export($arrayOffset->offset, true) . "]"; $baseType = $property->getType(); $indent = "\t\t"; $before = ""; $after = ""; for ($i = 0; $baseType instanceof ArrayType; ++$i) { $arrayType = $baseType; $baseType = $arrayType->getBaseType(); $before .= "{$indent}if (!(isset({$arrayPath}) && is_array({$arrayPath}))) {\n"; $before .= "{$indent}\t{$arrayPath} = array();\n"; $before .= "{$indent}}\n"; $before .= "{$indent}foreach ({$objectPath} instanceof \\Traversable ? {$objectPath} : (array){$objectPath} as \$k{$i} => \$v{$i}) {\n"; $after = "{$indent}}\n" . $after; $indent .= "\t"; $arrayPath .= "[\$k{$i}]"; $objectPath = "\$v{$i}"; } if (!empty($before)) { $to->addBody(rtrim($before)); } $matchingPropertySerializer = $this->getMatchingPropertySerializer($property, $arrayOffset); if ($matchingPropertySerializer !== null) { $sevo = $matchingPropertySerializer->serialize($property, $arrayOffset->group, $objectPath); if ($sevo->getStatement()) { $to->addBody(Strings::indent($sevo->getStatement(), 1, $indent)); } $to->addBody("{$indent}{$arrayPath} = {$sevo->getExpression()};"); } elseif ($baseType instanceof ScalarType) { $to->addBody("{$indent}{$arrayPath} = {$objectPath};"); } elseif ($baseType instanceof Type) { $propertyTypeMetaClassName = $spec->createMetaClassName($baseType); $ns->addUse($propertyTypeMetaClassName, null, $propertyTypeMetaClassNameAlias); $to->addBody("{$indent}{$arrayPath} = {$propertyTypeMetaClassNameAlias}::to{$what}(" . "{$objectPath}, " . "\$group, " . "\$filter === null ? null : \$filter[" . var_export($arrayOffset->offset, true) . "]" . ");"); } else { throw new MetaException("Unsupported property type " . get_class($baseType) . "."); } if (!empty($after)) { $to->addBody(rtrim($after)); } $to->addBody("\t}"); } $to->addBody(""); } $to->addBody("} catch (\\Exception \$e) {")->addBody("\t{$stackAlias}::\$objects->detach(\$object);")->addBody("\tthrow \$e;")->addBody("}")->addBody(""); $to->addBody("{$stackAlias}::\$objects->detach(\$object);")->addBody("return " . ($what === "Object" ? "(object)" : "") . "\$output;"); } }