Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug server #225

Draft
wants to merge 49 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
3f90d3f
Add var dumper collector
xepozz Aug 20, 2023
e4741d7
Include bootstrap config
xepozz Aug 21, 2023
3701eae
Fix composer-require-checker
xepozz Aug 21, 2023
6f5bdfc
Add timeline collector
xepozz Sep 2, 2023
38b70b9
Change timeline signature
xepozz Sep 2, 2023
98efc08
Use microtime instead of time
xepozz Sep 2, 2023
3426ea9
Remove redundant property
xepozz Sep 2, 2023
f8bf302
Apply fixes from StyleCI
StyleCIBot Sep 2, 2023
6d4db1b
Fix tests & psalm errors
xepozz Sep 4, 2023
5c19d73
Add var-dumper handler for dev-server
xepozz Sep 7, 2023
392a0a9
Add Connection class to control sockets
xepozz Sep 7, 2023
65960c5
Add dev-server command
xepozz Sep 7, 2023
2b05fe6
Add command to broadcast a message
xepozz Sep 7, 2023
1401836
Register commands
xepozz Sep 7, 2023
49d3e3e
Add bootstrap
xepozz Sep 7, 2023
619dae8
Apply fixes from StyleCI
StyleCIBot Sep 7, 2023
fbfdf76
Apply Rector changes (CI)
xepozz Sep 7, 2023
a9b10a6
Rename property
xepozz Sep 10, 2023
411df8a
Pass decorated instance to proxy callback factory
xepozz Sep 10, 2023
a5d1108
Use generators for reading
xepozz Sep 10, 2023
becbf1e
Improve socket wrapper
xepozz Sep 10, 2023
a365faa
Improve VarDumperHandler
xepozz Sep 10, 2023
ac0b04c
Add LoggerDecorator
xepozz Sep 10, 2023
fcfc53b
Add logger wrapper
xepozz Sep 10, 2023
939d44c
Add types
xepozz Sep 10, 2023
5b7fc1f
Apply fixes from StyleCI
StyleCIBot Sep 10, 2023
d90803d
Apply Rector changes (CI)
xepozz Sep 10, 2023
0c5e833
Specify type of debug message
xepozz Sep 11, 2023
fcff85b
Merge remote-tracking branch 'origin/dev-server' into dev-server
xepozz Sep 11, 2023
177e855
Apply fixes from StyleCI
StyleCIBot Sep 11, 2023
b6ff4c9
Apply Rector changes (CI)
xepozz Sep 11, 2023
bdc67c3
Add chunking
xepozz Sep 16, 2023
0fb8c48
Rename dev server -> debug server
xepozz Sep 16, 2023
c8e9248
Timeline collector (#224)
xepozz Sep 16, 2023
c378007
Merge branch 'var-dumper' into dev-server
xepozz Oct 4, 2023
50d0032
Merge branch 'master' into var-dumper
xepozz Oct 8, 2023
cc01080
Merge branch 'var-dumper' into dev-server
xepozz Oct 8, 2023
8fc864e
Merge branch 'master' into dev-server
xepozz Oct 8, 2023
14796e0
Merge branch 'master' into dev-server
xepozz Oct 14, 2023
6ecd849
Merge branch 'master' into dev-server
xepozz Jan 4, 2024
0351a23
Merge branch 'master' into dev-server
xepozz Mar 3, 2024
d5a9ef1
Merge branch 'master' into dev-server
xepozz Mar 4, 2024
a13a72e
Merge branch 'master' into dev-server
xepozz Jul 6, 2024
55788fe
Refactor
xepozz Jul 6, 2024
8b66603
Apply fixes from StyleCI
StyleCIBot Jul 6, 2024
3c41327
Research more
xepozz Jul 7, 2024
92e6313
Apply fixes from StyleCI
StyleCIBot Jul 7, 2024
31e5df6
Merge branch 'master' into dev-server
xepozz Nov 24, 2024
fde266e
Apply Rector changes (CI)
xepozz Nov 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer-require-checker.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"Yiisoft\\Cache\\Dependency\\Dependency",
"Yiisoft\\ErrorHandler\\Event\\ApplicationError",
"PHPUnit\\Framework\\TestCase",
"Yiisoft\\VarDumper\\HandlerInterface",
"opcache_invalidate"
]
}
13 changes: 11 additions & 2 deletions config/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,35 @@

declare(strict_types=1);

use Psr\Container\ContainerInterface;
use Yiisoft\VarDumper\Handler\CompositeHandler;
use Yiisoft\VarDumper\VarDumper;
use Yiisoft\Yii\Debug\Collector\VarDumperCollector;
use Yiisoft\Yii\Debug\Collector\VarDumperHandlerInterfaceProxy;
use Yiisoft\Yii\Debug\DebugServer\VarDumperHandler;

