Skip to content

Commit af71538

Browse files
committed
highlight leading/trailing whitespace in array keys / object property names
improve script output
1 parent c3c9907 commit af71538

34 files changed

Lines changed: 412 additions & 237 deletions

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"bdk/curl-http-message": "1.0",
5252
"bdk/errorhandler": "3.3.4",
5353
"bdk/promise": "1.0",
54-
"bdk/pubsub": "3.2",
54+
"bdk/pubsub": "3.2.1",
5555
"bdk/slack": "1.0",
5656
"bdk/teams": "1.0"
5757
},

src/Debug/Dump/AbstractValue.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public function __construct(Dumper $dumper)
6868
$this->optionStackPush(array(
6969
'addQuotes' => true,
7070
'charHighlight' => true,
71+
'charHighlightTrim' => false,
7172
'charReplace' => true,
7273
));
7374
}

src/Debug/Dump/Html/HtmlArray.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ private function dumpArrayValues(array $array, $outputKeys, array $absKeys)
102102
't_key' => true,
103103
),
104104
),
105-
$this->valDumper->dump($key, array('tagName' => null)) // don't wrap it
105+
$this->valDumper->dump($key, array(
106+
'charHighlightTrim' => true, // highlight leading/trailing whitespace
107+
'tagName' => null, // don't wrap it
108+
))
106109
)
107110
. '<span class="t_operator">=&gt;</span>'
108111
. $this->valDumper->dump($val)

src/Debug/Dump/Html/HtmlString.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public function isEncoded($val)
149149
/**
150150
* Add whitespace markup
151151
*
152-
* @param string $str string which to add whitespace html markup
152+
* @param string $str String which to add whitespace html markup
153153
*
154154
* @return string
155155
*/
@@ -289,7 +289,7 @@ private function doDump($val)
289289
$val = \htmlspecialchars($val);
290290
}
291291
if ($opts['charHighlight']) {
292-
$val = $this->highlightChars($val);
292+
$val = $this->highlightChars($val, $opts['charHighlightTrim']);
293293
}
294294
if ($opts['visualWhiteSpace']) {
295295
$val = $this->visualWhiteSpace($val);
@@ -326,11 +326,12 @@ protected function getEncoded()
326326
/**
327327
* Highlight confusable and other characters
328328
*
329-
* @param string $str HTML String to update
329+
* @param string $str HTML String to update
330+
* @param bool $highlightTrim (optional) highlight leading/trailing whitespace
330331
*
331332
* @return string
332333
*/
333-
private function highlightChars($str)
334+
private function highlightChars($str, $highlightTrim = false)
334335
{
335336
$chars = $this->valDumper->findChars($str);
336337
$charInfo = \array_intersect_key($this->valDumper->charData, \array_flip($chars));
@@ -347,6 +348,13 @@ private function highlightChars($str)
347348
: $this->buildHighlightReplacementOther($info);
348349
$str = \str_replace($char, $replacement, $str);
349350
}
351+
if ($highlightTrim) {
352+
$str = \preg_replace_callback('/(^\s+|\s+$)/', static function ($matches) {
353+
$substr = $matches[1];
354+
$substr = \str_replace(' ', '<span class="ws_s"> </span>', $substr);
355+
return '<span class="char-ws" title="whitespace">' . $substr . '</span>';
356+
}, $str);
357+
}
350358
return $str;
351359
}
352360
}

src/Debug/Dump/Html/Object/AbstractSection.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ protected function dumpItemInner($name, array $info, array $cfg)
142142
? $this->helper->dumpPhpDoc($info['phpDoc']['summary'])
143143
: '',
144144
),
145+
'charHighlightTrim' => true,
145146
)),
146147
'4_value' => $info['value'] !== Abstracter::UNDEFINED
147148
? '<span class="t_operator">=</span> ' . $this->valDumper->dump($info['value'], $cfg)

src/Debug/Dump/Html/Value.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public function __construct(Dumper $dumper)
5353
$this->string = new HtmlString($this);
5454
$this->optionStackPush(array(
5555
'charHighlight' => true,
56+
'charHighlightTrim' => false,
5657
'sanitize' => true,
5758
'sanitizeFirst' => true,
5859
'visualWhiteSpace' => true,

src/Debug/Plugin/Channel.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ public function getChannelsTop()
143143
$channels[$key] = $channel;
144144
}
145145
}
146+
\uasort($channels, [$this, 'sortChannelCallback']);
146147
return $channels;
147148
}
148149

@@ -203,6 +204,32 @@ public function hasChannel($path)
203204
: true;
204205
}
205206

