Today I had a requirement to match an XML path where a part of the path must have a specified attribute that does not have a specified suffix. Additionally, the solution must be in SimpleXML, and so cannot use the XPath function fn:ends-with
, since that is part of XPath 2.0 and hence isn’t supported. Phew!
Here is some XML that illustrates my use case. I want to select any primary key columns from tables that are not versionable; in this example, just one column would be returned.
<database> <table name="event"> <column name="id" type="integer" required="true" primaryKey="true" /> <column name="name" type="varchar" size="50" required="true" /> <column name="description" type="varchar" size="250" /> <column name="location" type="varchar" size="250" /> </table> <table name="event_versionable"> <column name="id" type="integer" required="true" primaryKey="true" /> <column name="name" type="varchar" size="50" required="true" /> <column name="description" type="varchar" size="250" /> <column name="location" type="varchar" size="250" /> </table> </database>
One simple solution is thus:
$suffix = '_versionable'; $match = "contains(@name, '$suffix')"; $search = " /database/table[not($match)]/column[@primaryKey=\"true\"] "; $keys = $this->xml->xpath($search);
However that will also match columns in tables that have ‘_versionable’ somewhere in the name attribute other than at the end, which isn’t quite what is needed. Using this StackOverflow answer, I have come up with the following:
$suffix = '_versionable'; $match = " substring( @name, string-length(@name) - string-length('$suffix') + 1 ) = '$suffix' "; $search = " /database/table[not($match)]/column[@primaryKey=\"true\"] "; $keys = $this->xml->xpath($search);
Note that the answer on StackOverflow uses name(), which I think returns the tag name; I’ve used @name instead, as I want the value of the attribute called ‘name’.