bkdotcom/PHPDebugConsole

View on GitHub
src/Debug/Plugin/Channel.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

/**
 * This file is part of PHPDebugConsole
 *
 * @package   PHPDebugConsole
 * @author    Brad Kent <bkfake-github@yahoo.com>
 * @license   http://opensource.org/licenses/MIT MIT
 * @copyright 2014-2024 Brad Kent
 * @since     3.0b1
 */

namespace bdk\Debug\Plugin;

use bdk\Debug;
use bdk\Debug\Plugin\CustomMethodTrait;
use bdk\PubSub\SubscriberInterface;

/**
 * Channel management
 */
class Channel implements SubscriberInterface
{
    use CustomMethodTrait;

    /** @var array<string,Debug> */
    private $channels = array();

    /** @var string[] */
    protected $methods = [
        'getChannel',
        'getChannels',
        'getChannelsTop',
        'getPropagateValues',
    ];

    /**
     * Return a named sub-instance... if channel does not exist, it will be created
     *
     * Channels can be used to categorize log data... for example, may have a framework channel, database channel, library-x channel, etc
     * Channels may have subchannels
     *
     * @param string|array $name   channel name (or channel path)
     * @param array        $config channel specific configuration
     *
     * @return Debug new or existing `Debug` instance
     *
     * @since 2.3
     */
    public function getChannel($name, $config = array())
    {
        // Split on "."
        // Split on "/" not adjacent to whitespace
        $names = \is_string($name)
            ? \preg_split('#(\.|(?<!\s)/(?!\s))#', $name)
            : $name;
        $name = \array_shift($names);
        if ($name === $this->debug->rootInstance->getCfg('channelName', Debug::CONFIG_DEBUG)) {
            return $names
                ? $this->debug->rootInstance->getChannel($names, $config)
                : $this->debug->rootInstance;
        }
        $curChannelName = $this->debug->getCfg('channelName', Debug::CONFIG_DEBUG);
        if (!isset($this->channels[$curChannelName][$name])) {
            $this->channels[$curChannelName][$name] = $this->createChannel($name, $names
                ? array()
                : $config);
        }
        $channel = $this->channels[$curChannelName][$name];
        if ($names) {
            $channel = $channel->getChannel($names, $config);
        }
        unset($config['nested']);
        if ($config) {
            $channel->setCfg($config, Debug::CONFIG_NO_RETURN);
        }
        return $channel;
    }

    /**
     * Return array of channels
     *
     * If $allDescendants == true :  key = "fully qualified" channel name
     *
     * @param bool $allDescendants (false) include all descendants?
     * @param bool $inclTop        (false) whether to incl topmost channels (ie "tabs")
     *
     * @return \bdk\Debug[] Does not include self
     */
    public function getChannels($allDescendants = false, $inclTop = false)
    {
        $curChannelName = $this->debug->getCfg('channelName', Debug::CONFIG_DEBUG);
        $channels = isset($this->channels[$curChannelName])
            ? $this->channels[$curChannelName]
            : array();
        if ($allDescendants) {
            $debug = $this->debug;
            $channelsNew = array();
            foreach ($channels as $channel) {
                $channelName = $channel->getCfg('channelName', Debug::CONFIG_DEBUG);
                $channelsNew = \array_merge(
                    $channelsNew,
                    array(
                        $channelName => $channel,
                    ),
                    $channel->getChannels(true)
                );
            }
            $channels = $channelsNew;
            $this->debug = $debug;
        }
        return $inclTop === false && $this->debug === $this->debug->rootInstance
            ? \array_diff_key($channels, $this->getChannelsTop())
            : $channels;
    }

    /**
     * Get the topmost channels (ie "tabs")
     *
     * (includes the general/root channel)
     *
     * @return \bdk\Debug[]
     */
    public function getChannelsTop()
    {
        $channelName = $this->debug->getCfg('channelName', Debug::CONFIG_DEBUG);
        $channels = array(
            $channelName => $this->debug,
        );
        if ($this->debug->parentInstance) {
            return $channels;
        }
        foreach ($this->debug->rootInstance->getChannels(false, true) as $name => $channel) {
            $fqn = $channel->getCfg('channelName', Debug::CONFIG_DEBUG);
            if (\strpos($fqn, '.') === false) {
                $channels[$name] = $channel;
            }
        }
        return $channels;
    }

    /**
     * Remove config values that should not be propagated to children channels
     *
     * @param array $cfg config array
     *
     * @return array
     */
    public function getPropagateValues($cfg)
    {
        $cfg = \array_diff_key($cfg, \array_flip([
            'errorHandler',
            'routeStream',
        ]));
        $cfg['debug'] = \array_diff_key($cfg['debug'], \array_flip([
            'channelIcon',
            'channelName',
            'onBootstrap',
            'onLog',
            'onOutput',
            'route',
        ]));
        foreach ($cfg as $k => $v) {
            if ($v === array()) {
                unset($cfg[$k]);
            }
        }
        return $cfg;
    }

    /**
     * Create a child channel
     *
     * @param string $name   Channel name
     * @param array  $config channel config
     *
     * @return array
     */
    private function createChannel($name, $config)
    {
        $cfg = $this->debug->getCfg(null, Debug::CONFIG_INIT);
        $channelNameCur = $cfg['debug']['channelName'];
        $cfg = $this->getPropagateValues($cfg);
        $cfg['debug'] = \array_merge(
            $cfg['debug'],
            array(
                'channelIcon' => null,
                'nested' => true, // true = regular child channel, false = tab
            ),
            isset($cfg['debug']['channels'][$name])
                ? $cfg['debug']['channels'][$name]
                : array(),
            $config
        );
        $cfg['debug']['channelName'] = $cfg['debug']['nested'] || $this->debug->parentInstance
            ? $channelNameCur . '.' . $name
            : $name;
        $cfg['debug']['parent'] = $this->debug;
        unset($cfg['debug']['nested']);
        return new Debug($cfg);
    }
}