From: Andy Street Date: Tue, 18 Dec 2018 15:46:01 +0000 (+0000) Subject: Add support for GPX wpt type X-Git-Url: https://git.street.me.uk/andy/gpx.git/commitdiff_plain/f528d2481e20c97020c2504ad9c2d5e2bc5ec653 Add support for GPX wpt type --- diff --git a/docs/samples/full-1.0.gpx b/docs/samples/full-1.0.gpx index 4debbf2..e2424da 100644 --- a/docs/samples/full-1.0.gpx +++ b/docs/samples/full-1.0.gpx @@ -1,5 +1,5 @@ - + full-1.0 This is a sample file containing all parts of the GPX 1.0 specification supported by libgpx. John Doe @@ -9,4 +9,26 @@ Example, GPX + + 10.3 + + 12.3 + 10 + Horse Sands Fort + Fort + A fort in the Solent + Guess + https://www.example.com/fort + More Information + Flag, Blue + Nautical + 3d + 6 + 1 + 2 + 3 + 86400 + 13 + Jane Doe + diff --git a/docs/samples/full-1.1.gpx b/docs/samples/full-1.1.gpx index 3d8c9f2..40a99c8 100644 --- a/docs/samples/full-1.1.gpx +++ b/docs/samples/full-1.1.gpx @@ -26,4 +26,30 @@ Jane Doe + + 10.3 + + 12.3 + 10 + Horse Sands Fort + Fort + A fort in the Solent + Guess + + More Information + text/html + + Flag, Blue + Nautical + 3d + 6 + 1 + 2 + 3 + 86400 + 13 + + Jane Doe + + diff --git a/src/libgpx/gpx.php b/src/libgpx/gpx.php index 4404d94..4a6e1ff 100644 --- a/src/libgpx/gpx.php +++ b/src/libgpx/gpx.php @@ -99,6 +99,13 @@ class GPX implements Geographic */ protected $metadataExtensions; + /** + * A list of waypoints. + * + * @var TypedDoublyLinkedList (Point) + */ + protected $waypoints; + /** * Create a new instance. */ @@ -107,6 +114,7 @@ class GPX implements Geographic $this->links = new TypedDoublyLinkedList('libgpx\Link'); $this->keywords = new TypedDoublyLinkedList('string'); $this->metadataExtensions = new TypedDoublyLinkedList('string'); + $this->waypoints = new TypedDoublyLinkedList('libgpx\Point'); } @@ -265,6 +273,16 @@ class GPX implements Geographic return $this->metadataExtensions; } + /** + * Fetch a list of waypoints. + * + * @return TypedDoublyLinkedList A list of waypoints. + */ + public function getWaypoints() + { + return $this->waypoints; + } + /** * Fetch a bounding box that covers the feature. * @@ -273,9 +291,12 @@ class GPX implements Geographic */ public function getBounds() { - //TODO: Implement this properly when waypoints, routes and tracks have - // been implemented. - return null; + $result = null; + foreach ($this->waypoints as $waypoint) { + $bounds = $waypoint->getBounds(); + $result = ($result === null ? $bounds : $result->extend($bounds)); + } + return $result; } } diff --git a/src/libgpx/gpxreader.php b/src/libgpx/gpxreader.php index 0168f24..f243513 100644 --- a/src/libgpx/gpxreader.php +++ b/src/libgpx/gpxreader.php @@ -26,6 +26,7 @@ namespace libgpx; use \DateTime; use \DateTimeZone; +use \DomainException; use \Exception; use \InvalidArgumentException; use \RuntimeException; @@ -176,7 +177,11 @@ class GPXReader $result->setCreator($this->xml->getAttribute('creator')); $struct = [ - 'elements' => [] + 'elements' => [ + 'wpt' => function ($gpx) { + $gpx->getWaypoints()[] = $this->readWpt(); + } + ] ]; if ($this->version == '1.1') { $struct['elements']['metadata'] = function ($gpx) { @@ -208,7 +213,7 @@ class GPXReader $struct['elements']['url'] = function ($gpx, &$state) { $href = $this->readXSDString(); $links = $gpx->getLinks(); - if ($links->isEmpty) { + if ($links->isEmpty()) { $link = new Link($href); if (isset($state['urlname'])) { $link->setText($state['urlname']); @@ -222,7 +227,7 @@ class GPXReader $struct['elements']['urlname'] = function ($gpx, &$state) { $text = $this->readXSDString(); $links = $gpx->getLinks(); - if ($links->isEmpty) { + if ($links->isEmpty()) { $state['urlname'] = $text; } else { $links->bottom()->setText($text); @@ -412,6 +417,121 @@ class GPXReader $this->readStruct($struct, $list); } + /** + * Read a wpt type. + * + * @see https://www.topografix.com/GPX/1/1/#type_wptType + * + * @return Point + * @throws MalformedGPXException If the GPX file was invalid or not supported. + */ + protected function readWpt() + { + try { + $result = new Point( + $this->string2float($this->xml->getAttribute('lat')), + $this->string2float($this->xml->getAttribute('lon')) + ); + $struct = [ + 'elements' => [ + 'ele' => function ($point) { + $point->setEle($this->string2float($this->readXSDString())); + }, + 'time' => function ($point) { + $point->setTime($this->string2DateTime($this->readXSDString())); + }, + 'magvar' => function ($point) { + $point->setMagvar($this->string2float($this->readXSDString())); + }, + 'geoidheight' => function ($point) { + $point->setGeoidHeight($this->string2float($this->readXSDString())); + }, + 'name' => function ($point) { + $point->setName($this->readXSDString()); + }, + 'cmt' => function ($point) { + $point->setComment($this->readXSDString()); + }, + 'desc' => function ($point) { + $point->setDescription($this->readXSDString()); + }, + 'src' => function ($point) { + $point->setSource($this->readXSDString()); + }, + 'sym' => function ($point) { + $point->setSymbol($this->readXSDString()); + }, + 'type' => function ($point) { + $point->setType($this->readXSDString()); + }, + 'fix' => function ($point) { + $point->setFix($this->readXSDString()); + }, + 'sat' => function ($point) { + $point->setSatellites($this->string2int($this->readXSDString())); + }, + 'hdop' => function ($point) { + $point->setHdop($this->string2float($this->readXSDString())); + }, + 'vdop' => function ($point) { + $point->setVdop($this->string2float($this->readXSDString())); + }, + 'pdop' => function ($point) { + $point->setPdop($this->string2float($this->readXSDString())); + }, + 'ageofdgpsdata' => function ($point) { + $point->setAgeOfDGPSData($this->string2float($this->readXSDString())); + }, + 'dgpsid' => function ($point) { + $point->setDGPSId($this->string2int($this->readXSDString())); + } + ] + ]; + if ($this->version == '1.1') { + $struct['elements']['link'] = function ($point) { + $point->getLinks()[] = $this->readLink(); + }; + $struct['elements']['extensions'] = function ($point) { + $this->readExtension($point->getExtensions()); + }; + } else { + $struct['elements']['url'] = function ($point, &$state) { + $href = $this->readXSDString(); + $links = $point->getLinks(); + if ($links->isEmpty()) { + $link = new Link($href); + if (isset($state['urlname'])) { + $link->setText($state['urlname']); + unset($state['urlname']); + } + $links[] = $link; + } else { + $links->bottom()->setHref($href); + } + }; + $struct['elements']['urlname'] = function ($point, &$state) { + $text = $this->readXSDString(); + $links = $point->getLinks(); + if ($links->isEmpty()) { + $state['urlname'] = $text; + } else { + $links->bottom()->setText($text); + } + }; + $struct['extensions'] = function ($point) { + $point->getExtensions()[] = $this->xml->readOuterXML(); + $this->xml->next(); + }; + } + $this->readStruct($struct, $result); + } catch (DomainException $e) { + throw new MalformedGPXException( + $e->getMessage(), $e->getCode(), $e + ); + } + return $result; + } + /** * Read a data structure from XML. * @@ -548,4 +668,36 @@ class GPXReader return $result; } + /** + * Convert a string to a float. + * + * @param string $value The string value to convert. + * @return float + * @throws MalformedGPXException If the value is not numeric. + */ + protected function string2float(string $value) + { + if (!is_numeric($value)) + throw new MalformedGPXException( + sprintf('Expected decimal value but got "%s"', $value) + ); + return floatval($value); + } + + /** + * Convert a string to a int. + * + * @param string $value The string value to convert. + * @return int + * @throws MalformedGPXException If the value is not numeric. + */ + protected function string2int(string $value) + { + if (!is_numeric($value)) + throw new MalformedGPXException( + sprintf('Expected decimal value but got "%s"', $value) + ); + return intval($value); + } + } diff --git a/src/libgpx/gpxwriter.php b/src/libgpx/gpxwriter.php index b4fd51f..7622fdb 100644 --- a/src/libgpx/gpxwriter.php +++ b/src/libgpx/gpxwriter.php @@ -233,6 +233,9 @@ class GPXWriter $namespace . ' ' . $schema ); $this->writeMetadata($gpx, $xml); + foreach ($gpx->getWaypoints() as $wpt) { + $this->writePoint($wpt, 'wpt', $xml); + } $xml->endElement(); $xml->endDocument(); $xml->flush(); @@ -398,4 +401,89 @@ class GPXWriter return $timestamp->format($format); } + /** + * Generate XML for GPX wpt type. + * + * @param Point $point The point to write. + * @param string $element The XML element name (e.g. ) + * @param XMLWriter $xml Where to write the point. + * @return void + */ + protected function writePoint(Point $point, string $element, XMLWriter $xml) + { + $xml->startElement($element); + $xml->writeAttribute('lat', $point->getLatitude()); + $xml->writeAttribute('lon', $point->getLongitude()); + $val = $point->getEle(); + if ($val !== null) + $xml->writeElement('ele', $val); + $val = $point->getTime(); + if ($val !== null) + $xml->writeElement('time', $this->createTimestamp($val)); + $val = $point->getMagvar(); + if ($val !== null) + $xml->writeElement('magvar', $val); + $val = $point->getGeoidHeight(); + if ($val !== null) + $xml->writeElement('geoidheight', $val); + $var = $point->getName(); + if ($var !== null) + $xml->writeElement('name', $var); + $var = $point->getComment(); + if ($var !== null) + $xml->writeElement('cmt', $var); + $var = $point->getDescription(); + if ($var !== null) + $xml->writeElement('desc', $var); + $var = $point->getSource(); + if ($var !== null) + $xml->writeElement('src', $var); + $var = $point->getLinks(false); + if ($var !== null) { + foreach($var as $link) { + $this->writeLink($link, $xml); + if ($this->format == '1.0') + break; + } + } + $var = $point->getSymbol(); + if ($var !== null) + $xml->writeElement('sym', $var); + $var = $point->getType(); + if ($var !== null) + $xml->writeElement('type', $var); + $var = $point->getFix(); + if ($var !== null) + $xml->writeElement('fix', $var); + $var = $point->getSatellites(); + if ($var !== null) + $xml->writeElement('sat', $var); + $var = $point->getHdop(); + if ($var !== null) + $xml->writeElement('hdop', $var); + $var = $point->getVdop(); + if ($var !== null) + $xml->writeElement('vdop', $var); + $var = $point->getPdop(); + if ($var !== null) + $xml->writeElement('pdop', $var); + $var = $point->getAgeOfDGPSData(); + if ($var !== null) + $xml->writeElement('ageofdgpsdata', $var); + $var = $point->getDGPSId(); + if ($var !== null) + $xml->writeElement('dgpsid', $var); + $var = $point->getExtensions(false); + if ($var !== null && !$var->isEmpty()) { + if ($this->format == '1.1') + $xml->startElement('extensions'); + foreach ($var as $extension) { + $xml->writeRaw($extension); + } + if ($this->format == '1.1') + $xml->endElement(); + } + $xml->endElement(); + } + } diff --git a/src/libgpx/point.php b/src/libgpx/point.php new file mode 100644 index 0000000..7568821 --- /dev/null +++ b/src/libgpx/point.php @@ -0,0 +1,688 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * + */ + +namespace libgpx; + +use \DomainException; +use \DateTimeInterface; + +/** + * A geographic point. + * + * @see https://www.topografix.com/GPX/1/1/#type_wptType + * + * @author Andy Street + */ +class Point implements Geographic +{ + + /** + * The latitude of the point. + * + * @var float + */ + protected $lat; + + /** + * The longitude of the point. + * + * @var float + */ + protected $lon; + + /** + * Elevation (in meters) of the point. + * + * @var float + */ + protected $ele; + + /** + * Creation/modification timestamp for element. + * + * Date and time in are in Univeral Coordinated Time (UTC), not local time! + * Conforms to ISO 8601 specification for date/time representation. + * Fractional seconds are allowed for millisecond timing in tracklogs. + * + * @var DateTimeInterface + */ + protected $time; + + /** + * Magnetic variation (in degrees) at the point. + * + * @var float + */ + protected $magvar; + + /** + * Height (in meters) of geoid (mean sea level) above WGS84 earth ellipsoid. + * As defined in NMEA GGA message. + * + * @var float + */ + protected $geoidheight; + + /** + * The name of the point. + * + * @var string + */ + protected $name; + + /** + * GPS waypoint comment. + * + * Sent to GPS as comment. + * + * @var string + */ + protected $comment; + + /** + * A text description of the element. + * + * Holds additional information about the element intended for the user, + * not the GPS. + * + * @var string + */ + protected $description; + + /** + * Source of data. + * + * Included to give user some idea of reliability and accuracy of data. + * E.g. "Garmin eTrex", "USGS quad Boston North". + * + * @var string + */ + protected $source; + + /** + * A list of links associated with this point. + * + * @var TypedDoublyLinkedList (Link) + */ + protected $links; + + /** + * Text of GPS symbol name. + * + * For interchange with other programs, use the exact spelling of the symbol + * as displayed on the GPS. If the GPS abbreviates words, spell them out. + * + * @var string + */ + protected $symbol; + + /** + * Type (classification) of the point. + * + * @var string + */ + protected $type; + + /** + * Type of GPS fix. + * + * "none", "2d", "3d", "dgps" or "pps". + * + * None means GPS had no fix. To signify "the fix info is unknown, leave out + * fixType entirely. pps = military signal used. + * + * @var string + */ + protected $fix; + + /** + * Number of satellites used to calculate the GPX fix. + * + * @var int + */ + protected $satellites; + + /** + * Horizontal dilution of precision. + * + * @var float + */ + protected $hdop; + + /** + * Vertical dilution of precision. + * + * @var float + */ + protected $vdop; + + /** + * Position dilution of precision. + * + * @var float + */ + protected $pdop; + + /** + * Number of seconds since last DGPS update. + * + * @var float + */ + protected $ageofdgpsdata; + + /** + * ID of DGPS station used in differential correction. + * + * @var int + */ + protected $dgpsid; + + /** + * A list of XML snippets describing unsupported data. + * + * @var TypedDoublyLinkedList (string) + */ + protected $extensions; + + /** + * Create a new point. + * + * @param float $lat Latitude + * @param float $lon Longitude + * @throws DomainException If not a valid latitude or longitude. + */ + public function __construct(float $lat, float $lon) + { + $this->setLatitude($lat); + $this->setLongitude($lon); + } + + /** + * Fetch the latitude of the point. + * + * @return float + */ + public function getLatitude() + { + return $this->lat; + } + + /** + * Set the latitude of the point. + * + * @param float $lat Latitude + * @return void + * @throws DomainException If not a valid latitude. + */ + public function setLatitude(float $lat) + { + if ($lat < -90 || $lat > 90) + throw new DomainException( + sprintf('Value %s is not in the range -90-+90.', $lat) + ); + $this->lat = $lat; + } + + /** + * Fetch the longitude of the point. + * + * @return float + */ + public function getLongitude() + { + return $this->lon; + } + + /** + * Set the longitude of the point. + * + * @param float $lon Longitude + * @return void + * @throws DomainException If not a valid longitude. + */ + public function setLongitude(float $lon) + { + if ($lon < -180 || $lon > 180) + throw new DomainException( + sprintf('Value %s is not in the range -180-+180.', $lon) + ); + $this->lon = $lon; + } + + /** + * Fetch the elevation of the point. + * + * @return float|null The elevation in meters or null if not set. + */ + public function getEle() + { + return $this->ele; + } + + /** + * Set the elevation of the point. + * + * @param float $ele The elevation in meters or null to delete. + * @return void + */ + public function setEle(float $ele = null) + { + $this->ele = $ele; + } + + /** + * Fetch the creation time of the point. + * + * @return DateTimeInterface|null The time or null if not set. + */ + public function getTime() + { + return $this->time; + } + + /** + * Set the creation time of the point. + * + * @param DateTimeInterface $time The creation time or null to delete. + * @return void + */ + public function setTime(DateTimeInterface $time = null) + { + $this->time = $time; + } + + /** + * Fetch the magnetic variation. + * + * @return float|null The magnetic variation in degrees or null if not set. + */ + public function getMagvar() + { + return $this->magvar; + } + + /** + * Set the magnetic variation. + * + * @param float $magvar The magnetic variation in degrees or null to delete. + * @return void + * @throws DomainException if not in range 0 <= value < 360 + */ + public function setMagvar(float $magvar = null) + { + if ( + $magvar !== null + && ($magvar < 0 || $magvar >= 360) + ) { + throw new DomainException( + sprintf('Value %s is not in the range 0 <= value < 360.', $lon) + ); + } + $this->magvar = $magvar; + } + + /** + * Fetch the geoid height at the point. + * + * @return float The geoid height in meters. + */ + public function getGeoidHeight() + { + return $this->geoidheight; + } + + /** + * Set the geoid height at the point. + * + * @param float $geoidheight The geoid height in meters. + * @return void + */ + public function setGeoidHeight(float $geoidheight = null) + { + $this->geoidheight = $geoidheight; + } + + /** + * Fetch the name of the point. + * + * @return string|null The name or null if not set. + */ + public function getName() + { + return $this->name; + } + + /** + * Set the name of the point. + * + * @param string $name The name or null to delete. + * @return void + */ + public function setName(string $name = null) + { + $this->name = $name; + } + + /** + * Fetch a comment for the point. + * + * @return string The comment or null if not set. + */ + public function getComment() + { + return $this->comment; + } + + /** + * Set a comment for the point. + * + * @param string $comment The comment or null to delete. + * @return void + */ + public function setComment(string $comment = null) + { + $this->comment = $comment; + } + + /** + * Fetch the description for the point. + * + * @return string The description or null if not set. + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set the description of the point. + * + * @param string $description The description or null to delete. + * @return void + */ + public function setDescription(string $description = null) + { + $this->description = $description; + } + + /** + * Fetch the source of the data. + * + * @return string|null The source or null if not set. + */ + public function getSource() + { + return $this->source; + } + + /** + * Set the source of the data. + * + * @param string $source The source of the data or null to delete. + * @return void + */ + public function setSource(string $source = null) + { + $this->source = $source; + } + + /** + * Fetch a list of links associated with this point. + * + * @param boolean $create Create the list if it does not already exist. + * @return TypedDoublyLinkedList|null A list of Link objects. + */ + public function getLinks(bool $create = true) + { + if ($create && $this->links === null) + $this->links = new TypedDoublyLinkedList('libgpx\Link'); + return $this->links; + } + + /** + * Fetch the waypoint symbol. + * + * @return string|null The symbol or null if not set. + */ + public function getSymbol() + { + return $this->symbol; + } + + /** + * Set the symbol representing this point. + * + * @param string $symbol The name of the symbol or null to delete. + * @return void + */ + public function setSymbol(string $symbol = null) + { + $this->symbol = $symbol; + } + + /** + * Fetch the type of the point. + * + * @return string|null The type or null if not set. + */ + public function getType() + { + return $this->type; + } + + /** + * Set the type of the point. + * + * @param string $type The type or null to delete. + * @return void + */ + public function setType(string $type = null) + { + $this->type = $type; + } + + /** + * Fetch the fix type for this point. + * + * @return string|null The fix type or null if not set. + */ + public function getFix() + { + return $this->fix; + } + + /** + * Set the fix type. + * + * @param string $fix "none", "2d", "3d", "dgps", "pps" or null to delete. + * @return void + * @throws DomainException If an invalid fix type is passed. + */ + public function setFix(string $fix = null) + { + if ($fix !== null && !in_array($fix, ['none', '2d', '3d', 'dgps', 'pps'])) + throw new DomainException(sprintf('Unknown fix type "%s"', $fix)); + $this->fix = $fix; + } + + /** + * Fetch the number of satellites used to calculate the fix. + * + * @return int The number of satellites or null if not set. + */ + public function getSatellites() + { + return $this->satellites; + } + + /** + * Set the number of satellites used to calculate the fix. + * + * @param int $satellites The number if satellites or null to delete. + * @return void + * @throws DomainException If $satellites < 0 + */ + public function setSatellites(int $satellites = null) + { + if ($satellites !== null && $satellites < 0) + throw new DomainException( + sprintf('Satellites must be >= 0 but got %s', $satellites) + ); + $this->satellites = $satellites; + } + + /** + * Fetch the hdop of the point. + * + * @return float|null The hdop or null if not set. + */ + public function getHdop() + { + return $this->hdop; + } + + /** + * Set the hdop of the point. + * + * @param float $hdop The hdop or null to delete. + * @return void + */ + public function setHdop(float $hdop = null) + { + $this->hdop = $hdop; + } + + /** + * Fetch the vdop of the point. + * + * @return float|null The vdop or null if not set. + */ + public function getVdop() + { + return $this->vdop; + } + + /** + * Set the vdop of the point. + * + * @param float $vdop The vdop or null to delete. + * @return void + */ + public function setVdop(float $vdop = null) + { + $this->vdop = $vdop; + } + + /** + * Fetch the pdop of the point. + * + * @return float|null The pdop or null if not set. + */ + public function getPdop() + { + return $this->pdop; + } + + /** + * Set the pdop of the point. + * + * @param float $pdop The pdop or null to delete. + * @return void + */ + public function setPdop(float $pdop = null) + { + $this->pdop = $pdop; + } + + /** + * Fetch the age of the DGPS data. + * + * @return float The age of DGPS data in seconds or null if not set. + */ + public function getAgeOfDGPSData() + { + return $this->ageofdgpsdata; + } + + /** + * Set the age of the DGPS data. + * + * @param float $ageofdgpsdata The age in seconds or null to delete. + * @return void + */ + public function setAgeOfDGPSData(float $ageofdgpsdata = null) + { + $this->ageofdgpsdata = $ageofdgpsdata; + } + + /** + * Fetch the id of the DGPS station. + * + * @return int The DGPS id or null if not set. + */ + public function getDGPSId() + { + return $this->dgpsid; + } + + /** + * Set the id of the DGPS station. + * + * @param int $dgpsid The station id. + * @return void + * @throws DomainException If the station id < 0 or > 1023. + */ + public function setDGPSId(int $dgpsid = null) + { + if ($dgpsid !== null && ($dgpsid < 0 || $dgpsid > 1023)) + throw new DomainException( + sprintf('DGPS id must be >= 0 and <= 1023 but got %s', $dgpsid) + ); + return $this->dgpsid = $dgpsid; + } + + /** + * Fetch a list of XML strings that describe unsupported elements. + * + * @param boolean $create Create the list if it does not already exist. + * @return TypedDoublyLinkedList|null A list of XML strings. + */ + public function getExtensions(bool $create = true) + { + if ($create && $this->extensions === null) + $this->extensions = new TypedDoublyLinkedList('string'); + return $this->extensions; + } + + /** + * Fetch a bounding box that covers the feature. + * + * @return Bounds|null A bounding box covering the extent of the feature or + * null if not applicable. + */ + public function getBounds() + { + return new Bounds($this->lat, $this->lon, $this->lat, $this->lon); + } +} diff --git a/src/libgpx/xmlreader.php b/src/libgpx/xmlreader.php index f3f2829..2b578be 100644 --- a/src/libgpx/xmlreader.php +++ b/src/libgpx/xmlreader.php @@ -98,9 +98,9 @@ class XMLReader extends \XMLReader * @return boolean Returns true on success or false on failure. * @throws LibXMLException If an error of sufficient magnitude is detected. */ - public function next() { + public function next($localname = null) { libxml_clear_errors(); - $result = parent::next(); + $result = ($localname === null ? parent::next() : parent::next($localname)); $error = libxml_get_last_error(); if ($error instanceof LibXMLError && $error->level >= $this->errorLevel) throw new LibXMLException($error);