packages/sql-catalog/tests/Unit/Reporter/Html/Page/StatementIndexPageTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Reporter\Html\Page;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlCatalog\Core\Catalog\AnalysisProblem;
11use SqlCatalog\Core\Catalog\CallSite;
12use SqlCatalog\Core\Catalog\Catalog;
13use SqlCatalog\Core\Catalog\CatalogEntry;
14use SqlCatalog\Core\Catalog\Finding;
15use SqlCatalog\Core\Catalog\FindingRule;
16use SqlCatalog\Core\Catalog\Placeholder;
17use SqlCatalog\Core\Catalog\Resolution;
18use SqlCatalog\Core\Catalog\Severity;
19use SqlCatalog\Core\Catalog\StatementPart;
20use SqlCatalog\Core\Catalog\ValueDomain;
21use SqlCatalog\Core\Sql\StatementKind;
22use SqlCatalog\Core\Text\LiteralText;
23use SqlCatalog\Core\Text\Origin;
24use SqlCatalog\Core\Text\TextHole;
25use SqlCatalog\Core\Text\TextPattern;
26use SqlCatalog\Core\Type\TypeShape;
27use SqlCatalog\Reporter\Html\CatalogIndex;
28use SqlCatalog\Reporter\Html\CatalogStatistics;
29use SqlCatalog\Reporter\Html\HtmlText;
30use SqlCatalog\Reporter\Html\Page\StatementIndexPage;
31use SqlCatalog\Reporter\Html\Palette;
32use SqlCatalog\Reporter\Html\ReportSite;
33use SqlCatalog\Reporter\Html\Scope;
34use SqlCatalog\Reporter\Html\SqlFormatter;
35use SqlCatalog\Reporter\Html\SqlHighlighter;
36use SqlCatalog\Reporter\Html\StatementList;
37use SqlCatalog\Reporter\Html\StatementRow;
38use SqlCatalog\Reporter\Html\TableName;
39
40#[CoversClass(StatementIndexPage::class)]
41#[UsesClass(CallSite::class)]
42#[UsesClass(Catalog::class)]
43#[UsesClass(CatalogEntry::class)]
44#[UsesClass(CatalogIndex::class)]
45#[UsesClass(CatalogStatistics::class)]
46#[UsesClass(HtmlText::class)]
47#[UsesClass(Palette::class)]
48#[UsesClass(ReportSite::class)]
49#[UsesClass(Resolution::class)]
50#[UsesClass(Scope::class)]
51#[UsesClass(Severity::class)]
52#[UsesClass(SqlHighlighter::class)]
53#[UsesClass(StatementKind::class)]
54#[UsesClass(StatementList::class)]
55#[UsesClass(StatementPart::class)]
56#[UsesClass(StatementRow::class)]
57#[UsesClass(LiteralText::class)]
58#[UsesClass(TextPattern::class)]
59#[UsesClass(AnalysisProblem::class)]
60#[UsesClass(Finding::class)]
61#[UsesClass(FindingRule::class)]
62#[UsesClass(Placeholder::class)]
63#[UsesClass(ValueDomain::class)]
64#[UsesClass(SqlFormatter::class)]
65#[UsesClass(TableName::class)]
66#[UsesClass(Origin::class)]
67#[UsesClass(TextHole::class)]
68#[UsesClass(TypeShape::class)]
69final class StatementIndexPageTest extends TestCase
70{
71    public function testRenderListsEveryStatementUnderTheFacetsThatNarrowIt(): void
72    {
73        $catalog = new Catalog([
74            new CatalogEntry('a1', StatementKind::Select, TextPattern::fromText('SELECT 1'), [], [], new CallSite('a.php', 1, 'f', 'pdo.query'), []),
75            new CatalogEntry('a2', StatementKind::Insert, TextPattern::fromText('INSERT'), [], [], new CallSite('a.php', 2, 'f', 'pdo.query'), []),
76        ]);
77        $page = (new StatementIndexPage())->render(new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter()));
78
79        self::assertStringContainsString('<h1>Statements<span class="count">2 statements</span></h1>', $page);
80        self::assertStringContainsString('<div data-narrowable><div class="facets" role="group" aria-label="Narrow the listing">', $page);
81        self::assertStringContainsString('<div class="active-filters" hidden></div><ol class="rows">', $page);
82        self::assertStringContainsString('href="statements/a2.html"', $page);
83    }
84
85    public function testRenderIsWrittenExactly(): void
86    {
87        $entries = [
88            new CatalogEntry('a1', StatementKind::Select, TextPattern::fromSegments([
89                new LiteralText('SELECT id FROM posts WHERE slug = '),
90                new TextHole(Origin::External, TypeShape::unknown(), '$_GET["s"]'),
91            ]), ['posts'], [new Placeholder('?', 0, null, new ValueDomain('int', [7], true, []))], new CallSite('src/a.php', 4, 'App\\R::find', 'pdo.query'), [
92                Finding::of(FindingRule::ExternalInput, 'spliced'),
93            ], false, ['App\\R::run', 'App\\R::find']),
94            new CatalogEntry('a2', StatementKind::Insert, TextPattern::fromText('INSERT INTO posts (id) VALUES (1)'), ['posts'], [], new CallSite('src/a.php', 9, 'App\\R::add', 'pdo.query'), []),
95            new CatalogEntry('a3', StatementKind::Update, TextPattern::fromText('UPDATE posts SET title = ? WHERE id = ?'), ['posts'], [], new CallSite('src/a.php', 14, 'App\\R::add', 'pdo.prepare'), [
96                Finding::of(FindingRule::PlaceholderCountMismatch, 'one bound'),
97            ]),
98            new CatalogEntry('b1', StatementKind::Delete, TextPattern::fromText('DELETE FROM users WHERE id = 1'), ['users'], [], new CallSite('src/b.php', 3, 'App\\Admin\\U::drop', 'pdo.query'), []),
99            new CatalogEntry('b2', StatementKind::Alter, TextPattern::fromText('ALTER TABLE users ADD x INT'), ['users'], [], new CallSite('src/b.php', 8, 'App\\Admin\\U::migrate', 'pdo.exec'), []),
100            new CatalogEntry('c1', StatementKind::Select, TextPattern::fromHole(new TextHole(Origin::Budget, TypeShape::unknown())), [], [], new CallSite('lib/c.php', 2, 'helper', 'mysqli.query'), [
101                Finding::of(FindingRule::AnalysisIncomplete, 'stopped'),
102            ], true, [], true),
103            new CatalogEntry('c2', StatementKind::Unknown, TextPattern::fromHole(new TextHole(Origin::Unreached, TypeShape::unknown(), '$db->query($sql)')), [], [], new CallSite('lib/c.php', 6, '{main}', 'unmatched'), [
104                Finding::of(FindingRule::CallNotAnalyzed, 'unseen'),
105            ]),
106            new CatalogEntry('d1', StatementKind::Select, TextPattern::fromText('SELECT * FROM posts p JOIN users u ON u.id = p.author'), ['posts', 'users'], [], new CallSite('src/d.php', 1, 'App\\R::find', 'pdo.query'), [
107                Finding::of(FindingRule::DynamicSql, 'dyn'),
108            ]),
109            new CatalogEntry('e1', StatementKind::Show, TextPattern::fromText('SHOW TABLES'), [], [], new CallSite('src/e.php', 1, 'App\\R::find', 'pdo.query'), []),
110            new CatalogEntry('f1', StatementKind::Select, TextPattern::fromText('SELECT 1 FROM t1'), ['t1'], [], new CallSite('src/f.php', 1, 'App\\F::a', 'pdo.query'), []),
111            new CatalogEntry('f2', StatementKind::Select, TextPattern::fromText('SELECT 2 FROM t2'), ['t2'], [], new CallSite('src/f.php', 2, 'App\\F::b', 'pdo.query'), []),
112            new CatalogEntry('f3', StatementKind::Select, TextPattern::fromText('SELECT 3 FROM t3'), ['t3'], [], new CallSite('src/f.php', 3, 'App\\G::a', 'pdo.query'), []),
113            new CatalogEntry('f4', StatementKind::Select, TextPattern::fromText('SELECT 4 FROM t4'), ['t4'], [], new CallSite('src/f.php', 4, 'App\\H::a', 'pdo.query'), []),
114            new CatalogEntry('f5', StatementKind::Select, TextPattern::fromText('SELECT 5 FROM t5'), ['t5'], [], new CallSite('src/g.php', 1, 'App\\I::a', 'pdo.query'), []),
115            new CatalogEntry('f6', StatementKind::Select, TextPattern::fromText('SELECT 6 FROM t6'), ['t6'], [], new CallSite('src/h.php', 1, 'App\\J::a', 'pdo.query'), []),
116            new CatalogEntry('f7', StatementKind::Select, TextPattern::fromText('SELECT 7 FROM t7'), ['t7'], [], new CallSite('src/i.php', 1, 'App\\K::a', 'pdo.query'), [
117                Finding::of(FindingRule::DynamicSql, 'dyn'),
118            ]),
119            new CatalogEntry('f8', StatementKind::Select, TextPattern::fromText('SELECT 8 FROM t8'), ['t8'], [], new CallSite('src/j.php', 1, 'App\\L::a', 'pdo.query'), [
120                Finding::of(FindingRule::DynamicSql, 'dyn'),
121            ]),
122            new CatalogEntry('f9', StatementKind::Select, TextPattern::fromText('SELECT 9 FROM t9'), ['t9'], [], new CallSite('src/k.php', 1, 'App\\M::a', 'pdo.query'), [
123                Finding::of(FindingRule::DynamicSql, 'dyn'),
124            ]),
125        ];
126        $catalog = new Catalog($entries, [new AnalysisProblem('src/broken.php', 'broken')]);
127        $site = new ReportSite($catalog, formatter: \SqlCatalog\Facade\Builtins::sqlFormatter());
128
129        self::assertSame(
130            '<h1>Statements<span class="count">18 statements</span></h1><p class="lede">Every statement the source can issue, in the order it is written.'
131                . ' Narrow the listing by what a statement does, how far the analysis got with it, or any text in it.</p><div data-narrowable><div class="facet'
132                . 's" role="group" aria-label="Narrow the listing"><input type="search" name="narrow" class="input facet-search" placeholder="Narrow by text…'
133                . '" aria-label="Narrow by text" autocomplete="off" spellcheck="false"><span class="facet-group"><button type="button" class="chip facet tone-b'
134                . 'lue" data-facet="kind" data-value="select" aria-pressed="false">SELECT<span class="facet-count">12</span></button><button type="button" clas'
135                . 's="chip facet tone-slate" data-facet="kind" data-value="unknown" aria-pressed="false">UNKNOWN<span class="facet-count">1</span></button><but'
136                . 'ton type="button" class="chip facet tone-teal" data-facet="kind" data-value="insert" aria-pressed="false">INSERT<span class="facet-count">1<'
137                . '/span></button><button type="button" class="chip facet tone-violet" data-facet="kind" data-value="update" aria-pressed="false">UPDATE<span c'
138                . 'lass="facet-count">1</span></button><button type="button" class="chip facet tone-pink" data-facet="kind" data-value="delete" aria-pressed="f'
139                . 'alse">DELETE<span class="facet-count">1</span></button><button type="button" class="chip facet tone-indigo" data-facet="kind" data-value="al'
140                . 'ter" aria-pressed="false">ALTER<span class="facet-count">1</span></button><button type="button" class="chip facet tone-slate" data-facet="ki'
141                . 'nd" data-value="show" aria-pressed="false">SHOW<span class="facet-count">1</span></button></span><span class="facet-group"><button type="but'
142                . 'ton" class="chip facet tone-ok" data-facet="resolution" data-value="resolved" aria-pressed="false">resolved<span class="facet-count">15</spa'
143                . 'n></button><button type="button" class="chip facet chip-ghost" data-facet="resolution" data-value="incomplete" aria-pressed="false">incomple'
144                . 'te<span class="facet-count">1</span></button><button type="button" class="chip facet tone-neutral" data-facet="resolution" data-value="not-a'
145                . 'nalyzed" aria-pressed="false">not-analyzed<span class="facet-count">1</span></button><button type="button" class="chip facet tone-danger" da'
146                . 'ta-facet="resolution" data-value="external-input" aria-pressed="false">external-input<span class="facet-count">1</span></button></span><span'
147                . ' class="facet-group"><button type="button" class="chip facet tone-warn" data-facet="severity" data-value="medium" aria-pressed="false">mediu'
148                . 'm<span class="facet-count">5</span></button><button type="button" class="chip facet tone-neutral" data-facet="severity" data-value="low" ari'
149                . 'a-pressed="false">low<span class="facet-count">2</span></button><button type="button" class="chip facet tone-danger" data-facet="severity" d'
150                . 'ata-value="high" aria-pressed="false">high<span class="facet-count">1</span></button></span><span class="facet-shown" aria-live="polite"></s'
151                . 'pan><button type="button" class="btn facet-clear" hidden>Clear</button></div><div class="active-filters" hidden></div><ol class="rows"><li c'
152                . 'lass="row" data-kind="select" data-resolution="incomplete" data-severity="low" data-rule="analysis-incomplete" data-sink="mysqli.query" data'
153                . '-open="open" data-table="" data-namespace="" data-class="" data-function="helper" data-file="lib/c.php"><a class="row-main" href="statements'
154                . '/c1.html"><span class="chip tone-blue">SELECT</span><pre class="code"><span class="hole tone-warn" title="This is a gap: a dependency the an'
155                . 'alyzer stopped following fills it.">{$}</span></pre></a><p class="row-meta"><a href="files/lib-c-php.html">lib/c.php:2</a><a href="files/lib'
156                . '-c-php.html#fn-helper">helper</a><span class="chip chip-ghost" title="A cycle or an analysis budget stopped the search before it closed.">in'
157                . 'complete</span></p></li><li class="row" data-kind="unknown" data-resolution="not-analyzed" data-severity="low" data-rule="call-not-analyzed"'
158                . ' data-sink="unmatched" data-open="open" data-table="" data-namespace="" data-class="" data-function="{main}" data-file="lib/c.php"><a class='
159                . '"row-main" href="statements/c2.html"><span class="chip tone-slate">UNKNOWN</span><pre class="code"><span class="tok-com">no statement was re'
160                . 'ad from this call</span> $db-&gt;query($sql)</pre></a><p class="row-meta"><a href="files/lib-c-php.html">lib/c.php:6</a><span class="chip to'
161                . 'ne-neutral" title="The call was found but never examined, so nothing was read from it.">not-analyzed</span></p></li><li class="row" data-kin'
162                . 'd="select" data-resolution="external-input" data-severity="high" data-rule="external-input" data-sink="pdo.query" data-open="" data-table="p'
163                . 'osts" data-namespace="App" data-class="App\\R" data-function="App\\R::find" data-file="src/a.php"><a class="row-main" href="statements/a1.html'
164                . '"><span class="chip tone-blue">SELECT</span><pre class="code"><span class="tok-kw">SELECT</span>
165    id
166<span class="tok-kw">FROM</span>
167   '
168                . ' posts
169<span class="tok-kw">WHERE</span>
170    slug = <span class="hole tone-danger" title="This is a gap: external input fills it. Written as'
171                . ' $_GET[&quot;s&quot;].">{$}</span></pre></a><p class="row-meta"><a href="files/src-a-php.html">src/a.php:4</a><a href="classes/app-r.html#fn'
172                . '-app-r-find">R::find</a><a class="chip chip-ghost" href="tables/posts.html">posts</a><span class="chip tone-danger" title="The values were f'
173                . 'ollowed to runtime input, so the text cannot be fixed.">external-input</span><span class="chip tone-danger" title="The most serious finding '
174                . 'on this statement">high</span></p></li><li class="row" data-kind="insert" data-resolution="resolved" data-severity="" data-rule="" data-sink'
175                . '="pdo.query" data-open="" data-table="posts" data-namespace="App" data-class="App\\R" data-function="App\\R::add" data-file="src/a.php"><a cla'
176                . 'ss="row-main" href="statements/a2.html"><span class="chip tone-teal">INSERT</span><pre class="code"><span class="tok-kw">INSERT</span> <span'
177                . ' class="tok-kw">INTO</span>
178    posts (id)
179<span class="tok-kw">VALUES</span>
180    (<span class="tok-num">1</span>)</pre></a><p class="row-me'
181                . 'ta"><a href="files/src-a-php.html">src/a.php:9</a><a href="classes/app-r.html#fn-app-r-add">R::add</a><a class="chip chip-ghost" href="table'
182                . 's/posts.html">posts</a></p></li><li class="row" data-kind="update" data-resolution="resolved" data-severity="medium" data-rule="placeholder-'
183                . 'count-mismatch" data-sink="pdo.prepare" data-open="" data-table="posts" data-namespace="App" data-class="App\\R" data-function="App\\R::add" d'
184                . 'ata-file="src/a.php"><a class="row-main" href="statements/a3.html"><span class="chip tone-violet">UPDATE</span><pre class="code"><span class'
185                . '="tok-kw">UPDATE</span>
186    posts
187<span class="tok-kw">SET</span>
188    title = <span class="tok-var">?</span>
189<span class="tok-kw">WHERE</spa'
190                . 'n>
191    id = <span class="tok-var">?</span></pre></a><p class="row-meta"><a href="files/src-a-php.html">src/a.php:14</a><a href="classes/app-'
192                . 'r.html#fn-app-r-add">R::add</a><a class="chip chip-ghost" href="tables/posts.html">posts</a><span class="chip tone-warn" title="The most ser'
193                . 'ious finding on this statement">medium</span></p></li><li class="row" data-kind="delete" data-resolution="resolved" data-severity="" data-ru'
194                . 'le="" data-sink="pdo.query" data-open="" data-table="users" data-namespace="App\\Admin" data-class="App\\Admin\\U" data-function="App\\Admin\\U::'
195                . 'drop" data-file="src/b.php"><a class="row-main" href="statements/b1.html"><span class="chip tone-pink">DELETE</span><pre class="code"><span '
196                . 'class="tok-kw">DELETE</span> <span class="tok-kw">FROM</span>
197    users
198<span class="tok-kw">WHERE</span>
199    id = <span class="tok-num">1</'
200                . 'span></pre></a><p class="row-meta"><a href="files/src-b-php.html">src/b.php:3</a><a href="classes/app-admin-u.html#fn-app-admin-u-drop">U::d'
201                . 'rop</a><a class="chip chip-ghost" href="tables/users.html">users</a></p></li><li class="row" data-kind="alter" data-resolution="resolved" da'
202                . 'ta-severity="" data-rule="" data-sink="pdo.exec" data-open="" data-table="users" data-namespace="App\\Admin" data-class="App\\Admin\\U" data-fu'
203                . 'nction="App\\Admin\\U::migrate" data-file="src/b.php"><a class="row-main" href="statements/b2.html"><span class="chip tone-indigo">ALTER</span'
204                . '><pre class="code"><span class="tok-kw">ALTER</span> <span class="tok-kw">TABLE</span> users <span class="tok-kw">ADD</span> x INT</pre></a>'
205                . '<p class="row-meta"><a href="files/src-b-php.html">src/b.php:8</a><a href="classes/app-admin-u.html#fn-app-admin-u-migrate">U::migrate</a><a'
206                . ' class="chip chip-ghost" href="tables/users.html">users</a></p></li><li class="row" data-kind="select" data-resolution="resolved" data-sever'
207                . 'ity="medium" data-rule="dynamic-sql" data-sink="pdo.query" data-open="" data-table="posts users" data-namespace="App" data-class="App\\R" dat'
208                . 'a-function="App\\R::find" data-file="src/d.php"><a class="row-main" href="statements/d1.html"><span class="chip tone-blue">SELECT</span><pre '
209                . 'class="code"><span class="tok-kw">SELECT</span>
210    *
211<span class="tok-kw">FROM</span>
212    posts p
213<span class="tok-kw">JOIN</span>
214    user'
215                . 's u
216<span class="tok-kw">ON</span>
217    u.id = p.author</pre></a><p class="row-meta"><a href="files/src-d-php.html">src/d.php:1</a><a href="c'
218                . 'lasses/app-r.html#fn-app-r-find">R::find</a><a class="chip chip-ghost" href="tables/posts.html">posts</a><a class="chip chip-ghost" href="ta'
219                . 'bles/users.html">users</a><span class="chip tone-warn" title="The most serious finding on this statement">medium</span></p></li><li class="r'
220                . 'ow" data-kind="show" data-resolution="resolved" data-severity="" data-rule="" data-sink="pdo.query" data-open="" data-table="" data-namespac'
221                . 'e="App" data-class="App\\R" data-function="App\\R::find" data-file="src/e.php"><a class="row-main" href="statements/e1.html"><span class="chip'
222                . ' tone-slate">SHOW</span><pre class="code"><span class="tok-kw">SHOW</span> TABLES</pre></a><p class="row-meta"><a href="files/src-e-php.html'
223                . '">src/e.php:1</a><a href="classes/app-r.html#fn-app-r-find">R::find</a></p></li><li class="row" data-kind="select" data-resolution="resolved'
224                . '" data-severity="" data-rule="" data-sink="pdo.query" data-open="" data-table="t1" data-namespace="App" data-class="App\\F" data-function="Ap'
225                . 'p\\F::a" data-file="src/f.php"><a class="row-main" href="statements/f1.html"><span class="chip tone-blue">SELECT</span><pre class="code"><spa'
226                . 'n class="tok-kw">SELECT</span>
227    <span class="tok-num">1</span>
228<span class="tok-kw">FROM</span>
229    t1</pre></a><p class="row-meta"><a hr'
230                . 'ef="files/src-f-php.html">src/f.php:1</a><a href="classes/app-f.html#fn-app-f-a">F::a</a><a class="chip chip-ghost" href="tables/t1.html">t1'
231                . '</a></p></li><li class="row" data-kind="select" data-resolution="resolved" data-severity="" data-rule="" data-sink="pdo.query" data-open="" '
232                . 'data-table="t2" data-namespace="App" data-class="App\\F" data-function="App\\F::b" data-file="src/f.php"><a class="row-main" href="statements/'
233                . 'f2.html"><span class="chip tone-blue">SELECT</span><pre class="code"><span class="tok-kw">SELECT</span>
234    <span class="tok-num">2</span>
235<'
236                . 'span class="tok-kw">FROM</span>
237    t2</pre></a><p class="row-meta"><a href="files/src-f-php.html">src/f.php:2</a><a href="classes/app-f.htm'
238                . 'l#fn-app-f-b">F::b</a><a class="chip chip-ghost" href="tables/t2.html">t2</a></p></li><li class="row" data-kind="select" data-resolution="re'
239                . 'solved" data-severity="" data-rule="" data-sink="pdo.query" data-open="" data-table="t3" data-namespace="App" data-class="App\\G" data-functi'
240                . 'on="App\\G::a" data-file="src/f.php"><a class="row-main" href="statements/f3.html"><span class="chip tone-blue">SELECT</span><pre class="code'
241                . '"><span class="tok-kw">SELECT</span>
242    <span class="tok-num">3</span>
243<span class="tok-kw">FROM</span>
244    t3</pre></a><p class="row-meta"'
245                . '><a href="files/src-f-php.html">src/f.php:3</a><a href="classes/app-g.html#fn-app-g-a">G::a</a><a class="chip chip-ghost" href="tables/t3.ht'
246                . 'ml">t3</a></p></li><li class="row" data-kind="select" data-resolution="resolved" data-severity="" data-rule="" data-sink="pdo.query" data-op'
247                . 'en="" data-table="t4" data-namespace="App" data-class="App\\H" data-function="App\\H::a" data-file="src/f.php"><a class="row-main" href="state'
248                . 'ments/f4.html"><span class="chip tone-blue">SELECT</span><pre class="code"><span class="tok-kw">SELECT</span>
249    <span class="tok-num">4</s'
250                . 'pan>
251<span class="tok-kw">FROM</span>
252    t4</pre></a><p class="row-meta"><a href="files/src-f-php.html">src/f.php:4</a><a href="classes/app'
253                . '-h.html#fn-app-h-a">H::a</a><a class="chip chip-ghost" href="tables/t4.html">t4</a></p></li><li class="row" data-kind="select" data-resoluti'
254                . 'on="resolved" data-severity="" data-rule="" data-sink="pdo.query" data-open="" data-table="t5" data-namespace="App" data-class="App\\I" data-'
255                . 'function="App\\I::a" data-file="src/g.php"><a class="row-main" href="statements/f5.html"><span class="chip tone-blue">SELECT</span><pre class'
256                . '="code"><span class="tok-kw">SELECT</span>
257    <span class="tok-num">5</span>
258<span class="tok-kw">FROM</span>
259    t5</pre></a><p class="row'
260                . '-meta"><a href="files/src-g-php.html">src/g.php:1</a><a href="classes/app-i.html#fn-app-i-a">I::a</a><a class="chip chip-ghost" href="tables'
261                . '/t5.html">t5</a></p></li><li class="row" data-kind="select" data-resolution="resolved" data-severity="" data-rule="" data-sink="pdo.query" d'
262                . 'ata-open="" data-table="t6" data-namespace="App" data-class="App\\J" data-function="App\\J::a" data-file="src/h.php"><a class="row-main" href='
263                . '"statements/f6.html"><span class="chip tone-blue">SELECT</span><pre class="code"><span class="tok-kw">SELECT</span>
264    <span class="tok-num'
265                . '">6</span>
266<span class="tok-kw">FROM</span>
267    t6</pre></a><p class="row-meta"><a href="files/src-h-php.html">src/h.php:1</a><a href="class'
268                . 'es/app-j.html#fn-app-j-a">J::a</a><a class="chip chip-ghost" href="tables/t6.html">t6</a></p></li><li class="row" data-kind="select" data-re'
269                . 'solution="resolved" data-severity="medium" data-rule="dynamic-sql" data-sink="pdo.query" data-open="" data-table="t7" data-namespace="App" d'
270                . 'ata-class="App\\K" data-function="App\\K::a" data-file="src/i.php"><a class="row-main" href="statements/f7.html"><span class="chip tone-blue">'
271                . 'SELECT</span><pre class="code"><span class="tok-kw">SELECT</span>
272    <span class="tok-num">7</span>
273<span class="tok-kw">FROM</span>
274    t7'
275                . '</pre></a><p class="row-meta"><a href="files/src-i-php.html">src/i.php:1</a><a href="classes/app-k.html#fn-app-k-a">K::a</a><a class="chip c'
276                . 'hip-ghost" href="tables/t7.html">t7</a><span class="chip tone-warn" title="The most serious finding on this statement">medium</span></p></li'
277                . '><li class="row" data-kind="select" data-resolution="resolved" data-severity="medium" data-rule="dynamic-sql" data-sink="pdo.query" data-ope'
278                . 'n="" data-table="t8" data-namespace="App" data-class="App\\L" data-function="App\\L::a" data-file="src/j.php"><a class="row-main" href="statem'
279                . 'ents/f8.html"><span class="chip tone-blue">SELECT</span><pre class="code"><span class="tok-kw">SELECT</span>
280    <span class="tok-num">8</sp'
281                . 'an>
282<span class="tok-kw">FROM</span>
283    t8</pre></a><p class="row-meta"><a href="files/src-j-php.html">src/j.php:1</a><a href="classes/app-'
284                . 'l.html#fn-app-l-a">L::a</a><a class="chip chip-ghost" href="tables/t8.html">t8</a><span class="chip tone-warn" title="The most serious findi'
285                . 'ng on this statement">medium</span></p></li><li class="row" data-kind="select" data-resolution="resolved" data-severity="medium" data-rule="'
286                . 'dynamic-sql" data-sink="pdo.query" data-open="" data-table="t9" data-namespace="App" data-class="App\\M" data-function="App\\M::a" data-file="'
287                . 'src/k.php"><a class="row-main" href="statements/f9.html"><span class="chip tone-blue">SELECT</span><pre class="code"><span class="tok-kw">SE'
288                . 'LECT</span>
289    <span class="tok-num">9</span>
290<span class="tok-kw">FROM</span>
291    t9</pre></a><p class="row-meta"><a href="files/src-k-php'
292                . '.html">src/k.php:1</a><a href="classes/app-m.html#fn-app-m-a">M::a</a><a class="chip chip-ghost" href="tables/t9.html">t9</a><span class="ch'
293                . 'ip tone-warn" title="The most serious finding on this statement">medium</span></p></li></ol></div>',
294            (new StatementIndexPage())->render($site),
295        );
296    }
297}
298