207+
/**
208+
* uasort callback
209+
*
210+
* @param Debug $channelA Debug instance
211+
* @param Debug $channelB Debug instance
212+
*
213+
* @return int
214+
*
215+
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
216+
*/
217+
public function sortChannelCallback(Debug $channelA, Debug $channelB)
218+
{
219+
$sortA = $channelA->getCfg('channelSort', Debug::CONFIG_DEBUG);
220+
$sortB = $channelB->getCfg('channelSort', Debug::CONFIG_DEBUG);
221+
$nameA = $channelA->getCfg('channelName', Debug::CONFIG_DEBUG);
222+
$nameB = $channelB->getCfg('channelName', Debug::CONFIG_DEBUG);
223+
// "root" channel should come first
224+
if ($channelA === $this->debug) {
225+
return -1;
226+
}
227+
if ($channelB === $this->debug) {
228+
return 1;
229+
}
230+
return $sortB - $sortA ?: \strcasecmp($nameA, $nameB);
231+
}
232+
206233
/**
207234
* Create a child channel
208235
*

src/Debug/Route/AbstractRoute.php

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public function processAlerts()
100100
{
101101
$str = '';
102102
foreach ($this->data['alerts'] as $logEntry) {
103-
if ($this->channelTest($logEntry)) {
103+
if ($this->testChannelRegex($logEntry->getChannelKey())) {
104104
$str .= $this->processLogEntryViaEvent($logEntry);
105105
}
106106
}
@@ -116,7 +116,7 @@ public function processLog()
116116
{
117117
$str = '';
118118
foreach ($this->data['log'] as $logEntry) {
119-
if ($this->channelTest($logEntry)) {
119+
if ($this->testChannelRegex($logEntry->getChannelKey())) {
120120
$str .= $this->processLogEntryViaEvent($logEntry);
121121
}
122122
}
@@ -137,7 +137,7 @@ public function processSummary()
137137
$summaryData = \call_user_func_array('array_merge', $summaryData);
138138
}
139139
foreach ($summaryData as $logEntry) {
140-
if ($this->channelTest($logEntry)) {
140+
if ($this->testChannelRegex($logEntry->getChannelKey())) {
141141
$str .= $this->processLogEntryViaEvent($logEntry);
142142
}
143143
}
@@ -187,9 +187,10 @@ public function processLogEntry(LogEntry $logEntry)
187187
public function setChannelName(Event $event)
188188
{
189189
$debug = $event->getSubject();
190+
$isRoot = $debug === $debug->rootInstance;
190191
$this->channelKey = $debug->getCfg('channelKey', Debug::CONFIG_DEBUG);
191192
$this->channelKeyRoot = $debug->getCfg('channelKey', Debug::CONFIG_DEBUG);
192-
$this->setChannelRegex('#^' . \preg_quote($this->channelKey, '#') . '(\.|$)#');
193+
$this->setChannelRegex('#^' . ($isRoot ? '.+' : \preg_quote($this->channelKey, '#')) . '(\.|$)#');
193194
}
194195

195196
/**
@@ -204,18 +205,6 @@ public function setChannelRegex($regex)
204205
$this->channelRegex = $regex;
205206
}
206207

207-
/**
208-
* Test channel for inclusion
209-
*
210-
* @param LogEntry $logEntry LogEntry instance
211-
*
212-
* @return bool
213-
*/
214-
protected function channelTest(LogEntry $logEntry)
215-
{
216-
return \preg_match($this->channelRegex, $logEntry->getChannelKey()) === 1;
217-
}
218-
219208
/**
220209
* Get dumper
221210
*
@@ -304,7 +293,7 @@ protected function shouldInclude(LogEntry $logEntry)
304293
*
305294
* @return bool
306295
*/
307-
private function testChannelKeyMatch($channelKey, $channelKeys = array())
296+
protected function testChannelKeyMatch($channelKey, $channelKeys = array())
308297
{
309298
foreach ($channelKeys as $channelKeyTest) {
310299
if ($this->testChannelKey($channelKey, $channelKeyTest)) {
@@ -318,7 +307,7 @@ private function testChannelKeyMatch($channelKey, $channelKeys = array())
318307
* Test if channel key matches test value
319308
*
320309
* @param string $channelKey channelKey to test
321-
* @param string $channelKeyTest test string
310+
* @param string $channelKeyTest key to test against
322311
*
323312
* @return bool
324313
*/
@@ -339,4 +328,18 @@ private function testChannelKey($channelKey, $channelKeyTest)
339328
}
340329
return false;
341330
}
331+
332+
/**
333+
* Test channel for inclusion
334+
*
335+
* Channel must be the channel (or descendant) that we're outputting
336+
*
337+
* @param string $channelKey channelKey to test
338+
*
339+
* @return bool
340+
*/
341+
private function testChannelRegex($channelKey)
342+
{
343+
return \preg_match($this->channelRegex, $channelKey) === 1;
344+
}
342345
}

src/Debug/Route/ChromeLogger.php

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,12 @@ public function processLogEntries($event = null)
104104
$this->dumper->crateRaw = false;
105105
$this->data = $this->debug->data->get();
106106
$this->data['log'] = \array_values($this->data['log']);
107-
$this->buildJsonData();
107+
$this->processChannels();
108108
$this->max = $this->getMaxLength();
109109
$encoded = $this->encode($this->jsonData);
110110
if ($this->max && \strlen($encoded) > $this->max) {
111111
$this->reduceData();
112-
$this->buildJsonData();
112+
$this->processChannels();
113113
$encoded = $this->encode($this->jsonData);
114114
$encoded = $this->assertEncodedLength($encoded);
115115
}
@@ -164,34 +164,14 @@ private function assertEncodedLength($encoded)
164164
return $this->encode($this->jsonData);
165165
}
166166

167-
/**
168-
* Build Chromelogger JSON
169-
*
170-
* @return void
171-
*/
172-
protected function buildJsonData()
173-
{
174-
$this->jsonData['rows'] = [];
175-
$this->processAlerts();
176-
$this->processSummary();
177-
$this->processLog();
178-
$heading = ['PHP', $this->getRequestMethodUri()];
179-
if (!$this->cfg['group']) {
180-
\array_unshift($this->jsonData['rows'], [$heading, null, 'info']);
181-
return;
182-
}
183-
\array_unshift($this->jsonData['rows'], [$heading, null, 'groupCollapsed']);
184-
\array_push($this->jsonData['rows'], [[], null, 'groupEnd']);
185-
}
186-
187167
/**
188168
* Calculate header size
189169
*
190170
* @return int
191171
*/
192172
protected function calcHeaderSize()
193173
{
194-
$this->buildJsonData();
174+
$this->processChannels();
195175
$encoded = $this->encode($this->jsonData);
196176
return \strlen(self::HEADER_NAME . ': ') + \strlen($encoded);
197177
}
@@ -224,6 +204,73 @@ protected function getMaxLength()
224204
return \min($maxVals);
225205
}
226206

207+
/**
208+
* Process log entries for given channel
209+
*
210+
* @param Debug $instance Debug instance
211+
*
212+
* @return void
213+
*/
214+
protected function processChannel(Debug $instance)
215+
{
216+
$channelKey = $instance->getCfg('channelKey', Debug::CONFIG_DEBUG);
217+
$name = $instance->getCfg('channelName', Debug::CONFIG_DEBUG);
218+
$this->setChannelRegex('#^' . \preg_quote($channelKey, '#') . '(\.|$)#');
219+
220+
$include = $this->testChannelKeyMatch($channelKey, $this->cfg['channels'])
221+
&& !$this->testChannelKeyMatch($channelKey, $this->cfg['channelsExclude']);
222+
223+
if ($include === false) {
224+
return;
225+
}
226+
227+
if ($instance === $instance->rootInstance) {
228+
$name = $this->debug->i18n->trans('channel.log');
229+
}
230+
231+
return $this->processLogEntryViaEvent(new LogEntry(
232+
$this->debug,
233+
'groupCollapsed',
234+
[$name]
235+
))
236+
. $this->processAlerts()
237+
. $this->processSummary()
238+
. $this->processLog()
239+
. $this->processLogEntryViaEvent(new LogEntry(
240+
$this->debug,
241+
'groupEnd'
242+
));
243+
}
244+
245+
/**
246+
* Process log entries grouped by top-level channels ("tabs")
247+
*
248+
* @return void
249+
*/
250+
protected function processChannels()
251+
{
252+
$this->jsonData['rows'] = [];
253+
$channels = $this->debug->getChannelsTop();
254+
foreach ($channels as $instance) {
255+
$key = $instance->getCfg('channelKey', Debug::CONFIG_DEBUG);
256+
if (\in_array($key, $this->cfg['channelsExclude'], true)) {
257+
continue;
258+
}
259+
if ($instance->getCfg('output', Debug::CONFIG_DEBUG) === false) {
260+
continue;
261+
}
262+
$this->processChannel($instance);
263+
}
264+
$heading = ['PHP', $this->getRequestMethodUri()];
265+
if (!$this->cfg['group']) {
266+
// not wrapping in group, just prepend an info heading
267+
\array_unshift($this->jsonData['rows'], [$heading, null, 'info']);
268+
return;
269+
}
270+
\array_unshift($this->jsonData['rows'], [$heading, null, 'groupCollapsed']);
271+
\array_push($this->jsonData['rows'], [[], null, 'groupEnd']);
272+
}
273+
227274
/**
228275
* Attempt to remove log entries to get header length < max
229276
*

0 commit comments

Comments
 (0)