Repository navigation
Start PHP with no Device Detection data file, and run the on-premise examples with nothing set - #91
Merged
Merged
Conversation
…file Each case starts its own PHP with the extension and no data file setting, an empty one, one naming a missing file, one naming a file that is not a data file, and settings the engine cannot be built with. PHP must start, engine_get() must give null, and DeviceDetectionOnPremise must refuse with a message naming FiftyOneDegreesHashEngine.data_file. Every on-premise console example must run with nothing set, finding or downloading the free Lite file, and the web example must answer with a page saying what to set.
… loaded The extension builds its engine only from a data file that exists, and a failure to build it gives a warning rather than stopping PHP. The setting and its default are unchanged. DeviceDetectionOnPremise refuses with a message naming FiftyOneDegreesHashEngine.data_file when no engine was built, or when the extension is not loaded. Every on-premise example calls ExampleUtils::startWithEngine first. With no engine, a console example finds the free Lite file, downloading it when it is missing, and runs again in a PHP started with that file, and a web example answers with a page saying which php.ini setting to set and where the free file is.
ini_get_all finds an extension by the lower case name PHP keeps it under, so the mixed case name gave a warning and no value. The console examples pass an empty list when there are no command line arguments, and the setup page helper is private.
Generated with SWIG 4.5.1 by on-premise/generate-wrapper.sh in run 43535118510, whose --check then passed.
…launcher A launcher started with nothing set must find the data file, the free Lite file downloaded when there is none, and start the PHP built-in server with it, so the Device Detection page shows the detection and the IP Intelligence page shows the lookup. The Device Detection page shown when PHP started without the engine must give the launcher command.
… file device-detection/onpremise/examples/onpremise/serve.php and ip-intelligence/examples/onpremise/serve.php find the data file as the console examples do, the free Lite file downloaded when there is none, and run the PHP built-in server with it in the foreground, so a web example runs from a fresh clone with nothing set. web-mixed also gets the Device Detection data file. The page a Device Detection web example shows when PHP started without the engine gives the launcher command. The readmes and AGENTS.md start the web examples with the launchers.
jwrosewell
marked this pull request as ready for review
November 14, 2026 05:20
jwrosewell
marked this pull request as draft
November 14, 2026 05:22
rc holds #88, #89, #97 and #98. Two conflicts, each resolved keeping both sides. In AGENTS.md the Pitfalls list keeps this branch's text for a data file that is not there and #88's item on a wrapper from another SWIG, which replaces the item on SWIG older than 4.1. In device-detection/onpremise/readme.md #98's text runs the commands from device-detection/onpremise, so the web example's start command is php examples/onpremise/serve.php gettingStartedWeb.php, introduced as the launcher.
With both extensions loaded and no data file named for either, the launcher gives the page both data files, the free Lite files downloaded when they are missing, and the page shows the Device Detection and IP Intelligence results with no error. Loading both extensions in one PHP works since #88.
…s short name The AGENTS.md start command for the mixed web page now loads only the two extensions and runs ip-intelligence/examples/onpremise/serve.php web-mixed. failureToMatch.php and manualDataUpdate.php now import ExampleUtils, so startWithEngine is called as ExampleUtils::startWithEngine.
jwrosewell
marked this pull request as ready for review
November 14, 2026 08:25
This was referenced Nov 14, 2026
Open
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.
Closes #93. Also makes every on-premise Device Detection example, and the IP Intelligence web examples, run from a fresh clone with nothing set.
The problem
Loading
FiftyOneDegreesHashEngine.sostops PHP before any script runs whenFiftyOneDegreesHashEngine.data_filenames no data file that exists. With the setting left out, PHP stops with "The data file '/usr/lib/php5/51Degrees.hash' could not be found", an uncaughtStatusCodeExceptionand exit code 134. This was found in #88's run 43499740734, on PHP 8.3 and 8.5. The%minitblock ofhash-php.ibuilds the engine when PHP starts with nothing catching the error, so a missing file, an empty setting, a file that is not a data file, or settings the engine cannot use all stop PHP the same way. The first red run below shows each of them ending with exit code 134.So a fresh clone cannot run any on-premise Device Detection example without first downloading the Lite file by hand and naming it in php.ini.
downloadMissingDataFilewas only reached fromuseDataFileFromEnv, and only when_51DEGREES_DD_PATHis set, and no console example called either. IP Intelligence already runs its console examples again in a PHP started with the file it finds or downloads (ip-intelligence/examples/classes/ExampleUtils.php). Neither package's web examples can do that, because a request cannot start PHP again, so the Device Detection readme asked for php.ini first, and the IP Intelligence readme asks for the data file in php.ini for its web pages too.What the specification says
This is
required-examples.md, lines 91 to 97, under "On-Premise Device Detection"."Every example" includes the web examples, and a start command that the person has to complete with a path is a setting. So a web example also has to start from a fresh clone with nothing set, which is what the launchers below do.
What changed and why
hash-php.i). It builds the engine only when the setting names a file that exists, asipi-php.idoes, and catches a failure to build it. With no engine,FiftyOneDegreesHashEngine::engine_get()gives null. A setting naming a missing file, or a file or settings the engine cannot be built from, gives a PHP warning when PHP starts with the path and the reason. Leaving the setting out, with no file at the default path, gives no warning./usr/lib/php5/51Degrees.hashloads the same file as before.DeviceDetectionOnPremiserefuses with a message (DeviceDetectionOnPremise.php,Messages.php), asIpIntelligenceOnPremisedoes, rather than failing later with a null. With no engine, the message says the setting is not set, or names the missing file, or names the file the engine could not be built from and points at the startup warning, and then says to setFiftyOneDegreesHashEngine.data_filein php.ini. Without the extension it says the extension is not loaded, where it used to end in "Class not found". The new public names areMessages::EXTENSION_NOT_LOADED,NO_ENGINE,DATA_FILE_NOT_SET,DATA_FILE_MISSING,DATA_FILE_NOT_LOADEDandConstants::DATA_FILE_SETTING, and none is removed or renamed.hash_wrap.cxxwas generated with SWIG 4.5.1 bygenerate-wrapper.shon a runner, building SWIG asphp-wrapper-check.ymldoes (run 43535118510, job "Generate the Device Detection wrapper", whose--checkthen passed). It differs from the one onrconly in the two blockshash-php.ichanges.php_FiftyOneDegreesHashEngine.hcame out the same.ExampleUtils::startWithEnginefirst (ExampleUtils.php), as the IP Intelligence console examples do. When the extension built an engine, the example carries on. Otherwise a console example takes the data file its first argument names, then the one_51DEGREES_DD_PATHnames, then the Lite file incxx/data/device-detection, downloaded with the data repository's script when it is missing, and runs again in a PHP started with-d FiftyOneDegreesHashEngine.data_file=<file>. It says so on standard error, with the command, and says to set the data file in php.ini so that PHP builds the engine once. A named file that does not exist is refused with a message rather than replaced by the Lite file. The console examples aregettingStartedConsole.php,metadataConsole.php,matchMetrics.php,failureToMatch.phpandmanualDataUpdate.php. A web example cannot start PHP again, sogettingStartedWeb.phpanduserAgentClientHints-Web.php, started some other way with no engine, answer with HTTP 500 and a page giving the launcher command, the php.ini setting, and where the free file is or the command that downloads it.device-detection/onpremise/examples/onpremise/serve.phpandip-intelligence/examples/onpremise/serve.phptake the example's name, then an optional data file and port (3000 unless given). Each finds the data file as its package's console examples do, the free Lite file downloaded when there is none, or uses php.ini's file when PHP built an engine from it and no other file is named. It then runsphp -d <setting>=<file> -S localhost:<port>in the foreground, printing the address and saying to set the file in php.ini for real use. The server keeps the options the launcher was started with, such as-d extension=..., where the system gives PHP's own command line, as Linux does. The Device Detection launcher servesgettingStartedWeb.phporuserAgentClientHints-Web.phpas the router script. The IP Intelligence launcher serves theweborweb-mixedfolder, and forweb-mixedalso gives the Device Detection data file that_51DEGREES_DD_PATHnames, or else the Lite file, when PHP built no Device Detection engine. For IP Intelligence this draft adds only the launcher, its tests and the start commands, and leaves the web pages as #88 and #98 made them.device-detection/onpremise/readme.mdsays what happens with no usable data file, that the graph settings no longer stop PHP, how the examples find the file, and starts the web example with the launcher.ip-intelligence/readme.mdgives the launcher command for its web examples.AGENTS.mdruns the Device Detection console example from a fresh clone with only-d extension=..., gives the launcher commands for both packages' web examples, including the mixed page in place of Load both on-premise extensions in one PHP, and run the mixed examples on the free data files #88's-Scommand that named both data files, keeps the php.ini way for building the engine once, updates two pitfalls, and says in "What CI tests" which tests run the examples with nothing set.rc.rc2104ad7, which holds #88, #89, #97 and #98, is merged in as f98bbec, with two conflicts each resolved keeping both sides. In theAGENTS.mdPitfalls list this draft's text for a data file that is not there stays, followed by Load both on-premise extensions in one PHP, and run the mixed examples on the free data files #88's item on a wrapper from another SWIG, which replaces the item on SWIG older than 4.1. Indevice-detection/onpremise/readme.mdEvery example ends with Find out more, and gives the tagged links it needs #98's text runs the commands fromdevice-detection/onpremise, so the web start command isphp examples/onpremise/serve.php gettingStartedWeb.php, introduced as the launcher. The merge brought no change tocxxor tohash-php.i, so the committed wrapper still matches. Since Every example ends with Find out more, and gives the tagged links it needs #98 importsExampleUtilsinfailureToMatch.phpandmanualDataUpdate.php, both now callExampleUtils::startWithEngine.Before you upgrade
PHP now starts when
FiftyOneDegreesHashEngine.data_filenames no data file, a missing file, or a file or settings the engine cannot be built from. Before, PHP stopped with exit code 134 before running any script. NowFiftyOneDegreesHashEngine::engine_get()gives null, PHP gives a warning when it starts unless the setting is left out with no file at its default path, andnew DeviceDetectionOnPremise()throws an exception naming the setting. Code that callsengine_get()directly should check for null. A health check that relied on PHP failing to start should check thatDeviceDetectionOnPremisecan be constructed instead. The setting's name and its default path are unchanged.Tests
device-detection/onpremise/tests/StartWithoutDataFileTests.php, added to the Unit suite inphpunit.xml, has 16 tests.ip-intelligence/tests/ServeWebExamplesTests.php, added to the Integration suite, has 2. Each test starts its own PHP with the system's own settings and a php.ini that only loads the extensions it needs, leaving out the package's php.ini and the environment variables that name a data file. The extension is loaded from a settings folder rather than the command line, so a PHP an example or a launcher starts loads it too, macOS included.engine_get()null with no data file setting, an empty one, one naming a missing file, one naming a file that is not a data file, and the Lite file with settings the engine cannot use, with the warning in the last three.DeviceDetectionOnPremiserefuses with the message for each of no setting, a missing file and a file it cannot load, and without the extension.gettingStartedWeb.phpunder a built-in server started with no data file answers HTTP 500 with the page naming the setting, the Lite file and the launcher.serve.php gettingStartedWeb.phpwith nothing set answers HTTP 200 with the detection ("Platform Name:"), and the IP Intelligenceserve.php webwith nothing set answers HTTP 200 with the lookup ("Registered Name:") and no failure message.serve.php web-mixed, with both extensions loaded and no data file named for either, answers HTTP 200 with both results ("Platform Name:" and "Registered Name:") and no error.Runs
Each throwaway run builds both extensions as CI does (
ci/setup-environment.ps1thenci/build-project.ps1) on Ubuntu PHP 8.0, 8.3 and 8.5 and macOS PHP 8.5, and runs the new tests, and after thercmerge also the example tests that #88 and #98 added. The PHP that runs PHPUnit gets its own copy of the Device Detection Lite file,cxx/data/device-detectionis left without one, and no IP Intelligence data file is fetched, so the examples and launchers have to download them.tmp/fixer-dd-redat 5ef56f0, which isrc6d95eee with only the first 15 tests and a temporary workflow. Every job printed "Tests: 15, Assertions: 32, Failures: 15." In each, 13 tests failed on exit code 134 with the uncaughtStatusCodeException, the refusal without the extension onClass "FiftyOneDegreesHashEngine" not found, and the web example on "The server did not answer."tmp/fixer-dd-genat ab33127. Its job "Generate the Device Detection wrapper" built SWIG 4.5.1 as the wrapper check does, rangenerate-wrapper.shand then--check, and uploaded the file committed as b41b100. Its preview job found 2 failures, both fromini_get_allneeding the extension's lower case name, fixed in 9488de2 before the green run.tmp/fixer-dd-greenat 1191ea7, which is b41b100 with only the temporary workflow. Every job printed "OK (15 tests, 63 assertions)", andcxx/data/device-detectionheld the downloaded Lite file afterwards.tmp/fixer-dd-red-bat 2a170d2, which is b41b100 with only the launcher tests (7fa876b) and a temporary workflow. Every job printed "Tests: 16, Assertions: 67, Failures: 2." for Device Detection, the 500 page lacking the launcher command and the launcher test failing on "Could not open input file: examples/onpremise/serve.php", and "Tests: 1, Assertions: 3, Failures: 1." for IP Intelligence, on the same message.tmp/fixer-dd-green-bat c047d0e, which is 4207b7e, the head at the time, with only the temporary workflow. Every job printed "OK (16 tests, 69 assertions)" for Device Detection and "OK (1 test, 6 assertions)" for IP Intelligence, whose launcher downloaded the Lite file.tmp/fixer-dd-red-c3at 863f408, which isrc2104ad7 with onlyServeWebExamplesTests.phpand the same workflow. On Ubuntu PHP 8.0, 8.3 and 8.5 the IP Intelligence step printed "Tests: 12, Assertions: 127, Failures: 2.", the two launcher tests forwebandweb-mixedfailing on "Could not open input file: examples/onpremise/serve.php", while Load both on-premise extensions in one PHP, and run the mixed examples on the free data files #88's and Every example ends with Find out more, and gives the tagged links it needs #98's tests passed. Its macOS PHP 8.5 job stopped at the samercbuild fault as below.tmp/fixer-dd-green-c3at 7514bae, which is the head c395e3f with only a temporary workflow. That workflow loads each extension from a php.ini in the settings folder, as CI does, and also runs Every example ends with Find out more, and gives the tagged links it needs #98'sExamplesWithoutSettingsTestsandWebExamplesWithoutSettingsTestsand Load both on-premise extensions in one PHP, and run the mixed examples on the free data files #88'sMixedExamplesTests. On Ubuntu PHP 8.0, 8.3 and 8.5 the Device Detection step printed "OK (23 tests, 181 assertions)" and the IP Intelligence step "OK (12 tests, 134 assertions)", or "OK (12 tests, 138 assertions)" on PHP 8.5. The macOS PHP 8.5 job stopped while building the IP Intelligence extension from the wrapper onrc2104ad7, with "assigning to 'zend_class_entry *' (aka '_zend_class_entry *') from incompatible type 'void *'" atipi_wrap.cxxline 1631. That is a fault onrcthat #100 fixes and this draft does not touch. The launcher tests passed on macOS PHP 8.5 in the green run for the launchers above, before Load both on-premise extensions in one PHP, and run the mixed examples on the free data files #88 was onrc. Two earlier runs for this step, 43561923735 and 43562112342, loaded the extension into PHPUnit with-d, which the PHP that Every example ends with Find out more, and gives the tagged links it needs #98's tests start does not inherit, so they are not cited.tmp/pr-matrix-91-c, passed. All 9 "Setting 'PullRequestSha'" lines name c395e3f, and Configure printed "Left out 5 macOS entries". On Ubuntu PHP 8.0, 8.1, 8.2, 8.3, 8.4 and 8.5, with each package's php.ini naming its data file, the on-premise Device Detection Unit suite printed "OK (88 tests, 381 assertions)" and the IP Intelligence Integration suite, which holds the launcher tests, printed "OK (74 tests, 982 assertions)", or 986 on PHP 8.3, as in job 132361027476 (Ubuntu PHP 8.3). Both counts include the example tests Load both on-premise extensions in one PHP, and run the mixed examples on the free data files #88 and Every example ends with Find out more, and gives the tagged links it needs #98 added. The Windows PHP 8.3 job skips both on-premise packages because their extensions are not built on Windows, and the Ubuntu PHP 7.4 job skips them because they require PHP 8.0. This build replaces run 43539053011 on b41b100 and run 43551019860 on 4207b7e, both of which passed before thercmerge.macOS was left out of this draft's full build under the 9 November decision. The rc head is built with macOS before it merges into main.
What remains
ip-intelligence/examples/onpremise/mixed/gettingstarted-console.php, still needs the Device Detection data file named. ItsstartWithEngineruns it again with the IP Intelligence data file only, so with nothing set for Device Detection,DeviceDetectionOnPremisenow refuses with the message above where PHP used to stop when it started. This is read from the code and was not run. Running it with nothing set needs the IP IntelligencestartWithEngineto pass the Device Detection data file too, as the launcher does forweb-mixed, which is a change to the IP Intelligence example helper and is not in this draft.