Refactor Examples - #36
Merged
Merged
Conversation
jakejackson1
force-pushed
the
refactor-examples
branch
from
December 11, 2022 09:44
9bb11c4 to
5fa665f
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #36 +/- ##
=========================================
Coverage 89.44% 89.44%
Complexity 1342 1342
=========================================
Files 26 26
Lines 3023 3023
=========================================
Hits 2704 2704
Misses 319 319 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
jakejackson1
force-pushed
the
refactor-examples
branch
2 times, most recently
from
December 12, 2022 21:58
8cf04b0 to
ee2e99f
Compare
jakejackson1
force-pushed
the
refactor-examples
branch
from
December 1, 2023 12:15
c1dcfa7 to
4c0100b
Compare
jakejackson1
force-pushed
the
refactor-examples
branch
from
April 12, 2024 07:27
4c0100b to
d9e304d
Compare
jakejackson1
force-pushed
the
refactor-examples
branch
2 times, most recently
from
August 21, 2026 04:16
d84beb9 to
61ad4a9
Compare
Reorganise every example into its own subdirectory with an index.php, fix the ones that no longer ran, and add CI so they cannot rot again unnoticed. Structure --------- Each example is now a directory with an index.php and its own fixtures, so the whole thing can be browsed, and the seven that were still loose in examples/ have been converted to match: simple_example.php -> hello-world/ techniques.php -> iterating-over-matches/ svg.php -> create-svg-document/ rss.php -> generating-rss-feed/ odt.php -> basic-odt-parser/ parse_php.php -> parsing-php-source/ sparql.php -> sparql-endpoint-query/ Repairs ------- Most of the remaining examples were dead. rss.php required the long-removed qp.php; sparql.php referenced an unqualified QueryPath class; techniques.php demonstrated eachLambda(), which calls create_function() and so cannot run on PHP 8; odt.php read from a tmp/ directory that never existed, and its .odt fixture had been truncated to zero bytes. The examples that had already been converted had since broken too: - Wikipedia, Urban Dictionary, and api.jquery.com now reject requests with no User-Agent. Those examples send one. html5qp() fetches URLs through masterminds/html5, which takes no stream context, so the Urban Dictionary example fetches the page itself and parses the markup as a string. - jQuery's api.xml is currently served truncated, so that example parses in recovery mode and uses the entries that do arrive. - The docx parser resolved its fixture relative to the working directory. - The Urban Dictionary example picked a random index with random_int(0, 7) from a list of seven, so one run in eight selected nothing. It now counts the matches and guards the empty case. - The cURL example queried MusicBrainz, which throttles by IP address and so answered with a 503 from any shared address - a CI runner, an office. It has been rewritten against the PubMed E-utilities API, which keeps the two-request REST shape the example is there to demonstrate and adds a nice use of attribute selectors, since PubMed names its fields with a Name attribute rather than a tag per field. Error paths ended with die($message), which exits 0 - a caught QueryPath exception looked like success. They now echo the message and exit(1), which reads the same in a browser and is correct on the command line. Tests ----- QueryPathTests\ExamplesTest runs every offline example in a subprocess and asserts it exits cleanly, emits no PHP diagnostic, and produces a reasonable amount of output. It is part of the unit suite, so the examples are exercised on every supported PHP version on every pull request. The examples that call third-party services are excluded from that suite, so it stays fast and deterministic. The new Examples workflow runs those weekly, on demand, and whenever an example changes. It is a blocking check: the examples retry when an API asks them to slow down, so a failure there means an example genuinely needs attention. The split is one list in ExampleRunner::NETWORK_EXAMPLES; anything not on it is treated as offline and runs with allow_url_fopen disabled, so a new example that needs the network but is missing from the list fails immediately rather than becoming flaky. composer run test:examples runs the whole set locally. Documentation ------------- examples/quickstart-guide.md described the qp.php autoloader and claimed the qp()/htmlqp() functions do not work under Composer, neither of which has been true for some time. It has been rewritten and now indexes every example with a note on what it covers. examples/README.md is a short landing page pointing at it, so the list is maintained in one place. Also drop the unreferenced The_Beatles.rdf, testGrid.html, and out.svg fixtures, fix the "retrival" typo in the curl example's directory name, add ext-dom and ext-simplexml to require (ext-zip to require-dev), and correct two docblock types in src/DOMQuery.php. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jakejackson1
force-pushed
the
refactor-examples
branch
from
August 21, 2026 04:22
61ad4a9 to
296d828
Compare
jakejackson1
marked this pull request as ready for review
August 21, 2026 04:26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Modernise and refactor the examples in the
/examples/directory, repair the ones that no longer ran, and add CI so they cannot rot again unnoticed.Pull Request type
Please check the type of change your PR introduces:
What is the current behavior?
Most of the examples were written between 2009 and 2012 and had not kept up. They sat loose in
/examples/with no organisation, and a good number of them simply did not run:rss.phprequired../src/QueryPath/QueryPath.php— removed years ago.sparql.phpreferenced an unqualifiedQueryPathclass, fatal since the move to namespaces.techniques.phpdemonstratedeachLambda(), which callscreate_function()and cannot run on PHP 8.odt.phpread from atmp/directory that was never in the repository, and its.odtfixture had at some point been truncated to zero bytes.User-Agent, and MusicBrainz answers back-to-back requests with a 503.Nothing checked any of this, so the breakage was only discoverable by running an example and watching it fail.
What is the new behavior?
Structure
Every example is now its own directory with an
index.phpand its own fixtures. The seven that were still loose have been converted to match:simple_example.phphello-world/techniques.phpiterating-over-matches/svg.phpcreate-svg-document/rss.phpgenerating-rss-feed/odt.phpbasic-odt-parser/parse_php.phpparsing-php-source/sparql.phpsparql-endpoint-query/All 17 examples run clean.
Repairs
Beyond the four dead scripts above, the examples that had already been converted needed work too:
html5qp()fetches URLs through masterminds/html5, which takes no stream context, so thecontextoption is silently ignored there. That example fetches the page itself and parses the markup as a string.api.xmlis currently served truncated (the etag confirms the file is short at the source, and it ends mid-tag). That example parses in recovery mode and uses the 331 entries that do arrive.random_int(0, 7)from a list of seven, so one run in eight selected nothing and printed an empty word. It now counts the matches and guards the empty case. Found by the new test suite, not by reading.esearchreturns IDs,esummaryreturns the records) and adds a good use of attribute selectors, since PubMed names its fields with aNameattribute rather than a tag per field. It also retries when an API asks it to slow down.Error paths ended with
die($message), which exits 0 — so a caughtQueryPath\Exceptionlooked like success to anything checking exit codes. They nowechothe message andexit(1), which reads the same in a browser and is correct on the command line.Tests
QueryPathTests\ExamplesTestruns every offline example in a subprocess and asserts it exits cleanly, emits no PHP diagnostic, and produces a reasonable amount of output. It is part of the unit suite, so the examples are now exercised on every supported PHP version on every pull request without a separate workflow.The examples that call third-party services are excluded from that suite, so it stays fast and deterministic. The new
Examplesworkflow runs those weekly, on demand, and whenever an example changes. It is a blocking check — the examples retry when an API asks them to slow down, so a failure there means an example genuinely needs attention rather than a remote host having a slow minute.The split is a single list,
ExampleRunner::NETWORK_EXAMPLES. Anything not on it is treated as offline and run withallow_url_fopendisabled, so a new example that needs the network but is missing from the list fails immediately rather than quietly becoming flaky.To run them by hand:
Documentation
examples/quickstart-guide.mddescribed theqp.phpautoloader and claimed thatqp()andhtmlqp()do not work under Composer — neither has been true for some time, and it made no mention ofhtml5qp()at all. It has been rewritten and now indexes every example with a note on what it covers.examples/README.mdis a short landing page pointing at it, so the list is maintained in one place.Housekeeping
The_Beatles.rdf,testGrid.html, andout.svgfixtures.retrivaltypo in the curl example's directory name.ext-domandext-simplexmltorequire, andext-ziptorequire-dev(the docx and odt examples need it).src/DOMQuery.php.Does this introduce a breaking change?
No library code changed. The only edits under
src/are two docblock corrections.Other information
Two library bugs surfaced while writing these examples. Both are filed separately rather than fixed here, since this PR is deliberately confined to
examples/, tests, and CI:?when an HTML-parsed document is serialized withhtml()/innerHTML(), which corrupts any PHP template round-tripped through QueryPath.writeHTML()is unaffected, which is whatparsing-php-source/uses.:eq,:first,:last,:lt,:gt,:odd,:even) index siblings rather than the match set, so all seven return the wrong elements whenever a match set spans more than one parent.:eq(0)never matches anything.On the earlier thought about moving the examples into their own repository: the new test suite ties them to the library's PHP version matrix, which is worth more while they are in-tree. Splitting them out later would mean giving that up or rebuilding it.
Verified: 319 tests pass,
composer run lintandlint:min-phpclean across all 77 files, all 17 examples run.