classAnalyzer
final class Analyzer
Public API: explicitly declared with
@visibility public.Reads PHP source and reports the SQL statements it can issue.
The whole source tree is indexed before any of it is walked, so a statement assembled from a constant, an enum or a method in another file still resolves.
$catalog = (new \SqlCatalog\Facade\Analyzer())->analyzeSource([
'users.php' => '<?php function all(PDO $db) { return $db->query("SELECT id FROM users"); }',
]);
$catalog->entries()[0]->sql() // => 'SELECT id FROM users'
$catalog->entries()[0]->tables // => ['users']$catalog = (new \SqlCatalog\Facade\Analyzer())->analyzeSource([
'unsafe.php' => '<?php function find(PDO $db) { return $db->query("SELECT * FROM users WHERE id = " . $_GET["id"]); }',
]);
$catalog->entries()[0]->sql() // => 'SELECT * FROM users WHERE id = {$}'
$catalog->entries()[0]->severity()->value // => 'high'Methods§
public function __construct(
ExtensionRegistry|null $extensions = null,
Registry|null $functionModels = null,
)Parameters
$extensions | ExtensionRegistry|null | The extensions available to the run, or null for the built-in ones |
$functionModels | Registry|null | The function interpretations, or null for the built-in models |
Calls 5
- static-call Registry::withBuiltins() line 64
- static-call Builtins::extensions() line 65
- new SourceParser line 66
- new ProgramIndexBuilder line 67
- new EntryFactory line 68
public function withConfiguration(Configuration $configuration): selfAn independent analyzer with the function registrations from catalog settings.
Parameters
$configuration | Configuration |
Returns
selfThrows
RuntimeException When a configured function model cannot be resolvedTest cases 1
Called from 1
Calls 2
- method-call Configuration::apply() line 78
- new
selfline 80
public function extensions(): ExtensionRegistryThe extensions this analyzer can be asked for.
Returns
Test cases 1
Called from 1
public function analyzePaths(
list<string> $paths,
?AnalysisOptions $options = null,
string $root = '.',
list<string> $excluded = [],
): CatalogThe statements the files under the given paths can issue.
Parameters
$paths | list<string> | Files and directories to read |
$options | ?AnalysisOptions | |
$root | string | The directory reported paths are relative to |
$excluded | list<string> | Patterns, matched against reported paths, to leave out |
Returns
Throws
SourceScanException When one of the paths cannot be readUnknownExtensionException When the options name an extension that is not registeredTest cases 3
Calls 3
- method-call SourceScanner::scan() line 107
- new SourceScanner line 107
- method-call Analyzer::analyzeSource() line 111
public function analyzeSource(
array<string, string> $sources,
?AnalysisOptions $options = null,
): CatalogThe statements the given sources can issue.
Parameters
$sources | array<string, string> | Source text, keyed by the path the catalog reports |
$options | ?AnalysisOptions |
Returns
Throws
UnknownExtensionException When the options name an extension that is not registeredTest cases 85
AnalyzeTarget::__invokecallsRecoverTarget::__invokecallsCatalogCommandTest::testStatusReportsFindingsOnlyWhenAskedTocallsExpressionEvaluatorTest::testEvaluateOperandsReachesAStatementNestedSeveralOperatorsDeepcallsExpressionEvaluatorTest::testEvaluateOperandsReachesAStatementWrittenInsideAnOperandcallsCallModelTest::testEvaluateModelsFactoriesMutationsAndLeavesOtherCallsAlonecallsCallModelTest::testMatchesClassRecognizesConcreteFrameworkTypesOnlyWhenEnabledcallsAnalyzerTest::testACallOnAClassTheExtensionsDoNotNameIsNotReportedAtAllcallsAnalyzerTest::testACallOnSomethingThatCouldNotBeNamedIsReportedAsUnmatchedcallsAnalyzerTest::testACallSiteSurvivesFailingToResolveItsStatementcallsAnalyzerTest::testACallSiteSurvivesSittingInAnOperandNothingNeedsTheValueOfcallsAnalyzerTest::testACallSiteSurvivesTheWalkNotReachingItcallsAnalyzerTest::testAHandleReachedThroughAGlobalIsRecognisedWhenSomethingSaysWhatItIscallsAnalyzerTest::testAHelperReturnCutShortByTheLoopBudgetCannotCloseTheCallercallsAnalyzerTest::testAlternativesPairedAcrossIndependentPartsAreMarkedAsSuchcallsAnalyzerTest::testAnalyzeSourceAcceptsAnApplicationSqlPolicycallsAnalyzerTest::testAnalyzeSourceAllowsExtensionsToModelSqlFragmentsFromAnyCallFormcallsAnalyzerTest::testAnalyzeSourceAppliesRegisteredFunctionsAndFallsBackToSourcecallsAnalyzerTest::testAnalyzeSourceBoundsDestructuredAlternativesBeforeTheyExhaustMemorycallsAnalyzerTest::testAnalyzeSourceCatalogsTheStatementsOfTheGivenSourcescallsAnalyzerTest::testAnalyzeSourceCombinesNamedFunctionModelsWithLaravelRegistrationscallsAnalyzerTest::testAnalyzeSourceDistinguishesNamespacedFunctionsAndGlobalBuiltinscallsAnalyzerTest::testAnalyzeSourceDoesNotAssumeCustomModelOrderingUsesTheStandardBuildercallsAnalyzerTest::testAnalyzeSourceDoesNotLoseAlternativesWhenMutatingAJoinedBuildercallsAnalyzerTest::testAnalyzeSourceDoesNotLoseArgumentSideEffectsOnTheBuilderReceivercallsAnalyzerTest::testAnalyzeSourceDoesNotLoseUnknownEffectsInsideNestedPredicatescallsAnalyzerTest::testAnalyzeSourceDoesNotMistakeCollectionMethodsForDatabaseExecutionscallsAnalyzerTest::testAnalyzeSourceFollowsBuilderAliasesStoredInArrayscallsAnalyzerTest::testAnalyzeSourceHandlesPlaceholderExpressionscallsAnalyzerTest::testAnalyzeSourceKeepsAModelledSinkWithoutARegisteredCompilerVisiblecallsAnalyzerTest::testAnalyzeSourceKeepsCapturedBuilderEffectsOpencallsAnalyzerTest::testAnalyzeSourceKeepsCapturedScalarBindingsInNestedPredicatescallsAnalyzerTest::testAnalyzeSourceKeepsConditionalMutationsAsAlternativescallsAnalyzerTest::testAnalyzeSourceKeepsDirectBuilderArrayWritesOpencallsAnalyzerTest::testAnalyzeSourceKeepsEachUnmodelledEscapeOfABuilderOpencallsAnalyzerTest::testAnalyzeSourceKeepsEarlyReturningScopesAndBooleanRegroupingOpencallsAnalyzerTest::testAnalyzeSourceKeepsNamedBuilderArgumentsOpencallsAnalyzerTest::testAnalyzeSourceKeepsObjectsPassedInsideArraysToUnknownHelpersOpencallsAnalyzerTest::testAnalyzeSourceKeepsReferenceRebindingOfBuildersOpencallsAnalyzerTest::testAnalyzeSourceKeepsTheBuilderStatementWhenOnlyAValueIsUnresolvedcallsAnalyzerTest::testAnalyzeSourceKeepsVariableNamesForUnresolvedSqlAndFragmentscallsAnalyzerTest::testAnalyzeSourceLetsRegisteredCallModelsOwnTheirArgumentEffectscallsAnalyzerTest::testAnalyzeSourceListsTheCountAndPageStatementsOfPaginatecallsAnalyzerTest::testAnalyzeSourceNormalizesPlaceholderListsThroughVariablescallsAnalyzerTest::testAnalyzeSourcePassesArgumentsToTraditionalLocalScopescallsAnalyzerTest::testAnalyzeSourcePreservesShortCircuitAlternativesOnTrackedBuilderscallsAnalyzerTest::testAnalyzeSourceRecognizesAuthenticationModelsWithoutVendorFilescallsAnalyzerTest::testAnalyzeSourceRecognizesBothRawAndBuilderCallsOnConcreteConnectionscallsAnalyzerTest::testAnalyzeSourceRefreshesTerminalReceiversAfterEvaluatingArgumentscallsAnalyzerTest::testAnalyzeSourceRefusesAnExtensionThatIsNotRegisteredcallsAnalyzerTest::testAnalyzeSourceReportsAFileItCannotParsecallsAnalyzerTest::testAnalyzeSourceReportsBothOutcomesOfAConditionalBuilderCallbackcallsAnalyzerTest::testAnalyzeSourceResolvesAcrossFilescallsAnalyzerTest::testAnalyzeSourceRetainsBindingsReusedByModelledBuilderCallscallsAnalyzerTest::testAnalyzeSourceRetainsTheLimitAfterReusingAFirstQuerycallsAnalyzerTest::testAnalyzeSourceSupportsRegisteredQueryModelsWithoutLaravelcallsAnalyzerTest::testAnElementReadUnderAnUnknownKeyGivesOneStatementPerElementcallsAnalyzerTest::testAnUndeterminedStatementSaysWhyItIsUndeterminedcallsAnalyzerTest::testAResolvedCallerDoesNotDeleteAnUndeterminedOnecallsAnalyzerTest::testAStaticPropertyNothingAssignsReadsLikeAConstantcallsAnalyzerTest::testAValueBoundToOnePlaceholderStaysWithThatPlaceholdercallsAnalyzerTest::testEntriesOfRecognisesOnlyTheExtensionsTheOptionsNamecallsAnalyzerTest::testFileScopeAliasesCannotHideLaterWritescallsAnalyzerTest::testIssetDoesNotDiscardAnUnknownSqlFragmentcallsAnalyzerTest::testIssetGuardsAConditionallyAssignedSqlFragmentcallsAnalyzerTest::testIssetSqlFragmentsKeepBothSyntacticBranchescallsAnalyzerTest::testMixedExternalAndUnresolvedDependenciesDoNotCloseTheSearchcallsAnalyzerTest::testSortRecordsOrdersByWhereTheStatementIsIssuedcallsAnalyzerTest::testUncertainWritesRemainOpenInsteadOfBecomingEmptycallsAnalyzerTest::testUnconditionalCandidatesNormalizeOnlyWhenStringifiedcallsAnalyzerTest::testValuesDecidedByNestedBranchesStayTogethercallsAnalyzerTest::testValuesDecidedByOneBranchStayTogethercallsAnalyzerTest::testWithConfigurationOverridesTheBuiltinWithoutChangingTheOriginalAnalyzercallsJsonReporterTest::testEntryToArrayCarriesTheFindingscallsJsonReporterTest::testRenderIsTheSameForTheSameCatalogcallsJsonReporterTest::testRenderWritesTheWholeDocumentExactlycallsJsonReporterTest::testSummaryCountsResolvedAndDynamicStatementscallsJsonReporterTest::testTheDocumentMatchesTheSchemaItDeclarescallsJsonReporterTest::testToArrayCarriesTheVersionAndTheStatementscallsTextReporterTest::testEntryLinesAreWrittenExactlycallsTextReporterTest::testEntryLinesShowTheFindingscallsTextReporterTest::testEntryLinesShowTheStatementAndItsParameterscallsTextReporterTest::testRenderWritesTheWholeReportExactlycallsTextReporterTest::testStatusLineSaysHowFarTheAnalyzerGotcallsTextReporterTest::testSummaryLineCountsWhatWasFoundcalls
Called from 1
Calls 5
- new AnalysisOptions line 122
- method-call Analyzer::parse() line 126
- new SourceFile line 126
- new Catalog line 134
- method-call Analyzer::entriesOf() line 134
public function parse(SourceFile $file): ParsedFile|AnalysisProblemThe file parsed, or the problem that stopped it from being parsed.
Parameters
$file | SourceFile |
Returns
Test cases 1
Called from 1
Calls 2
- method-call SourceParser::parse() line 143
- new AnalysisProblem line 145
public function entriesOf(list<ParsedFile> $files, AnalysisOptions $options): list<CatalogEntry>The catalog entries of a parsed source tree.
Parameters
$files | list<ParsedFile> | |
$options | AnalysisOptions |
Returns
list<CatalogEntry>Throws
UnknownExtensionException When the options name an extension that is not registeredTest cases 1
Called from 1
Calls 10
- method-call ExtensionRegistry::sinksOf() line 158
- new Interpreter line 159
- method-call ProgramIndexBuilder::build() line 160
- method-call AnalysisOptions::budget() line 162
- new DeclaredGlobals line 163
- method-call ExtensionRegistry::globalsOf() line 163
- method-call ExtensionRegistry::modelProvidersOf() line 166
- method-call Analyzer::sortRecords() line 169
- method-call EntryFactory::build() line 169
- method-call Interpreter::analyze() line 169
public function sortRecords(list<CatalogEntry> $entries): list<CatalogEntry>The entries in the order a report lists them.
Parameters
$entries | list<CatalogEntry> |
Returns
list<CatalogEntry>Test cases 1
Called from 1
Calls 2
- method-call Catalog::sorted() line 180
- new Catalog line 180
Private surface 5§
Implementation details, listed for orientation only.
private Registry $functionModelsprivate ExtensionRegistry $extensionsprivate SourceParser $parserprivate ProgramIndexBuilder $indexesprivate EntryFactory $entriesTest cases 95§
Test cases that cover or call this symbol, from the coverage report and from the analyzed test sources.
Dedicated tests 73
AnalyzerTest::testACallOnAClassTheExtensionsDoNotNameIsNotReportedAtAllcallsAnalyzerTest::testACallOnSomethingThatCouldNotBeNamedIsReportedAsUnmatchedcallsAnalyzerTest::testACallSiteSurvivesFailingToResolveItsStatementcallsAnalyzerTest::testACallSiteSurvivesSittingInAnOperandNothingNeedsTheValueOfcallsAnalyzerTest::testACallSiteSurvivesTheWalkNotReachingItcallsAnalyzerTest::testAHandleReachedThroughAGlobalIsRecognisedWhenSomethingSaysWhatItIscallsAnalyzerTest::testAHelperReturnCutShortByTheLoopBudgetCannotCloseTheCallercallsAnalyzerTest::testAlternativesPairedAcrossIndependentPartsAreMarkedAsSuchcallsAnalyzerTest::testAnalyzePathsReadsTheFilesUnderThemcallsAnalyzerTest::testAnalyzePathsRefusesAPathItCannotReadcallsAnalyzerTest::testAnalyzePathsSkipsWhatIsExcludedcallsAnalyzerTest::testAnalyzeSourceAcceptsAnApplicationSqlPolicycallsAnalyzerTest::testAnalyzeSourceAllowsExtensionsToModelSqlFragmentsFromAnyCallFormcallsAnalyzerTest::testAnalyzeSourceAppliesRegisteredFunctionsAndFallsBackToSourcecallsAnalyzerTest::testAnalyzeSourceBoundsDestructuredAlternativesBeforeTheyExhaustMemorycallsAnalyzerTest::testAnalyzeSourceCatalogsTheStatementsOfTheGivenSourcescallsAnalyzerTest::testAnalyzeSourceCombinesNamedFunctionModelsWithLaravelRegistrationscallsAnalyzerTest::testAnalyzeSourceDistinguishesNamespacedFunctionsAndGlobalBuiltinscallsAnalyzerTest::testAnalyzeSourceDoesNotAssumeCustomModelOrderingUsesTheStandardBuildercallsAnalyzerTest::testAnalyzeSourceDoesNotLoseAlternativesWhenMutatingAJoinedBuildercallsAnalyzerTest::testAnalyzeSourceDoesNotLoseArgumentSideEffectsOnTheBuilderReceivercallsAnalyzerTest::testAnalyzeSourceDoesNotLoseUnknownEffectsInsideNestedPredicatescallsAnalyzerTest::testAnalyzeSourceDoesNotMistakeCollectionMethodsForDatabaseExecutionscallsAnalyzerTest::testAnalyzeSourceFollowsBuilderAliasesStoredInArrayscallsAnalyzerTest::testAnalyzeSourceHandlesPlaceholderExpressionscallsAnalyzerTest::testAnalyzeSourceKeepsAModelledSinkWithoutARegisteredCompilerVisiblecallsAnalyzerTest::testAnalyzeSourceKeepsCapturedBuilderEffectsOpencallsAnalyzerTest::testAnalyzeSourceKeepsCapturedScalarBindingsInNestedPredicatescallsAnalyzerTest::testAnalyzeSourceKeepsConditionalMutationsAsAlternativescallsAnalyzerTest::testAnalyzeSourceKeepsDirectBuilderArrayWritesOpencallsAnalyzerTest::testAnalyzeSourceKeepsEachUnmodelledEscapeOfABuilderOpencallsAnalyzerTest::testAnalyzeSourceKeepsEarlyReturningScopesAndBooleanRegroupingOpencallsAnalyzerTest::testAnalyzeSourceKeepsNamedBuilderArgumentsOpencallsAnalyzerTest::testAnalyzeSourceKeepsObjectsPassedInsideArraysToUnknownHelpersOpencallsAnalyzerTest::testAnalyzeSourceKeepsReferenceRebindingOfBuildersOpencallsAnalyzerTest::testAnalyzeSourceKeepsTheBuilderStatementWhenOnlyAValueIsUnresolvedcallsAnalyzerTest::testAnalyzeSourceKeepsVariableNamesForUnresolvedSqlAndFragmentscallsAnalyzerTest::testAnalyzeSourceLetsRegisteredCallModelsOwnTheirArgumentEffectscallsAnalyzerTest::testAnalyzeSourceListsTheCountAndPageStatementsOfPaginatecallsAnalyzerTest::testAnalyzeSourceNormalizesPlaceholderListsThroughVariablescallsAnalyzerTest::testAnalyzeSourcePassesArgumentsToTraditionalLocalScopescallsAnalyzerTest::testAnalyzeSourcePreservesShortCircuitAlternativesOnTrackedBuilderscallsAnalyzerTest::testAnalyzeSourceRecognizesAuthenticationModelsWithoutVendorFilescallsAnalyzerTest::testAnalyzeSourceRecognizesBothRawAndBuilderCallsOnConcreteConnectionscallsAnalyzerTest::testAnalyzeSourceRefreshesTerminalReceiversAfterEvaluatingArgumentscallsAnalyzerTest::testAnalyzeSourceRefusesAnExtensionThatIsNotRegisteredcallsAnalyzerTest::testAnalyzeSourceReportsAFileItCannotParsecallsAnalyzerTest::testAnalyzeSourceReportsBothOutcomesOfAConditionalBuilderCallbackcallsAnalyzerTest::testAnalyzeSourceResolvesAcrossFilescallsAnalyzerTest::testAnalyzeSourceRetainsBindingsReusedByModelledBuilderCallscallsAnalyzerTest::testAnalyzeSourceRetainsTheLimitAfterReusingAFirstQuerycallsAnalyzerTest::testAnalyzeSourceSupportsRegisteredQueryModelsWithoutLaravelcallsAnalyzerTest::testAnElementReadUnderAnUnknownKeyGivesOneStatementPerElementcallsAnalyzerTest::testAnUndeterminedStatementSaysWhyItIsUndeterminedcallsAnalyzerTest::testAResolvedCallerDoesNotDeleteAnUndeterminedOnecallsAnalyzerTest::testAStaticPropertyNothingAssignsReadsLikeAConstantcallsAnalyzerTest::testAValueBoundToOnePlaceholderStaysWithThatPlaceholdercallsAnalyzerTest::testEntriesOfIsCalledWithTheParsedFilescallsAnalyzerTest::testEntriesOfRecognisesOnlyTheExtensionsTheOptionsNamecallsAnalyzerTest::testExtensionsAreTheOnesTheRunCanAskForcallsAnalyzerTest::testFileScopeAliasesCannotHideLaterWritescallsAnalyzerTest::testIssetDoesNotDiscardAnUnknownSqlFragmentcallsAnalyzerTest::testIssetGuardsAConditionallyAssignedSqlFragmentcallsAnalyzerTest::testIssetSqlFragmentsKeepBothSyntacticBranchescallsAnalyzerTest::testMixedExternalAndUnresolvedDependenciesDoNotCloseTheSearchcallsAnalyzerTest::testParseAnswersWithTheProblemThatStoppedItcallsAnalyzerTest::testSortRecordsIsCalledWithTheEntriescallsAnalyzerTest::testSortRecordsOrdersByWhereTheStatementIsIssuedcallsAnalyzerTest::testUncertainWritesRemainOpenInsteadOfBecomingEmptycallsAnalyzerTest::testUnconditionalCandidatesNormalizeOnlyWhenStringifiedcallsAnalyzerTest::testValuesDecidedByNestedBranchesStayTogethercallsAnalyzerTest::testValuesDecidedByOneBranchStayTogethercallsAnalyzerTest::testWithConfigurationOverridesTheBuiltinWithoutChangingTheOriginalAnalyzercalls
Other tests reaching this symbol 22
AnalyzeTarget::__constructcallsAnalyzeTarget::__invokecallsRecoverTarget::__constructcallsRecoverTarget::__invokecallsCatalogCommandTest::testStatusReportsFindingsOnlyWhenAskedTocallsExpressionEvaluatorTest::testEvaluateOperandsReachesAStatementNestedSeveralOperatorsDeepcallsExpressionEvaluatorTest::testEvaluateOperandsReachesAStatementWrittenInsideAnOperandcallsCatalogTestcallsCallModelTest::testEvaluateModelsFactoriesMutationsAndLeavesOtherCallsAlonecallsCallModelTest::testMatchesClassRecognizesConcreteFrameworkTypesOnlyWhenEnabledcallsJsonReporterTest::testEntryToArrayCarriesTheFindingscallsJsonReporterTest::testRenderIsTheSameForTheSameCatalogcallsJsonReporterTest::testRenderWritesTheWholeDocumentExactlycallsJsonReporterTest::testSummaryCountsResolvedAndDynamicStatementscallsJsonReporterTest::testTheDocumentMatchesTheSchemaItDeclarescallsJsonReporterTest::testToArrayCarriesTheVersionAndTheStatementscallsTextReporterTest::testEntryLinesAreWrittenExactlycallsTextReporterTest::testEntryLinesShowTheFindingscallsTextReporterTest::testEntryLinesShowTheStatementAndItsParameterscallsTextReporterTest::testRenderWritesTheWholeReportExactlycallsTextReporterTest::testStatusLineSaysHowFarTheAnalyzerGotcallsTextReporterTest::testSummaryLineCountsWhatWasFoundcalls