Skip to content

Instantly share code, notes, and snippets.

@DanielEScherzer
Last active October 18, 2024 03:00
Show Gist options
  • Save DanielEScherzer/1ca7856f2c25dbf30ffda1f9f529ffdd to your computer and use it in GitHub Desktop.
Save DanielEScherzer/1ca7856f2c25dbf30ffda1f9f529ffdd to your computer and use it in GitHub Desktop.
Invoking methods on uninitialized objects
<?php
$skip = [
"SensitiveParameterValue::getValue",
"SQLite3Result::__construct",
"Dom\\Node::__construct",
"Dom\\NamespaceInfo::__construct",
"DOMDocument::registerNodeClass",
"ArrayObject::setIteratorClass",
"ArrayIterator::seek",
"SplFileInfo::_bad_state_ex",
"GlobIterator::count",
"SplPriorityQueue::setExtractFlags",
"SplObjectStorage::current",
"SplObjectStorage::seek",
"php_user_filter::filter",
"ReflectionMethod::__construct",
"ReflectionMethod::createFromMethodName",
"ReflectionClassConstant::__construct",
"ReflectionExtension::__construct",
"ReflectionZendExtension::__construct",
"ReflectionAttribute::__construct",
"ReflectionEnum::__construct",
"ReflectionEnumUnitCase::__construct",
"ReflectionEnumBackedCase::__construct",
"Phar::__construct",
"Phar::mount",
"Phar::mungServer",
"PharData::__construct",
"PharData::mount",
"PharData::mungServer",
"PharFileInfo::__construct",
"SimpleXMLElement::__construct",
"XMLReader::getParserProperty",
"XMLReader::fromStream",
"XMLWriter::toStream",
// Extensions not enabled by default
"DOMImplementation::getFeature",
"IntlBreakIterator::getIterator",
"IntlRuleBasedBreakIterator::__construct",
"mysqli::connect",
"mysqli::poll",
"mysqli_stmt::bind_result",
"SNMP::setSecurity",
"SoapClient::__construct",
"SoapServer::__construct",
"SoapServer::setClass",
"tidy::getOpt",
"tidy::getOptDoc",
"_ZendTestClass::returnsThrowable",
"_ZendTestChildClass::returnsThrowable",
"ZendTestForbidDynamicCall::call",
"ZendTestForbidDynamicCall::callStatic",
"ZipArchive::addPattern",
// Types not in reflection
"IntlDateFormatter::formatObject",
// 16215 (type not in reflection)
"RecursiveTreeIterator::__construct",
// 16216 (type not in reflection)
"ReflectionParameter::__construct",
// 16217 (seg fault)
"SplFileObject::fputcsv",
// 16218 (type not in reflection)
"PhpToken::is",
];
$skip = array_fill_keys( $skip, true );
$messages = [
"Typed property access" => "/Typed property \S+ must not be accessed before initialization/",
"Unserialize offset" => "/Error at offset 0 of 3 bytes/",
"Unserialize data" => "/Incomplete or ill-typed serialization data/",
"No __clone" => "/Cannot clone object using __clone\(\)/",
"Date* not initialized" => "/Object of type Date\S* has not been correctly initialized/",
"Date* serialization data" => "/Invalid serialization data for Date\S* object/",
"Date* parsing" => "/Unknown or bad (timezone|format)/",
"SQLite3* initialised" => "/The SQLite3\S* object has not been correctly initialised/",
"Dom fetch" => "/Couldn't fetch (DOM|Dom\\\\)\S+/",
"Dom no serialize" => "/(Uns|S)erialization of '(DOM|Dom\\\\)\S+' is not allowed/",
"Dom xpath" => "/Invalid XPath Context/",
"finfo invalid" => "/Invalid finfo object/",
"RecursiveIteratorIterator invalid" => "/The object is in an invalid state/",
"Bad regex" => "/Delimiter must not be alphanumeric/",
"EmptyIterator" => "/Accessing the \S+ of an EmptyIterator/",
"SplFileInfo initialized" => "/Object not initialized/",
"Directories" => "/Failed to open directory:/",
"Spl empty" => "/Can't \S+ (from|at) an empty (datastructure|heap)/",
"SplDoublyLinkedList offset" => "/Argument #1 \(\\\$index\) is out of range/",
"Null offset" => "/Cannot access offset of type null/",
"SplObjectStorage offset" => "/SplObjectStorage::offset\S+ Argument \#1 \(\\\$object\) must be of type object/",
"MultipleIterator" => "/Called (key|current)\(\) on an invalid iterator/",
"No session" => "/Session is not active/",
"PDO data source" => "/Argument \#\d \(\\\$\S+ must be a valid data source name/",
"PDO* uninitialized" => "/(PDO|Pdo\\\\)\S* object is uninitialized/",
"Directory handle" => "/Unable to find my handle property/",
"Reflection failure" => "/Failed to retrieve the reflection object/",
"Phar* uninitialized" => "/Cannot call method on an uninitialized Phar\S* object/",
"Phar halt compiler" => "/__HALT_COMPILER\(\); must be declared in a phar/",
"Phar corruption" => "/internal corruption of phar/",
"SimpleXMLElement uninitialized" => "/SimpleXMLElement is not properly initialized/",
"SimpleXMLElement iterator" => "/Iterator not initialized or already consumed/",
"XMLReader not loaded" => "/Data must be loaded before (reading|expanding)/",
"XMLReader not loaded 2" => "/Schema must be set prior to reading/",
"XMLReader not loaded 3" => "/Cannot access parser properties before loading data/",
"XMLWriter uninitialized" => "/Invalid or uninitialized XMLWriter object/",
// Non-default extensions
"PDO uninitialized" => "/PDO object is not initialized, constructor was not called/",
"NumberFormatter locales" => "/Argument #1 \\(\\\$locale\) \S+ is invalid/",
"Intl construct" => "/Found unconstructed \S+/",
"Intl no new" => "/An object of this type cannot be created with the new operator/",
"mysqli* closed" => "/mysqli\S* object is already closed/",
"mysqli init" => "/mysqli object is not fully initialized/",
"SNMP non-empty" => "/Array of object IDs must not be empty/",
"SoapClient uri" => "/Error finding \"uri\" property/",
"SoapServer fetch" => "/Cannot fetch SoapServer object/",
"Tidy init" => "/tidy object is not initialized/",
"XSLT invalid XML" => "/Argument #1 \\(\\\$\S+\\) must be a valid XML node/",
"Zip not initialized" => "/Invalid or uninitialized Zip object/",
];
// Only for non-reflection running
// $messages = [
// "Typed property access" => "/Typed property \S+ must not be accessed before initialization/",
// "Date* not initialized" => "/Object of type Date\S* has not been correctly initialized/",
// "Date* serialization data" => "/Invalid serialization data for Date\S* object/",
// "Date* parsing" => "/Unknown or bad (timezone|format)/",
// "SQLite3* initialised" => "/The SQLite3\S* object has not been correctly initialised/",
// "Dom fetch" => "/Couldn't fetch (DOM|Dom\\\\)\S+/",
// "Dom no serialize" => "/(Uns|S)erialization of '(DOM|Dom\\\\)\S+' is not allowed/",
// "Dom xpath" => "/Invalid XPath Context/",
// "finfo invalid" => "/Invalid finfo object/",
// "iterator not initialized" => "/The \S+Iterator instance wasn't initialized/",
// "IteratorIterator invalid" => "/The object is in an invalid state/",
// "Bad regex" => "/Delimiter must not be alphanumeric/",
// "EmptyIterator" => "/Accessing the \S+ of an EmptyIterator/",
// "Unserialize offset" => "/Error at offset 0 of 3 bytes/",
// "Unserialize data" => "/Incomplete or ill-typed serialization data/",
// "SplFileInfo initialized" => "/Object not initialized/",
// "Directories" => "/Failed to open directory:/",
// "GlobIterator parent" => "/The parent constructor was not called/",
// "Spl empty" => "/Can't \S+ (from|at) an empty (datastructure|heap)/",
// "SplDoublyLinkedList offset" => "/Argument #1 \(\\\$index\) is out of range/",
// "Null offset" => "/Cannot access offset of type null/",
// "SplObjectStorage offset" => "/SplObjectStorage::offset\S+ Argument \#1 \(\\\$object\) must be of type object/",
// "MultipleIterator" => "/Called (key|current)\(\) on an invalid iterator/",
// "No session" => "/Session is not active/",
// "PDO data source" => "/Argument \#\d \(\\\$\S+ must be a valid data source name/",
// "PDO* uninitialized" => "/(PDO|Pdo\\\\)\S* object is uninitialized/",
// "Directory handle" => "/Unable to find my handle property/",
// "Reflection failure" => "/Failed to retrieve the reflection object/",
// "Phar* uninitialized" => "/Cannot call method on an uninitialized Phar\S* object/",
// "Phar halt compiler" => "/__HALT_COMPILER\(\); must be declared in a phar/",
// "Phar corruption" => "/internal corruption of phar/",
// "SimpleXMLElement uninitialized" => "/SimpleXMLElement is not properly initialized/",
// "SimpleXMLElement iterator" => "/Iterator not initialized or already consumed/",
// "XMLReader not loaded" => "/Data must be loaded before (reading|expanding)/",
// "XMLReader not loaded 2" => "/Schema must be set prior to reading/",
// "XMLReader not loaded 3" => "/Cannot access parser properties before loading data/",
// "XMLWriter uninitialized" => "/Invalid or uninitialized XMLWriter object/",
// ];
function getForType( $type ) {
if ( !$type || $type->allowsNull() ) {
return null;
}
if ( $type instanceof ReflectionIntersectionType ) {
throw new Exception( "ReflectionIntersectionType" );
}
if ( $type instanceof ReflectionUnionType ) {
$type = $type->getTypes()[0];
}
switch ( $type->getName() ) {
case "array": return [];
case "string": return "foo";
case "int": return 1;
case "float": return 123.45;
case "callable": return fn () => true;
case "bool": return true;
case "object": return (object)[ "foo" => "bar" ];
case "stdclass": return (object)[ "foo" => "bar" ];
case "Closure": return array_merge(...);
case "Traversable": return new RecursiveArrayIterator( [] );
case "Iterator": return new RecursiveArrayIterator( [] );
case "RecursiveIterator": return new RecursiveArrayIterator( [] );
case "SplObjectStorage": return new SplObjectStorage();
case "SQLite3": return new SQLite3(":memory:");
// extension: Date
case "DateTimeImmutable": return new DateTimeImmutable();
case "DateTimeInterface": return new DateTimeImmutable();
case "DateInterval": return new DateInterval("P1D");
case "DateTimeZone": return new DateTimeZone("UTC");
case "DateTime": return new DateTime();
// extension: DOM
case "DOMNode": return new DOMText("foo");
case "Dom\\Node": return Dom\HTMLDocument::createEmpty();
case "DOMAttr": return new DOMAttr("foo", "bar");
case "DOMElement": return new DOMElement("br");
case "Dom\\Attr": return Dom\HTMLDocument::createEmpty()->createAttribute("foo");
case "Dom\\AdjacentPosition": return Dom\AdjacentPosition::BeforeBegin;
case "Dom\\Element": return Dom\HTMLDocument::createEmpty()->createElement("br");
case "DOMDocument": return new DOMDocument();
// extension: Reflection
case "ReflectionClass": return new ReflectionClass( UnitEnum::class );
case "PropertyHookType": return PropertyHookType::Get;
// extension: Inttl
case "IntlTimeZone": return IntlTimeZone::createDefault();
case "IntlCalendar": return IntlCalendar::createInstance();
// extension: mysqli
case "mysqli": return new mysqli();
// extension: Soap
case "SoapHeader": return new SoapHeader("foo", "bar");
}
echo "\n";
var_dump( $type->getName() );
throw new Exception( "Not handled" );
}
function getParams( $method ) {
$result = [];
$params = $method->getParameters();
$params = array_slice( $params, 0, $method->getNumberOfRequiredParameters() );
foreach ( $params as $p ) {
$result[] = getForType( $p->getType() );
}
return $result;
}
function doRun( ReflectionMethod $method, object $instance, array $params ) {
$method->invokeArgs( $instance, getParams( $method ) );
return;
if ( $method->isPublic() ) {
$name = $method->name;
$instance->$name( ...$params );
}
}
function runForMethod( $method, $k, $ref ) {
global $skip, $messages;
echo "\tmethod " . $method->name;
if ( array_key_exists( $ref->name . "::" . $method->name, $skip ) ) {
echo "- skipping per skip list\n";
return;
}
$instance = $ref->newInstanceWithoutConstructor();
try {
doRun( $method, $instance, getParams( $method ) );
} catch ( Throwable $e ) {
foreach ( $messages as $note => $regex ) {
if ( preg_match( $regex, $e->getMessage() ) ) {
echo ": $note\n";
return;
}
}
throw $e;
}
echo ": success\n";
}
function runForClass( $clazz ) {
$ref = new ReflectionClass( $clazz );
if ( $ref->implementsInterface( "UnitEnum") ) {
echo " enum, skipping\n";
return;
} elseif ( $ref->isAbstract() ) {
echo " abstract, skipping\n";
return;
}
try {
$ref->newInstanceWithoutConstructor();
} catch ( ReflectionException $e ) {
echo " internal final, skipping\n";
return;
}
$methods = array_filter(
$ref->getMethods(),
static fn ( $meth ) => $meth->class === $clazz
);
if ( $methods === [] ) {
echo " no methods, skipping\n";
} else {
echo "\n";
array_walk( $methods, runForMethod(...), $ref );
}
}
$classes = get_declared_classes();
$n = 0;
foreach ( $classes as $class ) {
$n++;
echo "Trying for class #$n: $class...";
runForClass( $class );
}
echo "Total classes: $n\n";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment