Skip to content

Commit b4dfca5

Browse files
committed
WordPress plugin
1 parent cd48fd1 commit b4dfca5

29 files changed

Lines changed: 4433 additions & 1 deletion

phpunit.xml.dist

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@
3333
<file>src/Debug/Collector/Pdo/CompatTrait.php</file>
3434
<file>src/Debug/Collector/SimpleCache/CompatTrait.php</file>
3535
<file>src/Debug/Dump/charData.php</file>
36-
<directory>src/Debug/Framework</directory>
3736
<directory>src/Debug/lang</directory>
37+
<directory>src/Debug/Framework/Laravel</directory>
38+
<directory>src/Debug/Framework/Symfony</directory>
39+
<directory>src/Debug/Framework/Yii1_1</directory>
40+
<directory>src/Debug/Framework/Yii2</directory>
41+
<file>src/Debug/Framework/Cake4.php</file>
42+
<file>src/Debug/Framework/Slim2.php</file>
3843
<directory>src/Debug/node_modules</directory>
3944
<directory>src/Psr7</directory>
4045
</exclude>
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
<?php
2+
3+
namespace bdk\Debug\Framework\WordPress;
4+
5+
use bdk\Debug;
6+
use bdk\PubSub\Event;
7+
use bdk\PubSub\SubscriberInterface;
8+
9+
/**
10+
* Capture calls to deprecated functions, methods, classes, and files
11+
*/
12+
class Deprecated implements SubscriberInterface
13+
{
14+
const I18N_DOMAIN = 'wordpress';
15+
16+
/** @var Debug */
17+
protected $debug;
18+
19+
/**
20+
* {@inheritDoc}
21+
*/
22+
public function getSubscriptions()
23+
{
24+
return array(
25+
Debug::EVENT_BOOTSTRAP => 'onBootstrap',
26+
);
27+
}
28+
29+
/**
30+
* Debug::EVENT_BOOTSTRAP subscriber
31+
*
32+
* @param Event $event Debug::EVENT_BOOTSTRAP event object
33+
*
34+
* @return void
35+
*/
36+
public function onBootstrap(Event $event)
37+
{
38+
$this->debug = $event->getSubject();
39+
40+
\add_action('deprecated_argument_run', [$this, 'onDeprecatedArgument'], 0, 3);
41+
\add_action('deprecated_class_run', [$this, 'onDeprecatedConstructor'], 0, 3);
42+
\add_action('deprecated_constructor_run', [$this, 'onDeprecatedConstructor'], 0, 3);
43+
\add_action('deprecated_file_included', [$this, 'onDeprecatedFile'], 0, 4);
44+
\add_action('deprecated_function_run', [$this, 'onDeprecatedFunction'], 0, 3);
45+
\add_action('deprecated_hook_run', [$this, 'onDeprecatedHook'], 0, 3);
46+
47+
// Don't trigger E_NOTICE for deprecated usage.
48+
foreach (['argument', 'class', 'constructor', 'file', 'function', 'hook'] as $item) {
49+
\add_filter('deprecated_' . $item . '_trigger_error', static function () {
50+
return false;
51+
});
52+
}
53+
54+
// require ABSPATH . '/wp-includes/class-json.php';
55+
// $json = new \Services_JSON();
56+
}
57+
58+
/**
59+
* Called when a deprecated argument is used.
60+
*
61+
* @param string $function The function that was called.
62+
* @param string $message A message regarding the change.
63+
* @param string $version The version of WordPress that deprecated the argument used.
64+
*
65+
* @return void
66+
*/
67+
public function onDeprecatedArgument($function, $message, $version)
68+
{
69+
$message = \trim(
70+
$this->debug->i18n->trans('deprecated.func.arg', array(
71+
'function' => $function,
72+
'version' => $version,
73+
), self::I18N_DOMAIN) . ' ' . $message
74+
);
75+
$this->warn($message);
76+
}
77+
78+
/**
79+
* Called when a deprecated class is called.
80+
*
81+
* @param string $old The name of the class being instantiated.
82+
* @param string $replacement The class or function that should have been called.
83+
* @param string $version The version of WordPress that deprecated the class.
84+
*
85+
* @return void
86+
*/
87+
public function onDeprecatedClass($old, $replacement, $version)
88+
{
89+
$messageArgs = array(
90+
'old' => $old,
91+
'replacement' => $replacement,
92+
'version' => $version,
93+
);
94+
$message = $replacement
95+
? $this->debug->i18n->trans('deprecated.has-replacement', $messageArgs, self::I18N_DOMAIN)
96+
: $this->debug->i18n->trans('deprecated.no-replacement', $messageArgs, self::I18N_DOMAIN);
97+
$this->warn($message);
98+
}
99+
100+
/**
101+
* Called when a deprecated constructor is called.
102+
*
103+
* @param string $className The class containing the deprecated constructor.
104+
* @param string $version The version of WordPress that deprecated the function.
105+
* @param string $parentClass The parent class calling the deprecated constructor.
106+
*
107+
* @return void
108+
*/
109+
public function onDeprecatedConstructor($className, $version, $parentClass)
110+
{
111+
$messageArgs = array(
112+
'old' => $className,
113+
'parentClass' => $parentClass,
114+
'replacement' => '__construct',
115+
'version' => $version,
116+
);
117+
$message = $parentClass && $parentClass !== $className
118+
? $this->debug->i18n->trans('deprecated.constructor.parent-class', $messageArgs, self::I18N_DOMAIN)
119+
: $this->debug->i18n->trans('deprecated.constructor', $messageArgs, self::I18N_DOMAIN);
120+
$this->warn($message);
121+
}
122+
123+
/**
124+
* Called when a deprecated file is included
125+
*
126+
* @param string $old The file that was included
127+
* @param string $replacement The file that should have been included based on ABSPATH.
128+
* @param string $version The version of WordPress that deprecated the file.
129+
* @param string $message A message regarding the change.
130+
*
131+
* @return void
132+
*/
133+
public function onDeprecatedFile($old, $replacement, $version, $message = '')
134+
{
135+
$messageArgs = array(
136+
'old' => $old,
137+
'replacement' => $replacement,
138+
'version' => $version,
139+
);
140+
$message = \trim(($replacement
141+
? $this->debug->i18n->trans('deprecated.has-replacement', $messageArgs, self::I18N_DOMAIN)
142+
: $this->debug->i18n->trans('deprecated.no-replacement', $messageArgs, self::I18N_DOMAIN)
143+
) . ' ' . $message);
144+
$this->warn($message);
145+
}
146+
147+
/**
148+
* Called when a deprecated function is called.
149+
*
150+
* @param string $old The deprecated function that was called.
151+
* @param string $replacement The function that should have been called.
152+
* @param string $version The version of WordPress that deprecated the function.
153+
*
154+
* @return void
155+
*/
156+
public function onDeprecatedFunction($old, $replacement, $version)
157+
{
158+
$messageArgs = array(
159+
'old' => $old,
160+
'replacement' => $replacement,
161+
'version' => $version,
162+
);
163+
$message = $replacement
164+
? $this->debug->i18n->trans('deprecated.has-replacement', $messageArgs, self::I18N_DOMAIN)
165+
: $this->debug->i18n->trans('deprecated.no-replacement', $messageArgs, self::I18N_DOMAIN);
166+
$this->warn($message);
167+
}
168+
169+
/**
170+
* Called when a deprecated hook is used.
171+
*
172+
* @param string $old The deprecated hook that was used.
173+
* @param string $replacement The hook that should be used as a replacement.
174+
* @param string $version The version of WordPress that deprecated the argument used.
175+
* @param string $message A message regarding the change.
176+
*
177+
* @return void
178+
*/
179+
public function onDeprecatedHook($old, $replacement, $version, $message = '')
180+
{
181+
$messageArgs = array(
182+
'old' => $old,
183+
'replacement' => $replacement,
184+
'version' => $version,
185+
);
186+
$message = \trim(($replacement
187+
? $this->debug->i18n->trans('deprecated.has-replacement', $messageArgs, self::I18N_DOMAIN)
188+
: $this->debug->i18n->trans('deprecated.no-replacement', $messageArgs, self::I18N_DOMAIN)
189+
) . ' ' . $message);
190+
$this->warn($message);
191+
}
192+
193+
/**
194+
* Call warn with meta info
195+
*
196+
* @param string $message Message to log
197+
*
198+
* @return void
199+
*/
200+
private function warn($message)
201+
{
202+
$caller = $this->getCaller();
203+
$this->debug->warn($message, $this->debug->meta(array(
204+
'file' => $caller['file'],
205+
'icon' => 'fa fa-arrow-down',
206+
'line' => $caller['line'],
207+
)));
208+
}
209+
210+
/**
211+
* Find the caller of the deprecated whatever
212+
*
213+
* @return array|false
214+
*/
215+
private function getCaller()
216+
{
217+
$backtrace = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 8);
218+
$backtrace = \array_slice($backtrace, 3); // we can safely ignore the first few frames
219+
$foundDeprecated = false;
220+
foreach ($backtrace as $frame) {
221+
$function = $frame['function'];
222+
if (\strpos($function, '_deprecated_') === 0) {
223+
$foundDeprecated = true;
224+
} elseif ($foundDeprecated) {
225+
return $frame;
226+
}
227+
}
228+
return false;
229+
}
230+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
namespace bdk\Debug\Framework\WordPress;
4+
5+
use bdk\Debug;
6+
use bdk\Debug\AbstractComponent;
7+
use bdk\PubSub\Event;
8+
use bdk\PubSub\SubscriberInterface;
9+
10+
/**
11+
* Log cache info
12+
*/
13+
class Hooks extends AbstractComponent implements SubscriberInterface
14+
{
15+
const I18N_DOMAIN = 'wordpress';
16+
17+
/** @var array<string,mixed> */
18+
protected $cfg = array(
19+
'enabled' => true,
20+
);
21+
22+
/** @var Debug */
23+
protected $debug;
24+
25+
/** @var array<array-key,int> */
26+
protected $hooks = array();
27+
28+
/**
29+
* {@inheritDoc}
30+
*/
31+
public function getSubscriptions()
32+
{
33+
return array(
34+
Debug::EVENT_OUTPUT => 'onOutput',
35+
);
36+
}
37+
38+
/**
39+
* Called on each emitted hook
40+
*
41+
* @param string $hook Hook name
42+
*
43+
* @return void
44+
*/
45+
public function onHook($hook)
46+
{
47+
if (!isset($this->hooks[$hook])) {
48+
$trace = \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
49+
$functions = \array_column($trace, 'function');
50+
$isFilter = \strpos(\json_encode($functions), 'apply_filter') !== false;
51+
$this->hooks[$hook] = array(
52+
'count' => 0,
53+
'isFilter' => $isFilter,
54+
);
55+
}
56+
$this->hooks[$hook]['count']++;
57+
}
58+
59+
/**
60+
* Debug::EVENT_OUTPUT subscriber
61+
*
62+
* @param Event $event Debug::EVENT_BOOTSTRAP event object
63+
*
64+
* @return void
65+
*/
66+
public function onOutput(Event $event)
67+
{
68+
if (empty($this->hooks)) {
69+
return;
70+
}
71+
\ksort($this->hooks);
72+
$debug = $event->getSubject();
73+
$this->debug = $debug->getChannel('hooks', array(
74+
'channelIcon' => 'fa fa-arrow-up',
75+
'channelName' => $debug->i18n->trans('channel.hooks', [], self::I18N_DOMAIN),
76+
'channelSort' => 1,
77+
'nested' => false,
78+
));
79+
$this->debug->table('Hooks', $this->hooks, $this->debug->meta(array(
80+
'columns' => ['isFilter', 'count'],
81+
'tableInfo' => array(
82+
'columns' => array(
83+
'isFilter' => array(
84+
'attribs' => array('class' => ['text-center']),
85+
'falseAs' => '',
86+
'trueAs' => '<i class="fa fa-check"></i>',
87+
),
88+
),
89+
),
90+
'totalCols' => ['count'],
91+
)));
92+
}
93+
94+
/**
95+
* {@inheritDoc}
96+
*/
97+
protected function postSetCfg($cfg = array(), $prev = array())
98+
{
99+
$isFirstConfig = empty($this->cfg['configured']);
100+
$enabledChanged = isset($cfg['enabled']) && $cfg['enabled'] !== $prev['enabled'];
101+
if ($enabledChanged === false && $isFirstConfig === false) {
102+
return;
103+
}
104+
$this->cfg['configured'] = true;
105+
if ($cfg['enabled']) {
106+
\add_action('all', [$this, 'onHook']);
107+
return;
108+
}
109+
\remove_action('all', [$this, 'onHook']);
110+
}
111+
}

0 commit comments

Comments
 (0)