����C %# , #&')*)-0-(0%()(��C (((((((((((((((((((((((((((((((((((((((((((((((((((����"�������@�@�hC��}!���Ѱ��<"� 9iׂIIIHk�+?�c?��*Y�����!�du)b�T�9вU�$8G��I.�澬��D���Sq� q�}.<��Z�l�V!X� *x�-�\����t3i�Ũ�sNv71�ƛ\��z|t�L���$�����*f��kʮ��7�H;���~F%�'3�@�H�q�` 9mOL����/x@ @��G
d�8F�ه��Ka�Kdr�Fh.�]y4 JЛ��]�K�B�E$��$ $ �PR�����G�]��u�i$�$���'! "#031���C/Td=S�Q?���62Ccj{ ����̏d�چ/c�V�`��Wz͈�{Y`�d�h�L �]OB���l���o���mr���n��s-ڗEZ��N�_��1%b���H�ϣ������V�7):�ӷ)�}�~�(�;�!�b1�5K��[E�vϻ>��q.%� ���O���(�c�#x�$�'+��`٥v��v(�����M�"�v��B��.�a ���T�~�ϕ�hy(6nݱl��1yNɓx�������AR�8�rqv1.cS�+��_���&@�� �u�M�5Ĉ�Xm���eL�X�q��y#�9]�c�}ɄL��d�eJ몓���I1T�d��CaM�$��T�,�X �bʭ�!�%F5��X1x#���!�q��\��F��2��&Rq���C�ol~�̱�.0ϦL�d�`.������ ���m{�Y~k{C��}bv�;U��c<�r�~ɜs�1�j��]W�l��*նCr��Q�N9�-������d��E؛��nF��eړ�8(q��5UgRȱGTA��*������̆��V�珰����ezN��h�U]�T�FG�^���<��ay�,!���5.� �u�bΚ�V�J%��m�Dxn'�����6�@BPa�`��Hts� �ɮ���Ŏ�Zɬ��%B�X��d5Z���hC}�䅸�p+ k=��ʒ(�aՏFG&�%@/�{+�Yu+�ȣGѩ"O%�|vȲxF>�N(��ou�h6 &Y5��8�7�E$-��']n,@TD\��+���Ry�U��U^�Q,f>��1�����q��f��U��� ����F���ڥ��>I�����fNUw�u��#OMMQ6� N�*��_�� k� ����rS��`���1�:��!�F'<+� � b?O��2 !Q12A��� "3a������#$��?�,�7�!`yǮ(�1�6w��a���� �F�#��?*"s���v>��Ⱥ����f�v��͑���s����������]Gn��S ���ȥpG ы�E�g�)Z���x�rY�q�]�@f�_܃�pչEڎّC ����Ŝ*/ �h�O�Sv�و\��5��U��y��|o�Hm2C�S�BW����)��5��{T��W���=o*RA��<����L0g4{��쁢�ep�rw�8��7��U���t<Ԍѻ7�fGf�k}���Ê�㛆Gռz�Q@��{C��'G��8�!�S$�j��x���|���צV<��,����u�k�uu�rM�f�_dϣi ߫�ԟn�!K����mxu�=�槻�'j�X�����������%!A "1QR#Br��?�R:��R�n�b[�II?#��6<:�$gN����lGNlrr��dעMMn`ɿy�,�%B�e�W��dVS��r���� %�tT��(�ɷ��S�]�O]#�_LEMHN�M���kv���~X���O6�U�V_�����b���J�t�774����D!1AQa"2q�#3BRb����0���� 4CSr����cst�����?��^q���7�dG�U�"p��moz��'��n_x���唹e������<6��O�t���R>k��s=�Cr���e�?�i��� ����/��ں$be���o`ޮ�GHy�;fNAl�8��.�\�S������"���a�úF�YvNk�-*`v�k�ʈ2f�EE��Wa�,� �fF^#�;��[9��^~������Y$:0#W3������Z*���I�Z�ڹ�k�n--9=��G��;7F)m{T�Ɇ��=�����Ȭ5�5�B�aڞ5M����#m�5Ʀ��m�8��+Hh���$�}�:&�e�Q�[;i]С�:�:��o����$<~��5RB�?�s3�5�r��O��ֿ�w�P/��̅���(�Z6�R>)��N��4�!ʊ�wz�-�r�w+�yk���q�1�bKhƸ�4N�Ӑ�X����Q��_��})�+e1�5��n��q?��[�^�9�<�z3Fsi�8�'�)9p)�{��RP�Z+�*��p(aY��V����6l�g�9��;���d�u���Nt@�3�sTwzaŇ�GT�b�H��(#��*zc�������9K�b1�����t����Ê��
�Z?g�iD���H�R���B���^M����v���O���L�D,'d�q�C�P�����$Δ��U�֟֊=�s��F�$��J�ދZ?�N��������A�N�WP��,�� �¦�&;�x��dup�����i���Ipd���;�Dž!��ֿѮAb%�u��}j��-p��>I�[�N�bi����G�'�;4w�m]H�]����#LӘNN��R��������s�.]��en��-�8e��Ps����Q��;���ț�E�ݫ���7��g�_L��W��EZ:/��I���a�g�n�ܤ��iٹ���ŷ�T���H~i�a�����֎�~KV������ A-2m]�F"�m�9-Zbǰ�״ @����~�4�N�[�Uxč�tl>������u#r�gѐ�3���;M9�<�J�����1�vfL8����1�P�HgP�Xv��������{����O�}�n��KQ؋����7<�l�fey<�}�>�bX���4<`Y7���si��V)�s�:�{�rO�h�z �@4VW�B���&�������ɡob܋�F��4>y�s�fXWS�N�O$�,.u:�ԫ��g�yao4��$h��D#��ٸf^kh�7�#1Z�֥&���*�v-��;bޭ����Q�����h�ow�y]�ه.+�7�M�ⴻ �JY��g�f�i3q��KC��3�¹�?5�Z.N��^Z w���KF͂���7��ރ۞��wj��T�J.�q��\Sv1U����R��욽&�N����pЖ`�`у��m`v�n#z��4��>e��V�`'���h�����'�j�AҔ�-�4:H���n]9�h<��n����U�6m��2c�E�1/�Y�%���I��~ʏ�|VBƟ@����;�������%�M9M���}��1�D��d����%g���O��]��у&�r��f�7�uܲ���(!1AQaq�������0� ���?!��*��@)�Je�G��j��{�['��v+���������)���(�/����д%젍Z��kk�Lu�Rm���j.c���@Z� V�J��d��j���h6���2AO�� a;oBu���H�=���nK�W8�B�ɰ�u?��бأm,�sr����|����8˨i��qI2tZ�ۄJP��XE��������zޔj~]UMu����zv!����N�&�1�Y��zJ�ՠ��\p��o'ሸ�C؊Y��TD"HM5�Ъ��i߯a���F����A)�����ڮ����z�E���@�hg�֝8�1jk��\�M�3�8ܢ�� ������s�7����N}�ޭ������GN�Bc���L pk�;�J�δ3�e�iU�gAYW]\�>�GyگQ=��f�KA;T�a`eM+Q �� �Ln���̌]GM�����<Ħ�j���H��N�M�x�}aX{̣S� ��ԅ��n�MA�S�r�(����(�L��zo9���.�;
�ӳf������`Ӕ٢3�� IW��\9~_���saa�\ԊW�ܭX:���ӆ�38�ty*����N�qP����BI�Y��jE��>DP�!�R%-��4��'�皺;��~J�!�7m���X��h�P!曭���$�\�AYj�.lC��4��+�jD�dgC0-*���|��`ZD�+л�C"��)��s��8Kq�pq���Ms��4� ��7\U`�.��[Ey8��AH!/��,���(:M -�T䓥�~O�4-���Ԓn��}HDN7���K���$�_Ԕ䚞`�R�hB�_aX?4V��ŗ�@ه�u�a�;�{PcT+�������7YBo�?��r-ͩ{�ĎA�� ����˼n��M286��G���1���V�˜Jв"l��V5���5�C]h���̊�A���%� �'p���Ԃ���Ր��9=�d�=�e�{�'<3�_ �:^�~��4�(�n�-C�s��5m![�jmIqU�~�Tw8��`���p�H8�u�Д l m�aP�0�������9y����CM��F1G糞�.�U~�������FC�{�!e(Y�:���P����7~;�L�N^{�1r�\���ԬG(���0d�ÏO�qK�Z�⑼�T�{ 2��s��Kd�Տ?mMQ��=���6�7�i�����H+����9��d��=��;�QؤH8n�Lb�D��yS%�(�{b���Cu���p�t#C���$A"�H{���jqᶯ�:�n=E����hH�`�!�m��MA������?�v6���+MԿ⟚qK�i�D�*Q5��CZ���2�|]�:Xd+�t�:o@��M��� :�32��b����[\5=�ֵ7])�|t��Ϻ����w�B�ń�e���!`�:��I,��9:����j@/a 8����+<�u�(T^ۺ~��2oE�B�%b)��z��ݳځ�)��i�j��&��Fi`qr��w���7�@��P�� �3Z&<�m�S�C����7t�T����ƴ�q~J�e�r6�Z]�rL���ه�E17'�x���+[�ܜTc6�/�����W�`�qpMJ���N5^����x�}{l�Fm������1�oZ\�����/d�/6� �uӸ�0elXuX;M��$M�}mB��������Z%e���3f�js����O�J~2�z�86�*PB��v�Ν��e-��.�/��L�O����2����9���4}|��T5M���hÐ7�F*��l+y0����:|��=k[�d�;|�ԉe�=w�<��õ�<��'!1AQaq����� ������?��5����)�(���+>v����6&{���Ǹ@����M�����v��iA 6T'�w��h�s �E}�x��G&'g�� J~1q�f�f���&��q˘���-���vYm
�/i1 �I��6��u,)�#�,����l}*&`�$�ͬe�%�w3�x�Ѥ�Xc�D��执g�峕�5B/�|$��=���%8 a��2.l� c�@G� �\�/x[өq�]�v5?�����N|�!���\��,>��{�"r�/��?��&!1QAa�� ��ᑱ����?ĊD�肭�� nv@�yޝ (�����I ����U - ���b�m�E>,��1v!�d�&�� ���&�檔�5D�&0P��Ԕ�͒@Z��:E"� Q��`>PH:~�O�����P�3W��@hM��k�U��\�O��R�������5ʄ�,��f�|��r���}јxo)�"+h�QK���/��0�`�5�{M~�� ���'!1AQaq���0 �������?�?�k��#^�~�G��#V,������#Z�1'ܤ����������~p�O%O�O�\�q�`�~��}��E�Ű5 �輸�du����x\�$���s[�{T2t`B��gq�4Z]b� 㛪�3,(@����bAp�r)9:@|b�!r�g:N�^�Ʌ��� �x_�\��pm7I��0?>^k��������w���|.K�[sF@�]Gn*L �yO� le�P�.p��֍�j�S�=�ʨ�ןQF�"��5zʼn���k�*8�u" ����Fg��� �cSy�V������Ƈ��N��ؐ(�����48hV�A�ӎ^��^ ���jyB� ��p"�����y]�ļlU�(�7�U`3�pCGF'&yg������o��z������X��ν:�P"@�G@x[��o&MJ�$F.����hi w;}�/^͇q���n�mN�/�TQ���އ��O1\,}��bQ #¯^S!)��X���#GPȏ�t�� c^\��' }iIZ���a�)��������z��4͊�Ξy��48,��f���#�����KP!Jx�|w�ʆ�������������#��Z�������< �~K��r�p&qH/;�R���沽�+�E�R���~0v���V#ʀ�T��S(-ڝ��B�y�b�C�D������b��������8��~�= �Y�ͧ]��@n����M�k2�%�;�%,�r6�LR腻?^��;KŇ=�ք ���=`�ɥ��/����z�&�I{���#J��M���C��}�H9^UJ�,P ��pS����G�d69Ϭu���%"��ˢP��K�"k)��=��9� ����㇌,��Oli��Xzh� " � ������R��^�s����N�k��Q>�63(���� ��PQ�Py�����3����$f+W՛=4�ǁ`*��^��Eb�K�t�6��^��!�籷��ȭ��K{/;�L���p�x�����;a���Oلz�[�.NP4�]Gc�T�v����~sg'LED��]j��'�G�]�6rY����UPw�*O�İՋi�'8�۴�#g�Xx+=�eU6�R��c�"�u2��~�?n�y�;�u��3�'��6�f������b��߬M�$*��k&?6���*^1n����ێz)<��Gz� �����7����Y� ��ۃ)$A��2�L6� ե�H�<�r��#ʽ2��O��R���z�A��XW��@���������<�G� Ϥ�^�˓i�M�W���6 ��0��m){c�;ݧ�>R�a����}1�ٯ%�EY2�Q��Ep���$ ��E��qS��t#+x� *�h�UI��XM?�'//��a'�G�����q@���<��z��؟����cd��z�ˬT_u�Ѯ����&�z�k ��n ]�a%�py»�`Qd�xc������n�� ��*��oTd�;'j�<�!j���'�(~�ʹW�M� P�mȘ��@֨V+��R�`�$��`�+@��_[�kG����P���Zh9�R����&5b�v���Z���#p�&�Ա+��8�etZ7G���;��@"�e0���v7����?��z�?_���_�q1�T�"�p�ˎ/U 6_�B�>��0( ��}G#������Ȣ�p�� �9��;/& `�B&$�y��t(�*z�x���Ӕ������S�?Kȏ3���{p� b � ۍ-�z܈֦��6?<���ǬP�N�G �更� �6�/h�����0Z���������i�ua��e�*M'A� �x��v�q.>�F� oN{��Q���{gD��L��u��=|���O xN���d���q�8(��E�Uu��,��O� t�DJ ����;��G����e���C��VYZ�� ���T4{����(�Ӳ'c�t�f��w�c�jr�e�m �#7,�6��B�E4Q�P�.P�(&��^{9H-�m�o ��q�g1���=��>p�)/"p0!4�mS6ú�FN���h��D �)��XdT �FؤZ⸚�k���H�c8v� <���u�P�Հ���:��_�EN��|�ӛ��u?-�/�o�Lhk�ܸ�S�;�Rī�����T"�N����M��px7<�� j�$��`�Y)Pjh 5` K�Qf�4�C�bX"�D���;HD�Z�9R b�F)�UA����v�#��HD�!{������>I� �`�ԁ i�4�)t*�ç�Le�_���>ru�GEQg��ǔct��ō0��l6v���d�� ��GG8���v^�|�#JyZPSO�� Y�CuAߐ�"�x���OfHF@�K�V�!少Eҕ]h� ��[���)��.q����*0I<8��^�6�}p��^tho���ig�i����DK���p,��2�3�I��5����쓄OY�6s7Qs�Ow^�w�J/�A➰������0������g(Մ��y��Kԇ����QS��?H���w�X�=��ҞX�~���Q=�'���p?7�@g�~�G�}�r��g�T?���
One Hat Cyber Team
star
One Hat Cyber Team
star
Your IP :
216.73.216.151
Server IP :
50.6.170.23
Server :
Linux 50-6-170-23.bluehost.com 5.14.0-570.19.1.el9_6.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 4 04:00:24 EDT 2025 x86_64
Server Software :
Apache
PHP Version :
8.2.28
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
home
/
kidsuniversit
/
.trash
/
vendor.1
/
psy
/
psysh
/
src
/
View File Name :
Configuration.php
<?php /* * This file is part of Psy Shell. * * (c) 2012-2023 Justin Hileman * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Psy; use Psy\Exception\DeprecatedException; use Psy\Exception\RuntimeException; use Psy\ExecutionLoop\ProcessForker; use Psy\Output\OutputPager; use Psy\Output\ShellOutput; use Psy\Output\Theme; use Psy\TabCompletion\AutoCompleter; use Psy\VarDumper\Presenter; use Psy\VersionUpdater\Checker; use Psy\VersionUpdater\GitHubChecker; use Psy\VersionUpdater\IntervalChecker; use Psy\VersionUpdater\NoopChecker; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** * The Psy Shell configuration. */ class Configuration { const COLOR_MODE_AUTO = 'auto'; const COLOR_MODE_FORCED = 'forced'; const COLOR_MODE_DISABLED = 'disabled'; const INTERACTIVE_MODE_AUTO = 'auto'; const INTERACTIVE_MODE_FORCED = 'forced'; const INTERACTIVE_MODE_DISABLED = 'disabled'; const VERBOSITY_QUIET = 'quiet'; const VERBOSITY_NORMAL = 'normal'; const VERBOSITY_VERBOSE = 'verbose'; const VERBOSITY_VERY_VERBOSE = 'very_verbose'; const VERBOSITY_DEBUG = 'debug'; private static $AVAILABLE_OPTIONS = [ 'codeCleaner', 'colorMode', 'configDir', 'dataDir', 'defaultIncludes', 'eraseDuplicates', 'errorLoggingLevel', 'forceArrayIndexes', 'formatterStyles', 'historyFile', 'historySize', 'interactiveMode', 'manualDbFile', 'pager', 'prompt', 'rawOutput', 'requireSemicolons', 'runtimeDir', 'startupMessage', 'strictTypes', 'theme', 'updateCheck', 'useBracketedPaste', 'usePcntl', 'useReadline', 'useTabCompletion', 'useUnicode', 'verbosity', 'warnOnMultipleConfigs', 'yolo', ]; private $defaultIncludes; private $configDir; private $dataDir; private $runtimeDir; private $configFile; /** @var string|false */ private $historyFile; private $historySize; private $eraseDuplicates; private $manualDbFile; private $hasReadline; private $useReadline; private $useBracketedPaste; private $hasPcntl; private $usePcntl; private $newCommands = []; private $pipedInput; private $pipedOutput; private $rawOutput = false; private $requireSemicolons = false; private $strictTypes = false; private $useUnicode; private $useTabCompletion; private $newMatchers = []; private $errorLoggingLevel = \E_ALL; private $warnOnMultipleConfigs = false; private $colorMode = self::COLOR_MODE_AUTO; private $interactiveMode = self::INTERACTIVE_MODE_AUTO; private $updateCheck; private $startupMessage; private $forceArrayIndexes = false; /** @deprecated */ private $formatterStyles = []; private $verbosity = self::VERBOSITY_NORMAL; private $yolo = false; /** @var Theme */ private $theme; // services private $readline; /** @var ShellOutput */ private $output; private $shell; private $cleaner; private $pager; private $manualDb; private $presenter; private $autoCompleter; private $checker; /** @deprecated */ private $prompt; private $configPaths; /** * Construct a Configuration instance. * * Optionally, supply an array of configuration values to load. * * @param array $config Optional array of configuration values */ public function __construct(array $config = []) { $this->configPaths = new ConfigPaths(); // explicit configFile option if (isset($config['configFile'])) { $this->configFile = $config['configFile']; } elseif (isset($_SERVER['PSYSH_CONFIG']) && $_SERVER['PSYSH_CONFIG']) { $this->configFile = $_SERVER['PSYSH_CONFIG']; } elseif (\PHP_SAPI === 'cli-server' && ($configFile = \getenv('PSYSH_CONFIG'))) { $this->configFile = $configFile; } // legacy baseDir option if (isset($config['baseDir'])) { $msg = "The 'baseDir' configuration option is deprecated; ". "please specify 'configDir' and 'dataDir' options instead"; throw new DeprecatedException($msg); } unset($config['configFile'], $config['baseDir']); // go go gadget, config! $this->loadConfig($config); $this->init(); } /** * Construct a Configuration object from Symfony Console input. * * This is great for adding psysh-compatible command line options to framework- or app-specific * wrappers. * * $input should already be bound to an appropriate InputDefinition (see self::getInputOptions * if you want to build your own) before calling this method. It's not required, but things work * a lot better if we do. * * @see self::getInputOptions * * @throws \InvalidArgumentException * * @param InputInterface $input */ public static function fromInput(InputInterface $input): self { $config = new self(['configFile' => self::getConfigFileFromInput($input)]); // Handle --color and --no-color (and --ansi and --no-ansi aliases) if (self::getOptionFromInput($input, ['color', 'ansi'])) { $config->setColorMode(self::COLOR_MODE_FORCED); } elseif (self::getOptionFromInput($input, ['no-color', 'no-ansi'])) { $config->setColorMode(self::COLOR_MODE_DISABLED); } // Handle verbosity options if ($verbosity = self::getVerbosityFromInput($input)) { $config->setVerbosity($verbosity); } // Handle interactive mode if (self::getOptionFromInput($input, ['interactive', 'interaction'], ['-a', '-i'])) { $config->setInteractiveMode(self::INTERACTIVE_MODE_FORCED); } elseif (self::getOptionFromInput($input, ['no-interactive', 'no-interaction'], ['-n'])) { $config->setInteractiveMode(self::INTERACTIVE_MODE_DISABLED); } // Handle --compact if (self::getOptionFromInput($input, ['compact'])) { $config->setTheme('compact'); } // Handle --raw-output // @todo support raw output with interactive input? if (!$config->getInputInteractive()) { if (self::getOptionFromInput($input, ['raw-output'], ['-r'])) { $config->setRawOutput(true); } } // Handle --yolo if (self::getOptionFromInput($input, ['yolo'])) { $config->setYolo(true); } return $config; } /** * Get the desired config file from the given input. * * @return string|null config file path, or null if none is specified */ private static function getConfigFileFromInput(InputInterface $input) { // Best case, input is properly bound and validated. if ($input->hasOption('config')) { return $input->getOption('config'); } return $input->getParameterOption('--config', null, true) ?: $input->getParameterOption('-c', null, true); } /** * Get a boolean option from the given input. * * This helper allows fallback for unbound and unvalidated input. It's not perfect--for example, * it can't deal with several short options squished together--but it's better than falling over * any time someone gives us unbound input. * * @return bool true if the option (or an alias) is present */ private static function getOptionFromInput(InputInterface $input, array $names, array $otherParams = []): bool { // Best case, input is properly bound and validated. foreach ($names as $name) { if ($input->hasOption($name) && $input->getOption($name)) { return true; } } foreach ($names as $name) { $otherParams[] = '--'.$name; } foreach ($otherParams as $name) { if ($input->hasParameterOption($name, true)) { return true; } } return false; } /** * Get the desired verbosity from the given input. * * This is a bit more complext than the other options parsers. It handles `--quiet` and * `--verbose`, along with their short aliases, and fancy things like `-vvv`. * * @return string|null configuration constant, or null if no verbosity option is specified */ private static function getVerbosityFromInput(InputInterface $input) { // --quiet wins! if (self::getOptionFromInput($input, ['quiet'], ['-q'])) { return self::VERBOSITY_QUIET; } // Best case, input is properly bound and validated. // // Note that if the `--verbose` option is incorrectly defined as `VALUE_NONE` rather than // `VALUE_OPTIONAL` (as it is in Symfony Console by default) it doesn't actually work with // multiple verbosity levels as it claims. // // We can detect this by checking whether the the value === true, and fall back to unbound // parsing for this option. if ($input->hasOption('verbose') && $input->getOption('verbose') !== true) { switch ($input->getOption('verbose')) { case '-1': return self::VERBOSITY_QUIET; case '0': // explicitly normal, overrides config file default return self::VERBOSITY_NORMAL; case '1': case null: // `--verbose` and `-v` return self::VERBOSITY_VERBOSE; case '2': case 'v': // `-vv` return self::VERBOSITY_VERY_VERBOSE; case '3': case 'vv': // `-vvv` case 'vvv': case 'vvvv': case 'vvvvv': case 'vvvvvv': case 'vvvvvvv': return self::VERBOSITY_DEBUG; default: // implicitly normal, config file default wins return; } } // quiet and normal have to come before verbose, because it eats everything else. if ($input->hasParameterOption('--verbose=-1', true) || $input->getParameterOption('--verbose', false, true) === '-1') { return self::VERBOSITY_QUIET; } if ($input->hasParameterOption('--verbose=0', true) || $input->getParameterOption('--verbose', false, true) === '0') { return self::VERBOSITY_NORMAL; } // `-vvv`, `-vv` and `-v` have to come in descending length order, because `hasParameterOption` matches prefixes. if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || $input->getParameterOption('--verbose', false, true) === '3') { return self::VERBOSITY_DEBUG; } if ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || $input->getParameterOption('--verbose', false, true) === '2') { return self::VERBOSITY_VERY_VERBOSE; } if ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true)) { return self::VERBOSITY_VERBOSE; } } /** * Get a list of input options expected when initializing Configuration via input. * * @see self::fromInput * * @return InputOption[] */ public static function getInputOptions(): array { return [ new InputOption('config', 'c', InputOption::VALUE_REQUIRED, 'Use an alternate PsySH config file location.'), new InputOption('cwd', null, InputOption::VALUE_REQUIRED, 'Use an alternate working directory.'), new InputOption('color', null, InputOption::VALUE_NONE, 'Force colors in output.'), new InputOption('no-color', null, InputOption::VALUE_NONE, 'Disable colors in output.'), // --ansi and --no-ansi aliases to match Symfony, Composer, etc. new InputOption('ansi', null, InputOption::VALUE_NONE, 'Force colors in output.'), new InputOption('no-ansi', null, InputOption::VALUE_NONE, 'Disable colors in output.'), new InputOption('quiet', 'q', InputOption::VALUE_NONE, 'Shhhhhh.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_OPTIONAL, 'Increase the verbosity of messages.', '0'), new InputOption('compact', null, InputOption::VALUE_NONE, 'Run PsySH with compact output.'), new InputOption('interactive', 'i|a', InputOption::VALUE_NONE, 'Force PsySH to run in interactive mode.'), new InputOption('no-interactive', 'n', InputOption::VALUE_NONE, 'Run PsySH without interactive input. Requires input from stdin.'), // --interaction and --no-interaction aliases for compatibility with Symfony, Composer, etc new InputOption('interaction', null, InputOption::VALUE_NONE, 'Force PsySH to run in interactive mode.'), new InputOption('no-interaction', null, InputOption::VALUE_NONE, 'Run PsySH without interactive input. Requires input from stdin.'), new InputOption('raw-output', 'r', InputOption::VALUE_NONE, 'Print var_export-style return values (for non-interactive input)'), new InputOption('self-update', 'u', InputOption::VALUE_NONE, 'Update to the latest version'), new InputOption('yolo', null, InputOption::VALUE_NONE, 'Run PsySH with minimal input validation. You probably don\'t want this.'), ]; } /** * Initialize the configuration. * * This checks for the presence of Readline and Pcntl extensions. * * If a config file is available, it will be loaded and merged with the current config. * * If no custom config file was specified and a local project config file * is available, it will be loaded and merged with the current config. */ public function init() { // feature detection $this->hasReadline = \function_exists('readline'); $this->hasPcntl = ProcessForker::isSupported(); if ($configFile = $this->getConfigFile()) { $this->loadConfigFile($configFile); } if (!$this->configFile && $localConfig = $this->getLocalConfigFile()) { $this->loadConfigFile($localConfig); } $this->configPaths->overrideDirs([ 'configDir' => $this->configDir, 'dataDir' => $this->dataDir, 'runtimeDir' => $this->runtimeDir, ]); } /** * Get the current PsySH config file. * * If a `configFile` option was passed to the Configuration constructor, * this file will be returned. If not, all possible config directories will * be searched, and the first `config.php` or `rc.php` file which exists * will be returned. * * If you're trying to decide where to put your config file, pick * * ~/.config/psysh/config.php * * @return string|null */ public function getConfigFile() { if (isset($this->configFile)) { return $this->configFile; } $files = $this->configPaths->configFiles(['config.php', 'rc.php']); if (!empty($files)) { if ($this->warnOnMultipleConfigs && \count($files) > 1) { $msg = \sprintf('Multiple configuration files found: %s. Using %s', \implode(', ', $files), $files[0]); \trigger_error($msg, \E_USER_NOTICE); } return $files[0]; } } /** * Get the local PsySH config file. * * Searches for a project specific config file `.psysh.php` in the current * working directory. * * @return string|null */ public function getLocalConfigFile() { $localConfig = \getcwd().'/.psysh.php'; if (@\is_file($localConfig)) { return $localConfig; } } /** * Load configuration values from an array of options. * * @param array $options */ public function loadConfig(array $options) { foreach (self::$AVAILABLE_OPTIONS as $option) { if (isset($options[$option])) { $method = 'set'.\ucfirst($option); $this->$method($options[$option]); } } // legacy `tabCompletion` option if (isset($options['tabCompletion'])) { $msg = '`tabCompletion` is deprecated; use `useTabCompletion` instead.'; @\trigger_error($msg, \E_USER_DEPRECATED); $this->setUseTabCompletion($options['tabCompletion']); } foreach (['commands', 'matchers', 'casters'] as $option) { if (isset($options[$option])) { $method = 'add'.\ucfirst($option); $this->$method($options[$option]); } } // legacy `tabCompletionMatchers` option if (isset($options['tabCompletionMatchers'])) { $msg = '`tabCompletionMatchers` is deprecated; use `matchers` instead.'; @\trigger_error($msg, \E_USER_DEPRECATED); $this->addMatchers($options['tabCompletionMatchers']); } } /** * Load a configuration file (default: `$HOME/.config/psysh/config.php`). * * This configuration instance will be available to the config file as $config. * The config file may directly manipulate the configuration, or may return * an array of options which will be merged with the current configuration. * * @throws \InvalidArgumentException if the config file does not exist or returns a non-array result * * @param string $file */ public function loadConfigFile(string $file) { if (!\is_file($file)) { throw new \InvalidArgumentException(\sprintf('Invalid configuration file specified, %s does not exist', $file)); } $__psysh_config_file__ = $file; $load = function ($config) use ($__psysh_config_file__) { $result = require $__psysh_config_file__; if ($result !== 1) { return $result; } }; $result = $load($this); if (!empty($result)) { if (\is_array($result)) { $this->loadConfig($result); } else { throw new \InvalidArgumentException('Psy Shell configuration must return an array of options'); } } } /** * Set files to be included by default at the start of each shell session. * * @param array $includes */ public function setDefaultIncludes(array $includes = []) { $this->defaultIncludes = $includes; } /** * Get files to be included by default at the start of each shell session. * * @return string[] */ public function getDefaultIncludes(): array { return $this->defaultIncludes ?: []; } /** * Set the shell's config directory location. * * @param string $dir */ public function setConfigDir(string $dir) { $this->configDir = (string) $dir; $this->configPaths->overrideDirs([ 'configDir' => $this->configDir, 'dataDir' => $this->dataDir, 'runtimeDir' => $this->runtimeDir, ]); } /** * Get the current configuration directory, if any is explicitly set. * * @return string|null */ public function getConfigDir() { return $this->configDir; } /** * Set the shell's data directory location. * * @param string $dir */ public function setDataDir(string $dir) { $this->dataDir = (string) $dir; $this->configPaths->overrideDirs([ 'configDir' => $this->configDir, 'dataDir' => $this->dataDir, 'runtimeDir' => $this->runtimeDir, ]); } /** * Get the current data directory, if any is explicitly set. * * @return string|null */ public function getDataDir() { return $this->dataDir; } /** * Set the shell's temporary directory location. * * @param string $dir */ public function setRuntimeDir(string $dir) { $this->runtimeDir = (string) $dir; $this->configPaths->overrideDirs([ 'configDir' => $this->configDir, 'dataDir' => $this->dataDir, 'runtimeDir' => $this->runtimeDir, ]); } /** * Get the shell's temporary directory location. * * Defaults to `/psysh` inside the system's temp dir unless explicitly * overridden. * * @throws RuntimeException if no temporary directory is set and it is not possible to create one */ public function getRuntimeDir(): string { $runtimeDir = $this->configPaths->runtimeDir(); if (!\is_dir($runtimeDir)) { if (!@\mkdir($runtimeDir, 0700, true)) { throw new RuntimeException(\sprintf('Unable to create PsySH runtime directory. Make sure PHP is able to write to %s in order to continue.', \dirname($runtimeDir))); } } return $runtimeDir; } /** * Set the readline history file path. * * @param string $file */ public function setHistoryFile(string $file) { $this->historyFile = ConfigPaths::touchFileWithMkdir($file); } /** * Get the readline history file path. * * Defaults to `/history` inside the shell's base config dir unless * explicitly overridden. */ public function getHistoryFile(): string { if (isset($this->historyFile)) { return $this->historyFile; } $files = $this->configPaths->configFiles(['psysh_history', 'history']); if (!empty($files)) { if ($this->warnOnMultipleConfigs && \count($files) > 1) { $msg = \sprintf('Multiple history files found: %s. Using %s', \implode(', ', $files), $files[0]); \trigger_error($msg, \E_USER_NOTICE); } $this->setHistoryFile($files[0]); } else { // fallback: create our own history file $this->setHistoryFile($this->configPaths->currentConfigDir().'/psysh_history'); } return $this->historyFile; } /** * Set the readline max history size. * * @param int $value */ public function setHistorySize(int $value) { $this->historySize = (int) $value; } /** * Get the readline max history size. * * @return int */ public function getHistorySize() { return $this->historySize; } /** * Sets whether readline erases old duplicate history entries. * * @param bool $value */ public function setEraseDuplicates(bool $value) { $this->eraseDuplicates = (bool) $value; } /** * Get whether readline erases old duplicate history entries. * * @return bool|null */ public function getEraseDuplicates() { return $this->eraseDuplicates; } /** * Get a temporary file of type $type for process $pid. * * The file will be created inside the current temporary directory. * * @see self::getRuntimeDir * * @param string $type * @param int $pid * * @return string Temporary file name */ public function getTempFile(string $type, int $pid): string { return \tempnam($this->getRuntimeDir(), $type.'_'.$pid.'_'); } /** * Get a filename suitable for a FIFO pipe of $type for process $pid. * * The pipe will be created inside the current temporary directory. * * @param string $type * @param int $pid * * @return string Pipe name */ public function getPipe(string $type, int $pid): string { return \sprintf('%s/%s_%s', $this->getRuntimeDir(), $type, $pid); } /** * Check whether this PHP instance has Readline available. * * @return bool True if Readline is available */ public function hasReadline(): bool { return $this->hasReadline; } /** * Enable or disable Readline usage. * * @param bool $useReadline */ public function setUseReadline(bool $useReadline) { $this->useReadline = (bool) $useReadline; } /** * Check whether to use Readline. * * If `setUseReadline` as been set to true, but Readline is not actually * available, this will return false. * * @return bool True if the current Shell should use Readline */ public function useReadline(): bool { return isset($this->useReadline) ? ($this->hasReadline && $this->useReadline) : $this->hasReadline; } /** * Set the Psy Shell readline service. * * @param Readline\Readline $readline */ public function setReadline(Readline\Readline $readline) { $this->readline = $readline; } /** * Get the Psy Shell readline service. * * By default, this service uses (in order of preference): * * * GNU Readline * * Libedit * * A transient array-based readline emulation. * * @return Readline\Readline */ public function getReadline(): Readline\Readline { if (!isset($this->readline)) { $className = $this->getReadlineClass(); $this->readline = new $className( $this->getHistoryFile(), $this->getHistorySize(), $this->getEraseDuplicates() ); } return $this->readline; } /** * Get the appropriate Readline implementation class name. * * @see self::getReadline */ private function getReadlineClass(): string { if ($this->useReadline()) { if (Readline\GNUReadline::isSupported()) { return Readline\GNUReadline::class; } elseif (Readline\Libedit::isSupported()) { return Readline\Libedit::class; } } if (Readline\Userland::isSupported()) { return Readline\Userland::class; } return Readline\Transient::class; } /** * Enable or disable bracketed paste. * * Note that this only works with readline (not libedit) integration for now. * * @param bool $useBracketedPaste */ public function setUseBracketedPaste(bool $useBracketedPaste) { $this->useBracketedPaste = (bool) $useBracketedPaste; } /** * Check whether to use bracketed paste with readline. * * When this works, it's magical. Tabs in pastes don't try to autcomplete. * Newlines in paste don't execute code until you get to the end. It makes * readline act like you'd expect when pasting. * * But it often (usually?) does not work. And when it doesn't, it just spews * escape codes all over the place and generally makes things ugly :( * * If `useBracketedPaste` has been set to true, but the current readline * implementation is anything besides GNU readline, this will return false. * * @return bool True if the shell should use bracketed paste */ public function useBracketedPaste(): bool { $readlineClass = $this->getReadlineClass(); return $this->useBracketedPaste && $readlineClass::supportsBracketedPaste(); // @todo mebbe turn this on by default some day? // return $readlineClass::supportsBracketedPaste() && $this->useBracketedPaste !== false; } /** * Check whether this PHP instance has Pcntl available. * * @return bool True if Pcntl is available */ public function hasPcntl(): bool { return $this->hasPcntl; } /** * Enable or disable Pcntl usage. * * @param bool $usePcntl */ public function setUsePcntl(bool $usePcntl) { $this->usePcntl = (bool) $usePcntl; } /** * Check whether to use Pcntl. * * If `setUsePcntl` has been set to true, but Pcntl is not actually * available, this will return false. * * @return bool True if the current Shell should use Pcntl */ public function usePcntl(): bool { if (!isset($this->usePcntl)) { // Unless pcntl is explicitly *enabled*, don't use it while XDebug is debugging. // See https://github.com/bobthecow/psysh/issues/742 if (\function_exists('xdebug_is_debugger_active') && \xdebug_is_debugger_active()) { return false; } return $this->hasPcntl; } return $this->hasPcntl && $this->usePcntl; } /** * Check whether to use raw output. * * This is set by the --raw-output (-r) flag, and really only makes sense * when non-interactive, e.g. executing stdin. * * @return bool true if raw output is enabled */ public function rawOutput(): bool { return $this->rawOutput; } /** * Enable or disable raw output. * * @param bool $rawOutput */ public function setRawOutput(bool $rawOutput) { $this->rawOutput = (bool) $rawOutput; } /** * Enable or disable strict requirement of semicolons. * * @see self::requireSemicolons() * * @param bool $requireSemicolons */ public function setRequireSemicolons(bool $requireSemicolons) { $this->requireSemicolons = (bool) $requireSemicolons; } /** * Check whether to require semicolons on all statements. * * By default, PsySH will automatically insert semicolons at the end of * statements if they're missing. To strictly require semicolons, set * `requireSemicolons` to true. */ public function requireSemicolons(): bool { return $this->requireSemicolons; } /** * Enable or disable strict types enforcement. */ public function setStrictTypes($strictTypes) { $this->strictTypes = (bool) $strictTypes; } /** * Check whether to enforce strict types. */ public function strictTypes(): bool { return $this->strictTypes; } /** * Enable or disable Unicode in PsySH specific output. * * Note that this does not disable Unicode output in general, it just makes * it so PsySH won't output any itself. * * @param bool $useUnicode */ public function setUseUnicode(bool $useUnicode) { $this->useUnicode = (bool) $useUnicode; } /** * Check whether to use Unicode in PsySH specific output. * * Note that this does not disable Unicode output in general, it just makes * it so PsySH won't output any itself. */ public function useUnicode(): bool { if (isset($this->useUnicode)) { return $this->useUnicode; } // @todo detect `chsh` != 65001 on Windows and return false return true; } /** * Set the error logging level. * * @see self::errorLoggingLevel * * @param int $errorLoggingLevel */ public function setErrorLoggingLevel($errorLoggingLevel) { $this->errorLoggingLevel = (\E_ALL | \E_STRICT) & $errorLoggingLevel; } /** * Get the current error logging level. * * By default, PsySH will automatically log all errors, regardless of the * current `error_reporting` level. * * Set `errorLoggingLevel` to 0 to prevent logging non-thrown errors. Set it * to any valid error_reporting value to log only errors which match that * level. * * http://php.net/manual/en/function.error-reporting.php */ public function errorLoggingLevel(): int { return $this->errorLoggingLevel; } /** * Set a CodeCleaner service instance. * * @param CodeCleaner $cleaner */ public function setCodeCleaner(CodeCleaner $cleaner) { $this->cleaner = $cleaner; } /** * Get a CodeCleaner service instance. * * If none has been explicitly defined, this will create a new instance. */ public function getCodeCleaner(): CodeCleaner { if (!isset($this->cleaner)) { $this->cleaner = new CodeCleaner(null, null, null, $this->yolo(), $this->strictTypes()); } return $this->cleaner; } /** * Enable or disable running PsySH without input validation. * * You don't want this. */ public function setYolo($yolo) { $this->yolo = (bool) $yolo; } /** * Check whether to disable input validation. */ public function yolo(): bool { return $this->yolo; } /** * Enable or disable tab completion. * * @param bool $useTabCompletion */ public function setUseTabCompletion(bool $useTabCompletion) { $this->useTabCompletion = (bool) $useTabCompletion; } /** * @deprecated Call `setUseTabCompletion` instead * * @param bool $useTabCompletion */ public function setTabCompletion(bool $useTabCompletion) { @\trigger_error('`setTabCompletion` is deprecated; call `setUseTabCompletion` instead.', \E_USER_DEPRECATED); $this->setUseTabCompletion($useTabCompletion); } /** * Check whether to use tab completion. * * If `setUseTabCompletion` has been set to true, but readline is not * actually available, this will return false. * * @return bool True if the current Shell should use tab completion */ public function useTabCompletion(): bool { return isset($this->useTabCompletion) ? ($this->hasReadline && $this->useTabCompletion) : $this->hasReadline; } /** * @deprecated Call `useTabCompletion` instead */ public function getTabCompletion(): bool { @\trigger_error('`getTabCompletion` is deprecated; call `useTabCompletion` instead.', \E_USER_DEPRECATED); return $this->useTabCompletion(); } /** * Set the Shell Output service. * * @param ShellOutput $output */ public function setOutput(ShellOutput $output) { $this->output = $output; $this->pipedOutput = null; // Reset cached pipe info if (isset($this->theme)) { $output->setTheme($this->theme); } $this->applyFormatterStyles(); } /** * Get a Shell Output service instance. * * If none has been explicitly provided, this will create a new instance * with the configured verbosity and output pager supplied by self::getPager * * @see self::verbosity * @see self::getPager */ public function getOutput(): ShellOutput { if (!isset($this->output)) { $this->setOutput(new ShellOutput( $this->getOutputVerbosity(), null, null, $this->getPager() ?: null, $this->theme() )); // This is racy because `getOutputDecorated` needs access to the // output stream to figure out if it's piped or not, so create it // first, then update after we have a stream. $decorated = $this->getOutputDecorated(); if ($decorated !== null) { $this->output->setDecorated($decorated); } } return $this->output; } /** * Get the decoration (i.e. color) setting for the Shell Output service. * * @return bool|null 3-state boolean corresponding to the current color mode */ public function getOutputDecorated() { switch ($this->colorMode()) { case self::COLOR_MODE_FORCED: return true; case self::COLOR_MODE_DISABLED: return false; case self::COLOR_MODE_AUTO: default: return $this->outputIsPiped() ? false : null; } } /** * Get the interactive setting for shell input. */ public function getInputInteractive(): bool { switch ($this->interactiveMode()) { case self::INTERACTIVE_MODE_FORCED: return true; case self::INTERACTIVE_MODE_DISABLED: return false; case self::INTERACTIVE_MODE_AUTO: default: return !$this->inputIsPiped(); } } /** * Set the OutputPager service. * * If a string is supplied, a ProcOutputPager will be used which shells out * to the specified command. * * `cat` is special-cased to use the PassthruPager directly. * * @throws \InvalidArgumentException if $pager is not a string or OutputPager instance * * @param string|OutputPager|false $pager */ public function setPager($pager) { if ($pager === null || $pager === false || $pager === 'cat') { $pager = false; } if ($pager !== false && !\is_string($pager) && !$pager instanceof OutputPager) { throw new \InvalidArgumentException('Unexpected pager instance'); } $this->pager = $pager; } /** * Get an OutputPager instance or a command for an external Proc pager. * * If no Pager has been explicitly provided, and Pcntl is available, this * will default to `cli.pager` ini value, falling back to `which less`. * * @return string|OutputPager|false */ public function getPager() { if (!isset($this->pager) && $this->usePcntl()) { if (\getenv('TERM') === 'dumb') { return false; } if ($pager = \ini_get('cli.pager')) { // use the default pager $this->pager = $pager; } elseif ($less = $this->configPaths->which('less')) { // check for the presence of less... // n.b. The busybox less implementation is a bit broken, so // let's not use it by default. // // See https://github.com/bobthecow/psysh/issues/778 $link = @\readlink($less); if ($link !== false && \strpos($link, 'busybox') !== false) { return false; } $this->pager = $less.' -R -F -X'; } } return $this->pager; } /** * Set the Shell AutoCompleter service. * * @param AutoCompleter $autoCompleter */ public function setAutoCompleter(AutoCompleter $autoCompleter) { $this->autoCompleter = $autoCompleter; } /** * Get an AutoCompleter service instance. */ public function getAutoCompleter(): AutoCompleter { if (!isset($this->autoCompleter)) { $this->autoCompleter = new AutoCompleter(); } return $this->autoCompleter; } /** * @deprecated Nothing should be using this anymore */ public function getTabCompletionMatchers(): array { @\trigger_error('`getTabCompletionMatchers` is no longer used.', \E_USER_DEPRECATED); return []; } /** * Add tab completion matchers to the AutoCompleter. * * This will buffer new matchers in the event that the Shell has not yet * been instantiated. This allows the user to specify matchers in their * config rc file, despite the fact that their file is needed in the Shell * constructor. * * @param array $matchers */ public function addMatchers(array $matchers) { $this->newMatchers = \array_merge($this->newMatchers, $matchers); if (isset($this->shell)) { $this->doAddMatchers(); } } /** * Internal method for adding tab completion matchers. This will set any new * matchers once a Shell is available. */ private function doAddMatchers() { if (!empty($this->newMatchers)) { $this->shell->addMatchers($this->newMatchers); $this->newMatchers = []; } } /** * @deprecated Use `addMatchers` instead * * @param array $matchers */ public function addTabCompletionMatchers(array $matchers) { @\trigger_error('`addTabCompletionMatchers` is deprecated; call `addMatchers` instead.', \E_USER_DEPRECATED); $this->addMatchers($matchers); } /** * Add commands to the Shell. * * This will buffer new commands in the event that the Shell has not yet * been instantiated. This allows the user to specify commands in their * config rc file, despite the fact that their file is needed in the Shell * constructor. * * @param array $commands */ public function addCommands(array $commands) { $this->newCommands = \array_merge($this->newCommands, $commands); if (isset($this->shell)) { $this->doAddCommands(); } } /** * Internal method for adding commands. This will set any new commands once * a Shell is available. */ private function doAddCommands() { if (!empty($this->newCommands)) { $this->shell->addCommands($this->newCommands); $this->newCommands = []; } } /** * Set the Shell backreference and add any new commands to the Shell. * * @param Shell $shell */ public function setShell(Shell $shell) { $this->shell = $shell; $this->doAddCommands(); $this->doAddMatchers(); } /** * Set the PHP manual database file. * * This file should be an SQLite database generated from the phpdoc source * with the `bin/build_manual` script. * * @param string $filename */ public function setManualDbFile(string $filename) { $this->manualDbFile = (string) $filename; } /** * Get the current PHP manual database file. * * @return string|null Default: '~/.local/share/psysh/php_manual.sqlite' */ public function getManualDbFile() { if (isset($this->manualDbFile)) { return $this->manualDbFile; } $files = $this->configPaths->dataFiles(['php_manual.sqlite']); if (!empty($files)) { if ($this->warnOnMultipleConfigs && \count($files) > 1) { $msg = \sprintf('Multiple manual database files found: %s. Using %s', \implode(', ', $files), $files[0]); \trigger_error($msg, \E_USER_NOTICE); } return $this->manualDbFile = $files[0]; } } /** * Get a PHP manual database connection. * * @return \PDO|null */ public function getManualDb() { if (!isset($this->manualDb)) { $dbFile = $this->getManualDbFile(); if ($dbFile !== null && \is_file($dbFile)) { try { $this->manualDb = new \PDO('sqlite:'.$dbFile); } catch (\PDOException $e) { if ($e->getMessage() === 'could not find driver') { throw new RuntimeException('SQLite PDO driver not found', 0, $e); } else { throw $e; } } } } return $this->manualDb; } /** * Add an array of casters definitions. * * @param array $casters */ public function addCasters(array $casters) { $this->getPresenter()->addCasters($casters); } /** * Get the Presenter service. */ public function getPresenter(): Presenter { if (!isset($this->presenter)) { $this->presenter = new Presenter($this->getOutput()->getFormatter(), $this->forceArrayIndexes()); } return $this->presenter; } /** * Enable or disable warnings on multiple configuration or data files. * * @see self::warnOnMultipleConfigs() * * @param bool $warnOnMultipleConfigs */ public function setWarnOnMultipleConfigs(bool $warnOnMultipleConfigs) { $this->warnOnMultipleConfigs = (bool) $warnOnMultipleConfigs; } /** * Check whether to warn on multiple configuration or data files. * * By default, PsySH will use the file with highest precedence, and will * silently ignore all others. With this enabled, a warning will be emitted * (but not an exception thrown) if multiple configuration or data files * are found. * * This will default to true in a future release, but is false for now. */ public function warnOnMultipleConfigs(): bool { return $this->warnOnMultipleConfigs; } /** * Set the current color mode. * * @throws \InvalidArgumentException if the color mode isn't auto, forced or disabled * * @param string $colorMode */ public function setColorMode(string $colorMode) { $validColorModes = [ self::COLOR_MODE_AUTO, self::COLOR_MODE_FORCED, self::COLOR_MODE_DISABLED, ]; if (!\in_array($colorMode, $validColorModes)) { throw new \InvalidArgumentException('Invalid color mode: '.$colorMode); } $this->colorMode = $colorMode; } /** * Get the current color mode. */ public function colorMode(): string { return $this->colorMode; } /** * Set the shell's interactive mode. * * @throws \InvalidArgumentException if interactive mode isn't disabled, forced, or auto * * @param string $interactiveMode */ public function setInteractiveMode(string $interactiveMode) { $validInteractiveModes = [ self::INTERACTIVE_MODE_AUTO, self::INTERACTIVE_MODE_FORCED, self::INTERACTIVE_MODE_DISABLED, ]; if (!\in_array($interactiveMode, $validInteractiveModes)) { throw new \InvalidArgumentException('Invalid interactive mode: '.$interactiveMode); } $this->interactiveMode = $interactiveMode; } /** * Get the current interactive mode. */ public function interactiveMode(): string { return $this->interactiveMode; } /** * Set an update checker service instance. * * @param Checker $checker */ public function setChecker(Checker $checker) { $this->checker = $checker; } /** * Get an update checker service instance. * * If none has been explicitly defined, this will create a new instance. */ public function getChecker(): Checker { if (!isset($this->checker)) { $interval = $this->getUpdateCheck(); switch ($interval) { case Checker::ALWAYS: $this->checker = new GitHubChecker(); break; case Checker::DAILY: case Checker::WEEKLY: case Checker::MONTHLY: $checkFile = $this->getUpdateCheckCacheFile(); if ($checkFile === false) { $this->checker = new NoopChecker(); } else { $this->checker = new IntervalChecker($checkFile, $interval); } break; case Checker::NEVER: $this->checker = new NoopChecker(); break; } } return $this->checker; } /** * Get the current update check interval. * * One of 'always', 'daily', 'weekly', 'monthly' or 'never'. If none is * explicitly set, default to 'weekly'. */ public function getUpdateCheck(): string { return isset($this->updateCheck) ? $this->updateCheck : Checker::WEEKLY; } /** * Set the update check interval. * * @throws \InvalidArgumentException if the update check interval is unknown * * @param string $interval */ public function setUpdateCheck(string $interval) { $validIntervals = [ Checker::ALWAYS, Checker::DAILY, Checker::WEEKLY, Checker::MONTHLY, Checker::NEVER, ]; if (!\in_array($interval, $validIntervals)) { throw new \InvalidArgumentException('Invalid update check interval: '.$interval); } $this->updateCheck = $interval; } /** * Get a cache file path for the update checker. * * @return string|false Return false if config file/directory is not writable */ public function getUpdateCheckCacheFile() { return ConfigPaths::touchFileWithMkdir($this->configPaths->currentConfigDir().'/update_check.json'); } /** * Set the startup message. * * @param string $message */ public function setStartupMessage(string $message) { $this->startupMessage = $message; } /** * Get the startup message. * * @return string|null */ public function getStartupMessage() { return $this->startupMessage; } /** * Set the prompt. * * @deprecated The `prompt` configuration has been replaced by Themes and support will * eventually be removed. In the meantime, prompt is applied first by the Theme, then overridden * by any explicitly defined prompt. * * Note that providing a prompt but not a theme config will implicitly use the `classic` theme. */ public function setPrompt(string $prompt) { $this->prompt = $prompt; if (isset($this->theme)) { $this->theme->setPrompt($prompt); } } /** * Get the prompt. * * @return string|null */ public function getPrompt() { return $this->prompt; } /** * Get the force array indexes. */ public function forceArrayIndexes(): bool { return $this->forceArrayIndexes; } /** * Set the force array indexes. * * @param bool $forceArrayIndexes */ public function setForceArrayIndexes(bool $forceArrayIndexes) { $this->forceArrayIndexes = $forceArrayIndexes; } /** * Set the current output Theme. * * @param Theme|string|array $theme Theme (or Theme config) */ public function setTheme($theme) { if (!$theme instanceof Theme) { $theme = new Theme($theme); } $this->theme = $theme; if (isset($this->prompt)) { $this->theme->setPrompt($this->prompt); } if (isset($this->output)) { $this->output->setTheme($theme); $this->applyFormatterStyles(); } } /** * Get the current output Theme. */ public function theme(): Theme { if (!isset($this->theme)) { // If a prompt is explicitly set, and a theme is not, base it on the `classic` theme. $this->theme = $this->prompt ? new Theme('classic') : new Theme(); } if (isset($this->prompt)) { $this->theme->setPrompt($this->prompt); } return $this->theme; } /** * Set the shell output formatter styles. * * Accepts a map from style name to [fg, bg, options], for example: * * [ * 'error' => ['white', 'red', ['bold']], * 'warning' => ['black', 'yellow'], * ] * * Foreground, background or options can be null, or even omitted entirely. * * @deprecated The `formatterStyles` configuration has been replaced by Themes and support will * eventually be removed. In the meantime, styles are applied first by the Theme, then * overridden by any explicitly defined formatter styles. */ public function setFormatterStyles(array $formatterStyles) { foreach ($formatterStyles as $name => $style) { $this->formatterStyles[$name] = new OutputFormatterStyle(...$style); } if (isset($this->output)) { $this->applyFormatterStyles(); } } /** * Internal method for applying output formatter style customization. * * This is called on initialization of the shell output, and again if the * formatter styles config is updated. * * @deprecated The `formatterStyles` configuration has been replaced by Themes and support will * eventually be removed. In the meantime, styles are applied first by the Theme, then * overridden by any explicitly defined formatter styles. */ private function applyFormatterStyles() { $formatter = $this->output->getFormatter(); foreach ($this->formatterStyles as $name => $style) { $formatter->setStyle($name, $style); } $errorFormatter = $this->output->getErrorOutput()->getFormatter(); foreach (Theme::ERROR_STYLES as $name) { if (isset($this->formatterStyles[$name])) { $errorFormatter->setStyle($name, $this->formatterStyles[$name]); } } } /** * Get the configured output verbosity. */ public function verbosity(): string { return $this->verbosity; } /** * Set the shell output verbosity. * * Accepts OutputInterface verbosity constants. * * @throws \InvalidArgumentException if verbosity level is invalid * * @param string $verbosity */ public function setVerbosity(string $verbosity) { $validVerbosityLevels = [ self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE, self::VERBOSITY_VERY_VERBOSE, self::VERBOSITY_DEBUG, ]; if (!\in_array($verbosity, $validVerbosityLevels)) { throw new \InvalidArgumentException('Invalid verbosity level: '.$verbosity); } $this->verbosity = $verbosity; if (isset($this->output)) { $this->output->setVerbosity($this->getOutputVerbosity()); } } /** * Map the verbosity configuration to OutputInterface verbosity constants. * * @return int OutputInterface verbosity level */ public function getOutputVerbosity(): int { switch ($this->verbosity()) { case self::VERBOSITY_QUIET: return OutputInterface::VERBOSITY_QUIET; case self::VERBOSITY_VERBOSE: return OutputInterface::VERBOSITY_VERBOSE; case self::VERBOSITY_VERY_VERBOSE: return OutputInterface::VERBOSITY_VERY_VERBOSE; case self::VERBOSITY_DEBUG: return OutputInterface::VERBOSITY_DEBUG; case self::VERBOSITY_NORMAL: default: return OutputInterface::VERBOSITY_NORMAL; } } /** * Guess whether stdin is piped. * * This is mostly useful for deciding whether to use non-interactive mode. */ public function inputIsPiped(): bool { if ($this->pipedInput === null) { $this->pipedInput = \defined('STDIN') && self::looksLikeAPipe(\STDIN); } return $this->pipedInput; } /** * Guess whether shell output is piped. * * This is mostly useful for deciding whether to use non-decorated output. */ public function outputIsPiped(): bool { if ($this->pipedOutput === null) { $this->pipedOutput = self::looksLikeAPipe($this->getOutput()->getStream()); } return $this->pipedOutput; } /** * Guess whether an input or output stream is piped. * * @param resource|int $stream */ private static function looksLikeAPipe($stream): bool { if (\function_exists('posix_isatty')) { return !\posix_isatty($stream); } $stat = \fstat($stream); $mode = $stat['mode'] & 0170000; return $mode === 0010000 || $mode === 0040000 || $mode === 0100000 || $mode === 0120000; } }