driftdetect-core 0.4.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/call-graph/extractors/csharp-data-access-extractor.d.ts +8 -0
- package/dist/call-graph/extractors/csharp-data-access-extractor.d.ts.map +1 -1
- package/dist/call-graph/extractors/csharp-data-access-extractor.js +8 -0
- package/dist/call-graph/extractors/csharp-data-access-extractor.js.map +1 -1
- package/dist/call-graph/extractors/csharp-extractor.d.ts +35 -0
- package/dist/call-graph/extractors/csharp-extractor.d.ts.map +1 -1
- package/dist/call-graph/extractors/csharp-extractor.js +362 -4
- package/dist/call-graph/extractors/csharp-extractor.js.map +1 -1
- package/dist/call-graph/extractors/csharp-hybrid-extractor.d.ts +37 -0
- package/dist/call-graph/extractors/csharp-hybrid-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/csharp-hybrid-extractor.js +408 -0
- package/dist/call-graph/extractors/csharp-hybrid-extractor.js.map +1 -0
- package/dist/call-graph/extractors/hybrid-extractor-base.d.ts +102 -0
- package/dist/call-graph/extractors/hybrid-extractor-base.d.ts.map +1 -0
- package/dist/call-graph/extractors/hybrid-extractor-base.js +289 -0
- package/dist/call-graph/extractors/hybrid-extractor-base.js.map +1 -0
- package/dist/call-graph/extractors/index.d.ts +17 -13
- package/dist/call-graph/extractors/index.d.ts.map +1 -1
- package/dist/call-graph/extractors/index.js +24 -23
- package/dist/call-graph/extractors/index.js.map +1 -1
- package/dist/call-graph/extractors/java-data-access-extractor.d.ts +8 -0
- package/dist/call-graph/extractors/java-data-access-extractor.d.ts.map +1 -1
- package/dist/call-graph/extractors/java-data-access-extractor.js +8 -0
- package/dist/call-graph/extractors/java-data-access-extractor.js.map +1 -1
- package/dist/call-graph/extractors/java-extractor.d.ts +15 -0
- package/dist/call-graph/extractors/java-extractor.d.ts.map +1 -1
- package/dist/call-graph/extractors/java-extractor.js +120 -4
- package/dist/call-graph/extractors/java-extractor.js.map +1 -1
- package/dist/call-graph/extractors/java-hybrid-extractor.d.ts +36 -0
- package/dist/call-graph/extractors/java-hybrid-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/java-hybrid-extractor.js +426 -0
- package/dist/call-graph/extractors/java-hybrid-extractor.js.map +1 -0
- package/dist/call-graph/extractors/php-data-access-extractor.d.ts +8 -0
- package/dist/call-graph/extractors/php-data-access-extractor.d.ts.map +1 -1
- package/dist/call-graph/extractors/php-data-access-extractor.js +8 -0
- package/dist/call-graph/extractors/php-data-access-extractor.js.map +1 -1
- package/dist/call-graph/extractors/php-extractor.d.ts +48 -1
- package/dist/call-graph/extractors/php-extractor.d.ts.map +1 -1
- package/dist/call-graph/extractors/php-extractor.js +460 -6
- package/dist/call-graph/extractors/php-extractor.js.map +1 -1
- package/dist/call-graph/extractors/php-hybrid-extractor.d.ts +35 -0
- package/dist/call-graph/extractors/php-hybrid-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/php-hybrid-extractor.js +393 -0
- package/dist/call-graph/extractors/php-hybrid-extractor.js.map +1 -0
- package/dist/call-graph/extractors/python-data-access-extractor.d.ts +8 -0
- package/dist/call-graph/extractors/python-data-access-extractor.d.ts.map +1 -1
- package/dist/call-graph/extractors/python-data-access-extractor.js +8 -0
- package/dist/call-graph/extractors/python-data-access-extractor.js.map +1 -1
- package/dist/call-graph/extractors/python-hybrid-extractor.d.ts +85 -0
- package/dist/call-graph/extractors/python-hybrid-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/python-hybrid-extractor.js +462 -0
- package/dist/call-graph/extractors/python-hybrid-extractor.js.map +1 -0
- package/dist/call-graph/extractors/regex/base-regex-extractor.d.ts +154 -0
- package/dist/call-graph/extractors/regex/base-regex-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/regex/base-regex-extractor.js +346 -0
- package/dist/call-graph/extractors/regex/base-regex-extractor.js.map +1 -0
- package/dist/call-graph/extractors/regex/csharp-regex.d.ts +34 -0
- package/dist/call-graph/extractors/regex/csharp-regex.d.ts.map +1 -0
- package/dist/call-graph/extractors/regex/csharp-regex.js +393 -0
- package/dist/call-graph/extractors/regex/csharp-regex.js.map +1 -0
- package/dist/call-graph/extractors/regex/index.d.ts +25 -0
- package/dist/call-graph/extractors/regex/index.d.ts.map +1 -0
- package/dist/call-graph/extractors/regex/index.js +66 -0
- package/dist/call-graph/extractors/regex/index.js.map +1 -0
- package/dist/call-graph/extractors/regex/java-regex.d.ts +34 -0
- package/dist/call-graph/extractors/regex/java-regex.d.ts.map +1 -0
- package/dist/call-graph/extractors/regex/java-regex.js +327 -0
- package/dist/call-graph/extractors/regex/java-regex.js.map +1 -0
- package/dist/call-graph/extractors/regex/php-regex.d.ts +30 -0
- package/dist/call-graph/extractors/regex/php-regex.d.ts.map +1 -0
- package/dist/call-graph/extractors/regex/php-regex.js +333 -0
- package/dist/call-graph/extractors/regex/php-regex.js.map +1 -0
- package/dist/call-graph/extractors/regex/python-regex.d.ts +46 -0
- package/dist/call-graph/extractors/regex/python-regex.d.ts.map +1 -0
- package/dist/call-graph/extractors/regex/python-regex.js +380 -0
- package/dist/call-graph/extractors/regex/python-regex.js.map +1 -0
- package/dist/call-graph/extractors/regex/typescript-regex.d.ts +27 -0
- package/dist/call-graph/extractors/regex/typescript-regex.d.ts.map +1 -0
- package/dist/call-graph/extractors/regex/typescript-regex.js +349 -0
- package/dist/call-graph/extractors/regex/typescript-regex.js.map +1 -0
- package/dist/call-graph/extractors/semantic-data-access-scanner.d.ts +7 -0
- package/dist/call-graph/extractors/semantic-data-access-scanner.d.ts.map +1 -1
- package/dist/call-graph/extractors/semantic-data-access-scanner.js +7 -0
- package/dist/call-graph/extractors/semantic-data-access-scanner.js.map +1 -1
- package/dist/call-graph/extractors/types.d.ts +111 -0
- package/dist/call-graph/extractors/types.d.ts.map +1 -0
- package/dist/call-graph/extractors/types.js +68 -0
- package/dist/call-graph/extractors/types.js.map +1 -0
- package/dist/call-graph/extractors/typescript-data-access-extractor.d.ts +8 -0
- package/dist/call-graph/extractors/typescript-data-access-extractor.d.ts.map +1 -1
- package/dist/call-graph/extractors/typescript-data-access-extractor.js +8 -0
- package/dist/call-graph/extractors/typescript-data-access-extractor.js.map +1 -1
- package/dist/call-graph/extractors/typescript-hybrid-extractor.d.ts +116 -0
- package/dist/call-graph/extractors/typescript-hybrid-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/typescript-hybrid-extractor.js +635 -0
- package/dist/call-graph/extractors/typescript-hybrid-extractor.js.map +1 -0
- package/dist/error-handling/error-handling-analyzer.d.ts +73 -0
- package/dist/error-handling/error-handling-analyzer.d.ts.map +1 -0
- package/dist/error-handling/error-handling-analyzer.js +706 -0
- package/dist/error-handling/error-handling-analyzer.js.map +1 -0
- package/dist/error-handling/index.d.ts +8 -0
- package/dist/error-handling/index.d.ts.map +1 -0
- package/dist/error-handling/index.js +8 -0
- package/dist/error-handling/index.js.map +1 -0
- package/dist/error-handling/types.d.ts +307 -0
- package/dist/error-handling/types.d.ts.map +1 -0
- package/dist/error-handling/types.js +7 -0
- package/dist/error-handling/types.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -1
- package/dist/lake/pattern-shard-store.d.ts +6 -0
- package/dist/lake/pattern-shard-store.d.ts.map +1 -1
- package/dist/lake/pattern-shard-store.js +6 -0
- package/dist/lake/pattern-shard-store.js.map +1 -1
- package/dist/module-coupling/coupling-analyzer.d.ts +73 -0
- package/dist/module-coupling/coupling-analyzer.d.ts.map +1 -0
- package/dist/module-coupling/coupling-analyzer.js +668 -0
- package/dist/module-coupling/coupling-analyzer.js.map +1 -0
- package/dist/module-coupling/index.d.ts +9 -0
- package/dist/module-coupling/index.d.ts.map +1 -0
- package/dist/module-coupling/index.js +9 -0
- package/dist/module-coupling/index.js.map +1 -0
- package/dist/module-coupling/types.d.ts +273 -0
- package/dist/module-coupling/types.d.ts.map +1 -0
- package/dist/module-coupling/types.js +8 -0
- package/dist/module-coupling/types.js.map +1 -0
- package/dist/patterns/adapters/index.d.ts +11 -0
- package/dist/patterns/adapters/index.d.ts.map +1 -0
- package/dist/patterns/adapters/index.js +11 -0
- package/dist/patterns/adapters/index.js.map +1 -0
- package/dist/patterns/adapters/pattern-store-adapter.d.ts +59 -0
- package/dist/patterns/adapters/pattern-store-adapter.d.ts.map +1 -0
- package/dist/patterns/adapters/pattern-store-adapter.js +468 -0
- package/dist/patterns/adapters/pattern-store-adapter.js.map +1 -0
- package/dist/patterns/adapters/service-factory.d.ts +40 -0
- package/dist/patterns/adapters/service-factory.d.ts.map +1 -0
- package/dist/patterns/adapters/service-factory.js +144 -0
- package/dist/patterns/adapters/service-factory.js.map +1 -0
- package/dist/patterns/errors.d.ts +32 -0
- package/dist/patterns/errors.d.ts.map +1 -0
- package/dist/patterns/errors.js +45 -0
- package/dist/patterns/errors.js.map +1 -0
- package/dist/patterns/impl/cached-repository.d.ts +79 -0
- package/dist/patterns/impl/cached-repository.d.ts.map +1 -0
- package/dist/patterns/impl/cached-repository.js +296 -0
- package/dist/patterns/impl/cached-repository.js.map +1 -0
- package/dist/patterns/impl/file-repository.d.ts +75 -0
- package/dist/patterns/impl/file-repository.d.ts.map +1 -0
- package/dist/patterns/impl/file-repository.js +507 -0
- package/dist/patterns/impl/file-repository.js.map +1 -0
- package/dist/patterns/impl/index.d.ts +16 -0
- package/dist/patterns/impl/index.d.ts.map +1 -0
- package/dist/patterns/impl/index.js +21 -0
- package/dist/patterns/impl/index.js.map +1 -0
- package/dist/patterns/impl/memory-repository.d.ts +56 -0
- package/dist/patterns/impl/memory-repository.d.ts.map +1 -0
- package/dist/patterns/impl/memory-repository.js +323 -0
- package/dist/patterns/impl/memory-repository.js.map +1 -0
- package/dist/patterns/impl/pattern-service.d.ts +52 -0
- package/dist/patterns/impl/pattern-service.d.ts.map +1 -0
- package/dist/patterns/impl/pattern-service.js +382 -0
- package/dist/patterns/impl/pattern-service.js.map +1 -0
- package/dist/patterns/impl/repository-factory.d.ts +44 -0
- package/dist/patterns/impl/repository-factory.d.ts.map +1 -0
- package/dist/patterns/impl/repository-factory.js +140 -0
- package/dist/patterns/impl/repository-factory.js.map +1 -0
- package/dist/patterns/impl/unified-file-repository.d.ts +111 -0
- package/dist/patterns/impl/unified-file-repository.d.ts.map +1 -0
- package/dist/patterns/impl/unified-file-repository.js +677 -0
- package/dist/patterns/impl/unified-file-repository.js.map +1 -0
- package/dist/patterns/index.d.ts +23 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +41 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/repository.d.ts +241 -0
- package/dist/patterns/repository.d.ts.map +1 -0
- package/dist/patterns/repository.js +23 -0
- package/dist/patterns/repository.js.map +1 -0
- package/dist/patterns/service.d.ts +245 -0
- package/dist/patterns/service.d.ts.map +1 -0
- package/dist/patterns/service.js +25 -0
- package/dist/patterns/service.js.map +1 -0
- package/dist/patterns/types.d.ts +227 -0
- package/dist/patterns/types.d.ts.map +1 -0
- package/dist/patterns/types.js +117 -0
- package/dist/patterns/types.js.map +1 -0
- package/dist/store/pattern-store.d.ts +6 -0
- package/dist/store/pattern-store.d.ts.map +1 -1
- package/dist/store/pattern-store.js +6 -0
- package/dist/store/pattern-store.js.map +1 -1
- package/dist/test-topology/extractors/base-test-extractor.d.ts +89 -0
- package/dist/test-topology/extractors/base-test-extractor.d.ts.map +1 -0
- package/dist/test-topology/extractors/base-test-extractor.js +187 -0
- package/dist/test-topology/extractors/base-test-extractor.js.map +1 -0
- package/dist/test-topology/extractors/csharp-test-extractor.d.ts +23 -0
- package/dist/test-topology/extractors/csharp-test-extractor.d.ts.map +1 -0
- package/dist/test-topology/extractors/csharp-test-extractor.js +367 -0
- package/dist/test-topology/extractors/csharp-test-extractor.js.map +1 -0
- package/dist/test-topology/extractors/index.d.ts +12 -0
- package/dist/test-topology/extractors/index.d.ts.map +1 -0
- package/dist/test-topology/extractors/index.js +12 -0
- package/dist/test-topology/extractors/index.js.map +1 -0
- package/dist/test-topology/extractors/java-test-extractor.d.ts +20 -0
- package/dist/test-topology/extractors/java-test-extractor.d.ts.map +1 -0
- package/dist/test-topology/extractors/java-test-extractor.js +275 -0
- package/dist/test-topology/extractors/java-test-extractor.js.map +1 -0
- package/dist/test-topology/extractors/php-test-extractor.d.ts +24 -0
- package/dist/test-topology/extractors/php-test-extractor.d.ts.map +1 -0
- package/dist/test-topology/extractors/php-test-extractor.js +409 -0
- package/dist/test-topology/extractors/php-test-extractor.js.map +1 -0
- package/dist/test-topology/extractors/python-test-extractor.d.ts +23 -0
- package/dist/test-topology/extractors/python-test-extractor.d.ts.map +1 -0
- package/dist/test-topology/extractors/python-test-extractor.js +342 -0
- package/dist/test-topology/extractors/python-test-extractor.js.map +1 -0
- package/dist/test-topology/extractors/regex/csharp-test-regex.d.ts +51 -0
- package/dist/test-topology/extractors/regex/csharp-test-regex.d.ts.map +1 -0
- package/dist/test-topology/extractors/regex/csharp-test-regex.js +383 -0
- package/dist/test-topology/extractors/regex/csharp-test-regex.js.map +1 -0
- package/dist/test-topology/extractors/regex/index.d.ts +18 -0
- package/dist/test-topology/extractors/regex/index.d.ts.map +1 -0
- package/dist/test-topology/extractors/regex/index.js +43 -0
- package/dist/test-topology/extractors/regex/index.js.map +1 -0
- package/dist/test-topology/extractors/regex/java-test-regex.d.ts +50 -0
- package/dist/test-topology/extractors/regex/java-test-regex.d.ts.map +1 -0
- package/dist/test-topology/extractors/regex/java-test-regex.js +370 -0
- package/dist/test-topology/extractors/regex/java-test-regex.js.map +1 -0
- package/dist/test-topology/extractors/regex/php-test-regex.d.ts +56 -0
- package/dist/test-topology/extractors/regex/php-test-regex.d.ts.map +1 -0
- package/dist/test-topology/extractors/regex/php-test-regex.js +503 -0
- package/dist/test-topology/extractors/regex/php-test-regex.js.map +1 -0
- package/dist/test-topology/extractors/regex/python-test-regex.d.ts +57 -0
- package/dist/test-topology/extractors/regex/python-test-regex.d.ts.map +1 -0
- package/dist/test-topology/extractors/regex/python-test-regex.js +381 -0
- package/dist/test-topology/extractors/regex/python-test-regex.js.map +1 -0
- package/dist/test-topology/extractors/regex/typescript-test-regex.d.ts +60 -0
- package/dist/test-topology/extractors/regex/typescript-test-regex.d.ts.map +1 -0
- package/dist/test-topology/extractors/regex/typescript-test-regex.js +368 -0
- package/dist/test-topology/extractors/regex/typescript-test-regex.js.map +1 -0
- package/dist/test-topology/extractors/typescript-test-extractor.d.ts +24 -0
- package/dist/test-topology/extractors/typescript-test-extractor.d.ts.map +1 -0
- package/dist/test-topology/extractors/typescript-test-extractor.js +266 -0
- package/dist/test-topology/extractors/typescript-test-extractor.js.map +1 -0
- package/dist/test-topology/hybrid-test-topology-analyzer.d.ts +98 -0
- package/dist/test-topology/hybrid-test-topology-analyzer.d.ts.map +1 -0
- package/dist/test-topology/hybrid-test-topology-analyzer.js +555 -0
- package/dist/test-topology/hybrid-test-topology-analyzer.js.map +1 -0
- package/dist/test-topology/index.d.ts +16 -0
- package/dist/test-topology/index.d.ts.map +1 -0
- package/dist/test-topology/index.js +19 -0
- package/dist/test-topology/index.js.map +1 -0
- package/dist/test-topology/test-topology-analyzer.d.ts +85 -0
- package/dist/test-topology/test-topology-analyzer.d.ts.map +1 -0
- package/dist/test-topology/test-topology-analyzer.js +538 -0
- package/dist/test-topology/test-topology-analyzer.js.map +1 -0
- package/dist/test-topology/types.d.ts +300 -0
- package/dist/test-topology/types.d.ts.map +1 -0
- package/dist/test-topology/types.js +7 -0
- package/dist/test-topology/types.js.map +1 -0
- package/dist/wrappers/clustering/clusterer.d.ts +43 -0
- package/dist/wrappers/clustering/clusterer.d.ts.map +1 -0
- package/dist/wrappers/clustering/clusterer.js +374 -0
- package/dist/wrappers/clustering/clusterer.js.map +1 -0
- package/dist/wrappers/clustering/exclusions.d.ts +47 -0
- package/dist/wrappers/clustering/exclusions.d.ts.map +1 -0
- package/dist/wrappers/clustering/exclusions.js +318 -0
- package/dist/wrappers/clustering/exclusions.js.map +1 -0
- package/dist/wrappers/clustering/index.d.ts +6 -0
- package/dist/wrappers/clustering/index.d.ts.map +1 -0
- package/dist/wrappers/clustering/index.js +6 -0
- package/dist/wrappers/clustering/index.js.map +1 -0
- package/dist/wrappers/detection/detector.d.ts +69 -0
- package/dist/wrappers/detection/detector.d.ts.map +1 -0
- package/dist/wrappers/detection/detector.js +279 -0
- package/dist/wrappers/detection/detector.js.map +1 -0
- package/dist/wrappers/detection/index.d.ts +5 -0
- package/dist/wrappers/detection/index.d.ts.map +1 -0
- package/dist/wrappers/detection/index.js +5 -0
- package/dist/wrappers/detection/index.js.map +1 -0
- package/dist/wrappers/export/index.d.ts +5 -0
- package/dist/wrappers/export/index.d.ts.map +1 -0
- package/dist/wrappers/export/index.js +5 -0
- package/dist/wrappers/export/index.js.map +1 -0
- package/dist/wrappers/export/json.d.ts +127 -0
- package/dist/wrappers/export/json.d.ts.map +1 -0
- package/dist/wrappers/export/json.js +160 -0
- package/dist/wrappers/export/json.js.map +1 -0
- package/dist/wrappers/index.d.ts +56 -0
- package/dist/wrappers/index.d.ts.map +1 -0
- package/dist/wrappers/index.js +159 -0
- package/dist/wrappers/index.js.map +1 -0
- package/dist/wrappers/integration/adapter.d.ts +52 -0
- package/dist/wrappers/integration/adapter.d.ts.map +1 -0
- package/dist/wrappers/integration/adapter.js +209 -0
- package/dist/wrappers/integration/adapter.js.map +1 -0
- package/dist/wrappers/integration/index.d.ts +9 -0
- package/dist/wrappers/integration/index.d.ts.map +1 -0
- package/dist/wrappers/integration/index.js +12 -0
- package/dist/wrappers/integration/index.js.map +1 -0
- package/dist/wrappers/integration/pattern-adapter.d.ts +52 -0
- package/dist/wrappers/integration/pattern-adapter.d.ts.map +1 -0
- package/dist/wrappers/integration/pattern-adapter.js +192 -0
- package/dist/wrappers/integration/pattern-adapter.js.map +1 -0
- package/dist/wrappers/integration/scanner.d.ts +85 -0
- package/dist/wrappers/integration/scanner.d.ts.map +1 -0
- package/dist/wrappers/integration/scanner.js +367 -0
- package/dist/wrappers/integration/scanner.js.map +1 -0
- package/dist/wrappers/primitives/discovery.d.ts +57 -0
- package/dist/wrappers/primitives/discovery.d.ts.map +1 -0
- package/dist/wrappers/primitives/discovery.js +389 -0
- package/dist/wrappers/primitives/discovery.js.map +1 -0
- package/dist/wrappers/primitives/index.d.ts +6 -0
- package/dist/wrappers/primitives/index.d.ts.map +1 -0
- package/dist/wrappers/primitives/index.js +6 -0
- package/dist/wrappers/primitives/index.js.map +1 -0
- package/dist/wrappers/primitives/registry.d.ts +63 -0
- package/dist/wrappers/primitives/registry.d.ts.map +1 -0
- package/dist/wrappers/primitives/registry.js +447 -0
- package/dist/wrappers/primitives/registry.js.map +1 -0
- package/dist/wrappers/types.d.ts +137 -0
- package/dist/wrappers/types.d.ts.map +1 -0
- package/dist/wrappers/types.js +7 -0
- package/dist/wrappers/types.js.map +1 -0
- package/package.json +5 -1
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
* - Traits
|
|
12
12
|
* - Namespaces and use statements
|
|
13
13
|
* - Arrow functions (fn =>)
|
|
14
|
-
* - Closures
|
|
14
|
+
* - Closures (anonymous functions)
|
|
15
|
+
* - Nested functions within function bodies
|
|
16
|
+
* - Callback patterns (string callbacks, array callbacks, closures)
|
|
17
|
+
* - Module-level calls (top-level code)
|
|
15
18
|
* - Laravel controller actions
|
|
16
19
|
* - Static method calls (Class::method())
|
|
17
20
|
*/
|
|
@@ -36,6 +39,15 @@ export declare class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
36
39
|
* Visit a tree-sitter node and extract information
|
|
37
40
|
*/
|
|
38
41
|
private visitNode;
|
|
42
|
+
/**
|
|
43
|
+
* Extract calls at module level (not inside any function or class)
|
|
44
|
+
* This catches patterns like: require_once 'file.php'; $app->run();
|
|
45
|
+
*/
|
|
46
|
+
private extractModuleLevelCalls;
|
|
47
|
+
/**
|
|
48
|
+
* Extract a call to an array (for module-level calls)
|
|
49
|
+
*/
|
|
50
|
+
private extractCallToArray;
|
|
39
51
|
/**
|
|
40
52
|
* Extract namespace and visit children with namespace context
|
|
41
53
|
*/
|
|
@@ -56,6 +68,25 @@ export declare class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
56
68
|
* Extract a function definition (not a method)
|
|
57
69
|
*/
|
|
58
70
|
private extractFunctionDefinition;
|
|
71
|
+
/**
|
|
72
|
+
* Extract nested functions from a function body
|
|
73
|
+
* Handles: anonymous functions, arrow functions, and nested named functions
|
|
74
|
+
*/
|
|
75
|
+
private extractNestedFunctions;
|
|
76
|
+
/**
|
|
77
|
+
* Extract an anonymous function (closure)
|
|
78
|
+
* e.g., $fn = function($x) use ($y) { return $x + $y; }
|
|
79
|
+
*/
|
|
80
|
+
private extractAnonymousFunction;
|
|
81
|
+
/**
|
|
82
|
+
* Extract an arrow function
|
|
83
|
+
* e.g., $fn = fn($x) => $x * 2
|
|
84
|
+
*/
|
|
85
|
+
private extractArrowFunction;
|
|
86
|
+
/**
|
|
87
|
+
* Extract calls recursively from an expression (for arrow functions)
|
|
88
|
+
*/
|
|
89
|
+
private extractCallsFromBodyRecursive;
|
|
59
90
|
/**
|
|
60
91
|
* Extract a method declaration
|
|
61
92
|
*/
|
|
@@ -68,6 +99,22 @@ export declare class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
68
99
|
* Extract a function call (global function)
|
|
69
100
|
*/
|
|
70
101
|
private extractFunctionCall;
|
|
102
|
+
/**
|
|
103
|
+
* Extract callback references from function call arguments
|
|
104
|
+
* Handles patterns like:
|
|
105
|
+
* - String callbacks: array_map('strtoupper', $items)
|
|
106
|
+
* - Array callbacks: array_map([$this, 'process'], $items)
|
|
107
|
+
* - First-class callable: array_map($this->process(...), $items)
|
|
108
|
+
*/
|
|
109
|
+
private extractCallbackReferences;
|
|
110
|
+
/**
|
|
111
|
+
* Check if a string value is a common non-callback string
|
|
112
|
+
*/
|
|
113
|
+
private isCommonStringValue;
|
|
114
|
+
/**
|
|
115
|
+
* Extract callback from array syntax: [$object, 'method'] or [ClassName::class, 'method']
|
|
116
|
+
*/
|
|
117
|
+
private extractArrayCallback;
|
|
71
118
|
/**
|
|
72
119
|
* Extract a member call ($obj->method())
|
|
73
120
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"php-extractor.d.ts","sourceRoot":"","sources":["../../../src/call-graph/extractors/php-extractor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"php-extractor.d.ts","sourceRoot":"","sources":["../../../src/call-graph/extractors/php-extractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EAGrB,MAAM,aAAa,CAAC;AAOrB;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,sBAAsB;IAC/D,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAS;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAmE;IAEhG,OAAO,CAAC,MAAM,CAAiC;IAE/C;;OAEG;IACH,MAAM,CAAC,WAAW,IAAI,OAAO;IAI7B;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,oBAAoB;IA8B/D;;OAEG;IACH,OAAO,CAAC,SAAS;IA4DjB;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IA8D/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoI1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAmE/B;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAsDnC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IA2C/B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA6DjC;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IA6C9B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAqDhC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAmD5B;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAoBrC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA8DhC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA2B7B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAoD3B;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;IAuDjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAS3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAsD5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsCzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsCzB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA+C7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA6B5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAyBzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAkBnB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAQzB"}
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
* - Traits
|
|
12
12
|
* - Namespaces and use statements
|
|
13
13
|
* - Arrow functions (fn =>)
|
|
14
|
-
* - Closures
|
|
14
|
+
* - Closures (anonymous functions)
|
|
15
|
+
* - Nested functions within function bodies
|
|
16
|
+
* - Callback patterns (string callbacks, array callbacks, closures)
|
|
17
|
+
* - Module-level calls (top-level code)
|
|
15
18
|
* - Laravel controller actions
|
|
16
19
|
* - Static method calls (Class::method())
|
|
17
20
|
*/
|
|
@@ -47,6 +50,8 @@ export class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
47
50
|
// Extract namespace
|
|
48
51
|
let currentNamespace = null;
|
|
49
52
|
this.visitNode(tree.rootNode, result, source, null, currentNamespace);
|
|
53
|
+
// Extract module-level calls (top-level code not inside any function or class)
|
|
54
|
+
this.extractModuleLevelCalls(tree.rootNode, result, source);
|
|
50
55
|
}
|
|
51
56
|
catch (error) {
|
|
52
57
|
result.errors.push(error instanceof Error ? error.message : 'Unknown parse error');
|
|
@@ -71,7 +76,7 @@ export class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
71
76
|
this.extractTraitDeclaration(node, result, source, currentNamespace);
|
|
72
77
|
break;
|
|
73
78
|
case 'function_definition':
|
|
74
|
-
this.extractFunctionDefinition(node, result, source, currentNamespace);
|
|
79
|
+
this.extractFunctionDefinition(node, result, source, currentNamespace, null);
|
|
75
80
|
break;
|
|
76
81
|
case 'method_declaration':
|
|
77
82
|
this.extractMethodDeclaration(node, result, source, currentClass, currentNamespace);
|
|
@@ -98,6 +103,186 @@ export class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
98
103
|
}
|
|
99
104
|
}
|
|
100
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Extract calls at module level (not inside any function or class)
|
|
108
|
+
* This catches patterns like: require_once 'file.php'; $app->run();
|
|
109
|
+
*/
|
|
110
|
+
extractModuleLevelCalls(rootNode, result, source) {
|
|
111
|
+
const moduleCalls = [];
|
|
112
|
+
const visit = (node) => {
|
|
113
|
+
// Skip function and class definitions
|
|
114
|
+
if (node.type === 'function_definition' ||
|
|
115
|
+
node.type === 'class_declaration' ||
|
|
116
|
+
node.type === 'interface_declaration' ||
|
|
117
|
+
node.type === 'trait_declaration' ||
|
|
118
|
+
node.type === 'namespace_definition') {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Extract calls from expression statements
|
|
122
|
+
if (node.type === 'function_call_expression' ||
|
|
123
|
+
node.type === 'member_call_expression' ||
|
|
124
|
+
node.type === 'scoped_call_expression' ||
|
|
125
|
+
node.type === 'object_creation_expression') {
|
|
126
|
+
this.extractCallToArray(node, moduleCalls, source);
|
|
127
|
+
}
|
|
128
|
+
for (const child of node.children) {
|
|
129
|
+
visit(child);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
// Find the program node (root of PHP file)
|
|
133
|
+
const programNode = rootNode.children.find(c => c.type === 'program') ?? rootNode;
|
|
134
|
+
for (const child of programNode.children) {
|
|
135
|
+
visit(child);
|
|
136
|
+
}
|
|
137
|
+
// Add module-level calls to result
|
|
138
|
+
result.calls.push(...moduleCalls);
|
|
139
|
+
// If there are module-level calls, create a synthetic module function
|
|
140
|
+
if (moduleCalls.length > 0) {
|
|
141
|
+
const moduleFunc = this.createFunction({
|
|
142
|
+
name: '__module__',
|
|
143
|
+
startLine: 1,
|
|
144
|
+
endLine: rootNode.endPosition.row + 1,
|
|
145
|
+
startColumn: 0,
|
|
146
|
+
endColumn: 0,
|
|
147
|
+
parameters: [],
|
|
148
|
+
isMethod: false,
|
|
149
|
+
isStatic: false,
|
|
150
|
+
isExported: false,
|
|
151
|
+
isConstructor: false,
|
|
152
|
+
isAsync: false,
|
|
153
|
+
decorators: [],
|
|
154
|
+
bodyStartLine: 1,
|
|
155
|
+
bodyEndLine: rootNode.endPosition.row + 1,
|
|
156
|
+
});
|
|
157
|
+
result.functions.push(moduleFunc);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Extract a call to an array (for module-level calls)
|
|
162
|
+
*/
|
|
163
|
+
extractCallToArray(node, calls, _source) {
|
|
164
|
+
if (node.type === 'function_call_expression') {
|
|
165
|
+
const funcNode = node.childForFieldName('function');
|
|
166
|
+
if (!funcNode)
|
|
167
|
+
return;
|
|
168
|
+
let calleeName;
|
|
169
|
+
let receiver;
|
|
170
|
+
if (funcNode.type === 'qualified_name') {
|
|
171
|
+
const parts = funcNode.text.split('\\').filter(p => p);
|
|
172
|
+
calleeName = parts.pop() ?? funcNode.text;
|
|
173
|
+
if (parts.length > 0) {
|
|
174
|
+
receiver = parts.join('\\');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
calleeName = funcNode.text;
|
|
179
|
+
}
|
|
180
|
+
const argsNode = node.childForFieldName('arguments');
|
|
181
|
+
let argumentCount = 0;
|
|
182
|
+
if (argsNode) {
|
|
183
|
+
for (const child of argsNode.children) {
|
|
184
|
+
if (child.type === 'argument') {
|
|
185
|
+
argumentCount++;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
calls.push(this.createCall({
|
|
190
|
+
calleeName,
|
|
191
|
+
receiver,
|
|
192
|
+
fullExpression: node.text,
|
|
193
|
+
line: node.startPosition.row + 1,
|
|
194
|
+
column: node.startPosition.column,
|
|
195
|
+
argumentCount,
|
|
196
|
+
isMethodCall: false,
|
|
197
|
+
isConstructorCall: false,
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
else if (node.type === 'member_call_expression') {
|
|
201
|
+
const nameNode = node.childForFieldName('name');
|
|
202
|
+
const objectNode = node.childForFieldName('object');
|
|
203
|
+
if (!nameNode)
|
|
204
|
+
return;
|
|
205
|
+
const argsNode = node.childForFieldName('arguments');
|
|
206
|
+
let argumentCount = 0;
|
|
207
|
+
if (argsNode) {
|
|
208
|
+
for (const child of argsNode.children) {
|
|
209
|
+
if (child.type === 'argument') {
|
|
210
|
+
argumentCount++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
calls.push(this.createCall({
|
|
215
|
+
calleeName: nameNode.text,
|
|
216
|
+
receiver: objectNode?.text,
|
|
217
|
+
fullExpression: node.text,
|
|
218
|
+
line: node.startPosition.row + 1,
|
|
219
|
+
column: node.startPosition.column,
|
|
220
|
+
argumentCount,
|
|
221
|
+
isMethodCall: true,
|
|
222
|
+
isConstructorCall: false,
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
else if (node.type === 'scoped_call_expression') {
|
|
226
|
+
const nameNode = node.childForFieldName('name');
|
|
227
|
+
const scopeNode = node.childForFieldName('scope');
|
|
228
|
+
if (!nameNode)
|
|
229
|
+
return;
|
|
230
|
+
const argsNode = node.childForFieldName('arguments');
|
|
231
|
+
let argumentCount = 0;
|
|
232
|
+
if (argsNode) {
|
|
233
|
+
for (const child of argsNode.children) {
|
|
234
|
+
if (child.type === 'argument') {
|
|
235
|
+
argumentCount++;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
calls.push(this.createCall({
|
|
240
|
+
calleeName: nameNode.text,
|
|
241
|
+
receiver: scopeNode?.text,
|
|
242
|
+
fullExpression: node.text,
|
|
243
|
+
line: node.startPosition.row + 1,
|
|
244
|
+
column: node.startPosition.column,
|
|
245
|
+
argumentCount,
|
|
246
|
+
isMethodCall: true,
|
|
247
|
+
isConstructorCall: false,
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
else if (node.type === 'object_creation_expression') {
|
|
251
|
+
let calleeName;
|
|
252
|
+
let receiver;
|
|
253
|
+
for (const child of node.children) {
|
|
254
|
+
if (child.type === 'name' || child.type === 'qualified_name') {
|
|
255
|
+
const parts = child.text.split('\\').filter(p => p);
|
|
256
|
+
calleeName = parts.pop();
|
|
257
|
+
if (parts.length > 0) {
|
|
258
|
+
receiver = parts.join('\\');
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (!calleeName)
|
|
264
|
+
return;
|
|
265
|
+
const argsNode = node.childForFieldName('arguments');
|
|
266
|
+
let argumentCount = 0;
|
|
267
|
+
if (argsNode) {
|
|
268
|
+
for (const child of argsNode.children) {
|
|
269
|
+
if (child.type === 'argument') {
|
|
270
|
+
argumentCount++;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
calls.push(this.createCall({
|
|
275
|
+
calleeName,
|
|
276
|
+
receiver,
|
|
277
|
+
fullExpression: node.text,
|
|
278
|
+
line: node.startPosition.row + 1,
|
|
279
|
+
column: node.startPosition.column,
|
|
280
|
+
argumentCount,
|
|
281
|
+
isMethodCall: false,
|
|
282
|
+
isConstructorCall: true,
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
101
286
|
/**
|
|
102
287
|
* Extract namespace and visit children with namespace context
|
|
103
288
|
*/
|
|
@@ -261,12 +446,16 @@ export class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
261
446
|
/**
|
|
262
447
|
* Extract a function definition (not a method)
|
|
263
448
|
*/
|
|
264
|
-
extractFunctionDefinition(node, result, source, currentNamespace) {
|
|
449
|
+
extractFunctionDefinition(node, result, source, currentNamespace, parentFunction) {
|
|
265
450
|
const nameNode = node.childForFieldName('name');
|
|
266
451
|
if (!nameNode)
|
|
267
452
|
return;
|
|
268
453
|
const name = nameNode.text;
|
|
269
|
-
|
|
454
|
+
let qualifiedName = currentNamespace ? `${currentNamespace}\\${name}` : name;
|
|
455
|
+
// Build qualified name including parent function for nested functions
|
|
456
|
+
if (parentFunction) {
|
|
457
|
+
qualifiedName = `${parentFunction}.${name}`;
|
|
458
|
+
}
|
|
270
459
|
// Get return type
|
|
271
460
|
const returnTypeNode = node.childForFieldName('return_type');
|
|
272
461
|
const returnType = returnTypeNode?.text;
|
|
@@ -288,7 +477,7 @@ export class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
288
477
|
returnType,
|
|
289
478
|
isMethod: false,
|
|
290
479
|
isStatic: false,
|
|
291
|
-
isExported: true, //
|
|
480
|
+
isExported: parentFunction ? false : true, // Nested functions are not exported
|
|
292
481
|
isConstructor: false,
|
|
293
482
|
isAsync: false,
|
|
294
483
|
decorators,
|
|
@@ -299,6 +488,148 @@ export class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
299
488
|
// Extract calls from function body
|
|
300
489
|
if (bodyNode) {
|
|
301
490
|
this.extractCallsFromBody(bodyNode, result, source);
|
|
491
|
+
// Extract nested functions (closures, arrow functions, named functions)
|
|
492
|
+
this.extractNestedFunctions(bodyNode, result, source, currentNamespace, qualifiedName);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Extract nested functions from a function body
|
|
497
|
+
* Handles: anonymous functions, arrow functions, and nested named functions
|
|
498
|
+
*/
|
|
499
|
+
extractNestedFunctions(bodyNode, result, source, currentNamespace, parentFunctionName) {
|
|
500
|
+
let closureCounter = 0;
|
|
501
|
+
const visit = (node) => {
|
|
502
|
+
// Named function inside another function (rare in PHP but possible)
|
|
503
|
+
if (node.type === 'function_definition') {
|
|
504
|
+
this.extractFunctionDefinition(node, result, source, currentNamespace, parentFunctionName);
|
|
505
|
+
return; // Don't recurse into this function's children
|
|
506
|
+
}
|
|
507
|
+
// Anonymous function: $fn = function($x) { ... }
|
|
508
|
+
if (node.type === 'anonymous_function_creation_expression') {
|
|
509
|
+
closureCounter++;
|
|
510
|
+
this.extractAnonymousFunction(node, result, source, parentFunctionName, closureCounter);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
// Arrow function: $fn = fn($x) => $x * 2
|
|
514
|
+
if (node.type === 'arrow_function') {
|
|
515
|
+
closureCounter++;
|
|
516
|
+
this.extractArrowFunction(node, result, source, parentFunctionName, closureCounter);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
// Don't recurse into class declarations
|
|
520
|
+
if (node.type === 'class_declaration') {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
for (const child of node.children) {
|
|
524
|
+
visit(child);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
for (const child of bodyNode.children) {
|
|
528
|
+
visit(child);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Extract an anonymous function (closure)
|
|
533
|
+
* e.g., $fn = function($x) use ($y) { return $x + $y; }
|
|
534
|
+
*/
|
|
535
|
+
extractAnonymousFunction(node, result, source, parentFunctionName, counter) {
|
|
536
|
+
const syntheticName = `closure${counter}`;
|
|
537
|
+
const qualifiedName = `${parentFunctionName}.${syntheticName}`;
|
|
538
|
+
// Get parameters
|
|
539
|
+
const parametersNode = node.childForFieldName('parameters');
|
|
540
|
+
const parameters = parametersNode ? this.extractParameters(parametersNode) : [];
|
|
541
|
+
// Get return type
|
|
542
|
+
const returnTypeNode = node.childForFieldName('return_type');
|
|
543
|
+
const returnType = returnTypeNode?.text;
|
|
544
|
+
// Check for static modifier
|
|
545
|
+
const isStatic = node.children.some(c => c.type === 'static_modifier');
|
|
546
|
+
// Get body
|
|
547
|
+
const bodyNode = node.childForFieldName('body');
|
|
548
|
+
const func = this.createFunction({
|
|
549
|
+
name: syntheticName,
|
|
550
|
+
qualifiedName,
|
|
551
|
+
startLine: node.startPosition.row + 1,
|
|
552
|
+
endLine: node.endPosition.row + 1,
|
|
553
|
+
startColumn: node.startPosition.column,
|
|
554
|
+
endColumn: node.endPosition.column,
|
|
555
|
+
parameters,
|
|
556
|
+
returnType,
|
|
557
|
+
isMethod: false,
|
|
558
|
+
isStatic,
|
|
559
|
+
isExported: false,
|
|
560
|
+
isConstructor: false,
|
|
561
|
+
isAsync: false,
|
|
562
|
+
decorators: [],
|
|
563
|
+
bodyStartLine: bodyNode ? bodyNode.startPosition.row + 1 : node.startPosition.row + 1,
|
|
564
|
+
bodyEndLine: bodyNode ? bodyNode.endPosition.row + 1 : node.endPosition.row + 1,
|
|
565
|
+
});
|
|
566
|
+
result.functions.push(func);
|
|
567
|
+
// Extract calls from closure body
|
|
568
|
+
if (bodyNode) {
|
|
569
|
+
this.extractCallsFromBody(bodyNode, result, source);
|
|
570
|
+
// Recursively extract nested functions
|
|
571
|
+
this.extractNestedFunctions(bodyNode, result, source, null, qualifiedName);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Extract an arrow function
|
|
576
|
+
* e.g., $fn = fn($x) => $x * 2
|
|
577
|
+
*/
|
|
578
|
+
extractArrowFunction(node, result, source, parentFunctionName, counter) {
|
|
579
|
+
const syntheticName = `arrow${counter}`;
|
|
580
|
+
const qualifiedName = `${parentFunctionName}.${syntheticName}`;
|
|
581
|
+
// Get parameters
|
|
582
|
+
const parametersNode = node.childForFieldName('parameters');
|
|
583
|
+
const parameters = parametersNode ? this.extractParameters(parametersNode) : [];
|
|
584
|
+
// Get return type
|
|
585
|
+
const returnTypeNode = node.childForFieldName('return_type');
|
|
586
|
+
const returnType = returnTypeNode?.text;
|
|
587
|
+
// Check for static modifier
|
|
588
|
+
const isStatic = node.children.some(c => c.type === 'static_modifier');
|
|
589
|
+
// Get body (expression for arrow functions)
|
|
590
|
+
const bodyNode = node.childForFieldName('body');
|
|
591
|
+
const func = this.createFunction({
|
|
592
|
+
name: syntheticName,
|
|
593
|
+
qualifiedName,
|
|
594
|
+
startLine: node.startPosition.row + 1,
|
|
595
|
+
endLine: node.endPosition.row + 1,
|
|
596
|
+
startColumn: node.startPosition.column,
|
|
597
|
+
endColumn: node.endPosition.column,
|
|
598
|
+
parameters,
|
|
599
|
+
returnType,
|
|
600
|
+
isMethod: false,
|
|
601
|
+
isStatic,
|
|
602
|
+
isExported: false,
|
|
603
|
+
isConstructor: false,
|
|
604
|
+
isAsync: false,
|
|
605
|
+
decorators: [],
|
|
606
|
+
bodyStartLine: bodyNode ? bodyNode.startPosition.row + 1 : node.startPosition.row + 1,
|
|
607
|
+
bodyEndLine: bodyNode ? bodyNode.endPosition.row + 1 : node.endPosition.row + 1,
|
|
608
|
+
});
|
|
609
|
+
result.functions.push(func);
|
|
610
|
+
// Extract calls from arrow function body
|
|
611
|
+
if (bodyNode) {
|
|
612
|
+
this.extractCallsFromBodyRecursive(bodyNode, result, source);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Extract calls recursively from an expression (for arrow functions)
|
|
617
|
+
*/
|
|
618
|
+
extractCallsFromBodyRecursive(node, result, source) {
|
|
619
|
+
if (node.type === 'function_call_expression') {
|
|
620
|
+
this.extractFunctionCall(node, result, source);
|
|
621
|
+
}
|
|
622
|
+
else if (node.type === 'member_call_expression') {
|
|
623
|
+
this.extractMemberCall(node, result, source);
|
|
624
|
+
}
|
|
625
|
+
else if (node.type === 'scoped_call_expression') {
|
|
626
|
+
this.extractScopedCall(node, result, source);
|
|
627
|
+
}
|
|
628
|
+
else if (node.type === 'object_creation_expression') {
|
|
629
|
+
this.extractObjectCreation(node, result, source);
|
|
630
|
+
}
|
|
631
|
+
for (const child of node.children) {
|
|
632
|
+
this.extractCallsFromBodyRecursive(child, result, source);
|
|
302
633
|
}
|
|
303
634
|
}
|
|
304
635
|
/**
|
|
@@ -323,9 +654,10 @@ export class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
323
654
|
const decorators = this.extractAttributes(node);
|
|
324
655
|
// Get body
|
|
325
656
|
const bodyNode = node.childForFieldName('body');
|
|
657
|
+
const qualifiedName = currentClass ? `${currentClass}.${isConstructor ? 'constructor' : name}` : name;
|
|
326
658
|
const func = this.createFunction({
|
|
327
659
|
name: isConstructor ? 'constructor' : (isDestructor ? 'destructor' : name),
|
|
328
|
-
qualifiedName
|
|
660
|
+
qualifiedName,
|
|
329
661
|
startLine: node.startPosition.row + 1,
|
|
330
662
|
endLine: node.endPosition.row + 1,
|
|
331
663
|
startColumn: node.startPosition.column,
|
|
@@ -346,6 +678,8 @@ export class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
346
678
|
// Extract calls from method body
|
|
347
679
|
if (bodyNode) {
|
|
348
680
|
this.extractCallsFromBody(bodyNode, result, source);
|
|
681
|
+
// Extract nested functions (closures, arrow functions)
|
|
682
|
+
this.extractNestedFunctions(bodyNode, result, source, null, qualifiedName);
|
|
349
683
|
}
|
|
350
684
|
}
|
|
351
685
|
/**
|
|
@@ -414,6 +748,126 @@ export class PhpCallGraphExtractor extends BaseCallGraphExtractor {
|
|
|
414
748
|
isConstructorCall: false,
|
|
415
749
|
});
|
|
416
750
|
result.calls.push(call);
|
|
751
|
+
// Extract callback references from arguments
|
|
752
|
+
this.extractCallbackReferences(node, result);
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Extract callback references from function call arguments
|
|
756
|
+
* Handles patterns like:
|
|
757
|
+
* - String callbacks: array_map('strtoupper', $items)
|
|
758
|
+
* - Array callbacks: array_map([$this, 'process'], $items)
|
|
759
|
+
* - First-class callable: array_map($this->process(...), $items)
|
|
760
|
+
*/
|
|
761
|
+
extractCallbackReferences(node, result) {
|
|
762
|
+
const argsNode = node.childForFieldName('arguments');
|
|
763
|
+
if (!argsNode)
|
|
764
|
+
return;
|
|
765
|
+
for (const arg of argsNode.children) {
|
|
766
|
+
if (arg.type !== 'argument')
|
|
767
|
+
continue;
|
|
768
|
+
const argExpr = arg.namedChildren[0];
|
|
769
|
+
if (!argExpr)
|
|
770
|
+
continue;
|
|
771
|
+
// String callback: 'functionName' or "functionName"
|
|
772
|
+
if (argExpr.type === 'string' || argExpr.type === 'encapsed_string') {
|
|
773
|
+
const funcName = argExpr.text.replace(/^['"]|['"]$/g, '');
|
|
774
|
+
// Only treat as callback if it looks like a valid function name
|
|
775
|
+
if (/^[a-z_][a-z0-9_]*$/i.test(funcName) && !this.isCommonStringValue(funcName)) {
|
|
776
|
+
result.calls.push(this.createCall({
|
|
777
|
+
calleeName: funcName,
|
|
778
|
+
fullExpression: funcName,
|
|
779
|
+
line: argExpr.startPosition.row + 1,
|
|
780
|
+
column: argExpr.startPosition.column,
|
|
781
|
+
argumentCount: 0,
|
|
782
|
+
isMethodCall: false,
|
|
783
|
+
isConstructorCall: false,
|
|
784
|
+
}));
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
// Array callback: [$object, 'methodName'] or [ClassName::class, 'methodName']
|
|
788
|
+
if (argExpr.type === 'array_creation_expression') {
|
|
789
|
+
this.extractArrayCallback(argExpr, result);
|
|
790
|
+
}
|
|
791
|
+
// Variable that might be a callable: $callback
|
|
792
|
+
if (argExpr.type === 'variable_name') {
|
|
793
|
+
const varName = argExpr.text.replace(/^\$/, '');
|
|
794
|
+
// Common callback variable names
|
|
795
|
+
const callbackVarNames = ['callback', 'handler', 'fn', 'func', 'callable', 'closure'];
|
|
796
|
+
if (callbackVarNames.some(n => varName.toLowerCase().includes(n))) {
|
|
797
|
+
result.calls.push(this.createCall({
|
|
798
|
+
calleeName: varName,
|
|
799
|
+
fullExpression: argExpr.text,
|
|
800
|
+
line: argExpr.startPosition.row + 1,
|
|
801
|
+
column: argExpr.startPosition.column,
|
|
802
|
+
argumentCount: 0,
|
|
803
|
+
isMethodCall: false,
|
|
804
|
+
isConstructorCall: false,
|
|
805
|
+
}));
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Check if a string value is a common non-callback string
|
|
812
|
+
*/
|
|
813
|
+
isCommonStringValue(value) {
|
|
814
|
+
const commonValues = [
|
|
815
|
+
'true', 'false', 'null', 'yes', 'no', 'on', 'off',
|
|
816
|
+
'asc', 'desc', 'id', 'name', 'email', 'password',
|
|
817
|
+
'get', 'post', 'put', 'delete', 'patch',
|
|
818
|
+
];
|
|
819
|
+
return commonValues.includes(value.toLowerCase());
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Extract callback from array syntax: [$object, 'method'] or [ClassName::class, 'method']
|
|
823
|
+
*/
|
|
824
|
+
extractArrayCallback(node, result) {
|
|
825
|
+
const elements = [];
|
|
826
|
+
for (const child of node.children) {
|
|
827
|
+
if (child.type === 'array_element_initializer') {
|
|
828
|
+
const valueNode = child.namedChildren[0];
|
|
829
|
+
if (valueNode) {
|
|
830
|
+
elements.push(valueNode);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
// Need exactly 2 elements: [object/class, method]
|
|
835
|
+
if (elements.length !== 2)
|
|
836
|
+
return;
|
|
837
|
+
const [first, second] = elements;
|
|
838
|
+
if (!first || !second)
|
|
839
|
+
return;
|
|
840
|
+
// Second element should be a string (method name)
|
|
841
|
+
if (second.type !== 'string' && second.type !== 'encapsed_string')
|
|
842
|
+
return;
|
|
843
|
+
const methodName = second.text.replace(/^['"]|['"]$/g, '');
|
|
844
|
+
let receiver;
|
|
845
|
+
// First element: $this, $object, ClassName::class, or 'ClassName'
|
|
846
|
+
if (first.type === 'variable_name') {
|
|
847
|
+
receiver = first.text;
|
|
848
|
+
}
|
|
849
|
+
else if (first.type === 'class_constant_access_expression') {
|
|
850
|
+
// ClassName::class
|
|
851
|
+
const classNode = first.childForFieldName('class');
|
|
852
|
+
if (classNode) {
|
|
853
|
+
receiver = classNode.text;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
else if (first.type === 'string' || first.type === 'encapsed_string') {
|
|
857
|
+
receiver = first.text.replace(/^['"]|['"]$/g, '');
|
|
858
|
+
}
|
|
859
|
+
if (methodName) {
|
|
860
|
+
result.calls.push(this.createCall({
|
|
861
|
+
calleeName: methodName,
|
|
862
|
+
receiver,
|
|
863
|
+
fullExpression: node.text,
|
|
864
|
+
line: node.startPosition.row + 1,
|
|
865
|
+
column: node.startPosition.column,
|
|
866
|
+
argumentCount: 0,
|
|
867
|
+
isMethodCall: true,
|
|
868
|
+
isConstructorCall: false,
|
|
869
|
+
}));
|
|
870
|
+
}
|
|
417
871
|
}
|
|
418
872
|
/**
|
|
419
873
|
* Extract a member call ($obj->method())
|