/**
* @var $params array
*/

return [
static function ($container) use ($params) {
static function (ContainerInterface $container) use ($params) {
if (!($params['yiisoft/yii-debug']['enabled'] ?? false)) {
return;
}
if (!$container->has(VarDumperCollector::class)) {
return;
}

$decorated = VarDumper::getDefaultHandler();

if ($params['yiisoft/yii-debug']['devServer']['enabled'] ?? false) {
$decorated = new CompositeHandler([$decorated, new VarDumperHandler()]);
}

VarDumper::setDefaultHandler(
new VarDumperHandlerInterfaceProxy(
VarDumper::getDefaultHandler(),
$decorated,
$container->get(VarDumperCollector::class),
),
);
Expand Down
18 changes: 16 additions & 2 deletions config/di.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
use Composer\Autoload\ClassLoader;
use Psr\Container\ContainerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface;
use Yiisoft\Aliases\Aliases;
use Yiisoft\VarDumper\ClosureExporter;
use Yiisoft\VarDumper\UseStatementParser;
use Yiisoft\Yii\Debug\Collector\ContainerInterfaceProxy;
use Yiisoft\Yii\Debug\Collector\ContainerProxyConfig;
use Yiisoft\Yii\Debug\Collector\LogCollector;
use Yiisoft\Yii\Debug\Collector\LoggerInterfaceProxy;
use Yiisoft\Yii\Debug\Collector\ServiceCollector;
use Yiisoft\Yii\Debug\Collector\Stream\FilesystemStreamCollector;
use Yiisoft\Yii\Debug\DebuggerIdGenerator;
use Yiisoft\Yii\Debug\DebugServer\LoggerDecorator;
use Yiisoft\Yii\Debug\Storage\FileStorage;
use Yiisoft\Yii\Debug\Storage\StorageInterface;

Expand Down Expand Up @@ -44,13 +48,23 @@
$params = $params['yiisoft/yii-debug'];
$collector = $container->get(ServiceCollector::class);
$dispatcher = $container->get(EventDispatcherInterface::class);
$debuggerEnabled = (bool) ($params['enabled'] ?? false);
$isDebuggerEnabled = (bool) ($params['enabled'] ?? false);
$isDevServerEnabled = (bool) ($params['devServer']['enabled'] ?? false);

$trackedServices = (array) ($params['trackedServices'] ?? []);

if ($isDevServerEnabled) {
$trackedServices[LoggerInterface::class] = static fn (
ContainerInterface $container,
LoggerInterface $logger,
) => new LoggerInterfaceProxy(new LoggerDecorator($logger), $container->get(LogCollector::class));
}

$path = $container->get(Aliases::class)->get('@runtime/cache/container-proxy');
$logLevel = $params['logLevel'] ?? ContainerInterfaceProxy::LOG_NOTHING;

return new ContainerProxyConfig(
$debuggerEnabled,
$isDebuggerEnabled,
$trackedServices,
$dispatcher,
$collector,
Expand Down
7 changes: 7 additions & 0 deletions config/params.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
use Yiisoft\Yii\Debug\Collector\Web\RequestCollector;
use Yiisoft\Yii\Debug\Collector\Web\WebAppInfoCollector;
use Yiisoft\Yii\Debug\Command\DebugResetCommand;
use Yiisoft\Yii\Debug\Command\DebugServerBroadcastCommand;
use Yiisoft\Yii\Debug\Command\DebugServerCommand;

/**
* @var $params array
Expand All @@ -33,6 +35,9 @@
return [
'yiisoft/yii-debug' => [
'enabled' => true,
'devServer' => [
'enabled' => true,
],
'collectors' => [
LogCollector::class,
EventCollector::class,
Expand Down Expand Up @@ -88,6 +93,8 @@
'yiisoft/yii-console' => [
'commands' => [
'debug:reset' => DebugResetCommand::class,
DebugServerCommand::COMMAND_NAME => DebugServerCommand::class,
DebugServerBroadcastCommand::COMMAND_NAME => DebugServerBroadcastCommand::class,
],
],
];
19 changes: 5 additions & 14 deletions src/Collector/ContainerInterfaceProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ final class ContainerInterfaceProxy implements ContainerInterface

private ProxyManager $proxyManager;

private array $decoratedServices = [];

private array $serviceProxy = [];

public function __construct(
Expand Down Expand Up @@ -60,13 +58,7 @@ public function get($id): mixed
$this->logProxy(ContainerInterface::class, $this->decorated, 'get', [$id], $instance, $timeStart);
}

if (
is_object($instance)
&& (
($proxy = $this->getServiceProxyCache($id)) ||
($proxy = $this->getServiceProxy($id, $instance))
)
) {
if (is_object($instance) && ($proxy = $this->getServiceProxy($id, $instance))) {
$this->setServiceProxyCache($id, $proxy);
return $proxy;
}
Expand Down Expand Up @@ -96,13 +88,12 @@ public function isActive(): bool
return $this->config->getIsActive() && $this->config->getDecoratedServices() !== [];
}

private function getServiceProxyCache(string $service): ?object
{
return $this->serviceProxy[$service] ?? null;
}

private function getServiceProxy(string $service, object $instance): ?object
{
if (isset($this->serviceProxy[$service])) {
return $this->serviceProxy[$service];
}

if (!$this->isDecorated($service)) {
return null;
}
Expand Down
61 changes: 61 additions & 0 deletions src/Command/DebugServerBroadcastCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);
declare(ticks=1);

namespace Yiisoft\Yii\Debug\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Yiisoft\VarDumper\VarDumper;
use Yiisoft\Yii\Console\ExitCode;
use Yiisoft\Yii\Debug\DebugServer\Connection;

final class DebugServerBroadcastCommand extends Command
{
public const COMMAND_NAME = 'dev:broadcast';
protected static $defaultName = self::COMMAND_NAME;

protected static $defaultDescription = 'Runs PHP built-in web server';

public function configure(): void
{
$this
->setHelp(
'Broadcasts a message to all connected clients.'
)
->addOption('message', 'm', InputOption::VALUE_OPTIONAL, 'A text to broadcast', 'Test message')
->addOption('env', 'e', InputOption::VALUE_OPTIONAL, 'It is only used for testing.');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title('Yii3 Debug Server');
$io->writeln('https://yiiframework.com' . "\n");

$env = $input->getOption('env');
if ($env === 'test') {
return ExitCode::OK;
}

$socket = Connection::create();
if (\function_exists('pcntl_signal')) {
$io->success('Quit the server with CTRL-C or COMMAND-C.');

\pcntl_signal(\SIGINT, static function () use ($socket): void {
$socket->close();
exit(1);
});
}

$data = $input->getOption('message');
$socket->broadcast(Connection::MESSAGE_TYPE_LOGGER, $data);
$socket->broadcast(Connection::MESSAGE_TYPE_VAR_DUMPER, VarDumper::create(['$data' => $data])->asJson(false));

return ExitCode::OK;
}
}
89 changes: 89 additions & 0 deletions src/Command/DebugServerCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);
declare(ticks=1);

namespace Yiisoft\Yii\Debug\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Yiisoft\Yii\Console\ExitCode;
use Yiisoft\Yii\Debug\DebugServer\Connection;

final class DebugServerCommand extends Command
{
public const COMMAND_NAME = 'dev';
protected static $defaultName = self::COMMAND_NAME;

protected static $defaultDescription = 'Runs PHP built-in web server';

public function __construct(
private readonly string $address = '0.0.0.0',
private readonly int $port = 8890,
) {
parent::__construct();
}

public function configure(): void
{
$this
->setHelp(
'In order to access server from remote machines use 0.0.0.0:8000. That is especially useful when running server in a virtual machine.'
)
->addOption('address', 'a', InputOption::VALUE_OPTIONAL, 'Host to serve at', $this->address)
->addOption('port', 'p', InputOption::VALUE_OPTIONAL, 'Port to serve at', $this->port)
->addOption('env', 'e', InputOption::VALUE_OPTIONAL, 'It is only used for testing.');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title('Yii3 Debug Server');
$io->writeln('https://yiiframework.com' . "\n");

$env = $input->getOption('env');
if ($env === 'test') {
return ExitCode::OK;
}

$socket = Connection::create();
$socket->bind();

$io->success(
sprintf(
'Listening on "%s".',
$socket->getUri(),
)
);

if (\function_exists('pcntl_signal')) {
$io->success('Quit the server with CTRL-C or COMMAND-C.');

\pcntl_signal(\SIGINT, static function () use ($socket): void {
$socket->close();
exit(1);
});
}

foreach ($socket->read() as $message) {
if ($message[0] === Connection::TYPE_ERROR) {
$io->error('Connection closed with error: ' . $message[1]);
break;
}

$data = \json_decode($message[1], null, 512, JSON_THROW_ON_ERROR);
$type = match ($data[0]) {
Connection::MESSAGE_TYPE_VAR_DUMPER => 'VarDumper',
Connection::MESSAGE_TYPE_LOGGER => 'Logger',
default => 'Plain text',
};

$io->block($data[1], $type);
}

return ExitCode::OK;
}
}
Loading
Loading