1 ); } } class Other { public static function update( $request ) { return array( 'b' => 2 ); } } PHP; public function testFindsAFunctionInsideAFunctionExistsGuard(): void { $print = SourceFingerprint::fromSource(self::SOURCE, 'et_demo_save'); self::assertNotNull($print); self::assertSame(7, $print->startLine); self::assertSame(13, $print->endLine); self::assertStringStartsWith('function et_demo_save(', $print->source); self::assertStringEndsWith('}', $print->source); self::assertMatchesRegularExpression('/^[0-9a-f]{64}$/', $print->hash); } public function testClassConstantsAndInterpolationDoNotConfuseTheBraceCount(): void { $print = SourceFingerprint::fromSource(self::SOURCE, 'et_demo_save'); self::assertNotNull($print); self::assertStringContainsString('return Foo::class;', $print->source); self::assertStringNotContainsString('class Controller', $print->source); } public function testMethodIsFoundInItsOwnClassOnly(): void { $controller = SourceFingerprint::fromSource(self::SOURCE, 'update', 'ET\Builder\VisualBuilder\Controller'); $other = SourceFingerprint::fromSource(self::SOURCE, 'update', 'Other'); self::assertNotNull($controller); self::assertNotNull($other); self::assertStringContainsString("'a' => 1", $controller->source); self::assertStringContainsString("'b' => 2", $other->source); self::assertNotSame($controller->hash, $other->hash); self::assertNull(SourceFingerprint::fromSource(self::SOURCE, 'update'), 'a method is no top-level function'); self::assertNull(SourceFingerprint::fromSource(self::SOURCE, 'et_demo_save', 'Controller')); self::assertNull(SourceFingerprint::fromSource(self::SOURCE, 'missing')); } public function testCommentsAndWhitespaceDoNotChangeTheHash(): void { $reformatted = str_replace( ["\t\t\$value = ", "if ( \$id ) {"], ["\t\t// Why: demo.\n\t\t\$value = ", "if (\$id)\n\t\t{"], self::SOURCE ); self::assertSame( SourceFingerprint::fromSource(self::SOURCE, 'et_demo_save')?->hash, SourceFingerprint::fromSource($reformatted, 'et_demo_save')?->hash ); } public function testACodeChangeChangesTheHash(): void { $changed = str_replace("'_et_demo'", "'_et_demo_v2'", self::SOURCE); self::assertNotSame( SourceFingerprint::fromSource(self::SOURCE, 'et_demo_save')?->hash, SourceFingerprint::fromSource($changed, 'et_demo_save')?->hash ); } public function testOfReadsTheFileAndThrowsForMissingFunctions(): void { $file = (string) tempnam(sys_get_temp_dir(), 'lit'); file_put_contents($file, self::SOURCE); try { self::assertSame( SourceFingerprint::fromSource(self::SOURCE, 'et_demo_save')?->hash, SourceFingerprint::of($file, 'et_demo_save')->hash ); try { SourceFingerprint::of($file, 'update', 'Missing'); self::fail('a missing method must throw'); } catch (RuntimeException $e) { self::assertStringContainsString('Missing::update() not found', $e->getMessage()); } } finally { unlink($file); } } public function testOfThrowsForAnUnreadableFile(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('is not readable'); SourceFingerprint::of('/nonexistent/lit-divi.php', 'et_demo_save'); } }