From: Banana Date: Mon, 26 Dec 2022 23:40:34 +0000 (+0100) Subject: php 8 updates and cleanup X-Git-Tag: 2.8.1_20221231~9 X-Git-Url: http://91.132.146.200/gitweb/?a=commitdiff_plain;h=34b652244b4e8d4d6b0d13c0cf7ec25575465ef8;p=insipid.git php 8 updates and cleanup --- diff --git a/ChangeLog b/ChangeLog index 4863726..4010a89 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,8 @@ version 2.x - Deathwind Chapel () + + + Moved debug setting into config + + Updated shellcommand class and added the link to the github repo + version 2.8 - Wastelands (2022-12-10) + Fixed documentation diff --git a/README b/README index c01abe6..bcb2656 100644 --- a/README +++ b/README @@ -9,4 +9,4 @@ https://bulma.io/ https://github.com/PHPMailer/PHPMailer https://ionicons.com/ https://github.com/ifsnop/mysqldump-php -shellcommand by Michael Härtl +https://github.com/mikehaertl/php-shellcommand diff --git a/TODO b/TODO index e242914..1d18e70 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,7 @@ TODO / Feature list ++ mysql dump to be replaced with https://github.com/druidfi/mysqldump-php +++ update readme too ++ update google pagespeed endpoint + view table really still needed? + stats cleanup. Management functions should be standalone + theme support diff --git a/documentation/update.txt b/documentation/update.txt index fe7d00e..d2d3820 100644 --- a/documentation/update.txt +++ b/documentation/update.txt @@ -1,6 +1,10 @@ If you are updating from a previous version make sure every update info from the version your are updating from is done. +## version 2.9 ++ Added debug setting into config. See config.default.php for DEBUG constant. + Add it to your local config.php + ## version 2.8 Wastelands + Nothing. diff --git a/webroot/config.default.php b/webroot/config.default.php index 1c0cb00..2dcd55f 100644 --- a/webroot/config.default.php +++ b/webroot/config.default.php @@ -79,3 +79,6 @@ const EMAIL_REPLY_BACK_SUBJECT = 'Insipid email import response'; # Use wkhtmltopdf to create a whole page screenshot of a given link const WKHTMLTOPDF_USE = false; const WKHTMLTOPDF_COMMAND = '/absolute/path/to/wkhtmltoimage'; + +# debug +const DEBUG = true; diff --git a/webroot/index.php b/webroot/index.php index 3ec1842..40ead7c 100644 --- a/webroot/index.php +++ b/webroot/index.php @@ -32,7 +32,7 @@ ini_set('error_reporting',-1); // E_ALL & E_STRICT # time settings date_default_timezone_set('Europe/Berlin'); -define('DEBUG',false); +require('config.php'); ## check request $_urlToParse = filter_var($_SERVER['QUERY_STRING'],FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW); @@ -53,7 +53,6 @@ else { ini_set('display_errors',false); } -require('config.php'); require('lib/summoner.class.php'); require('lib/management.class.php'); require('lib/tag.class.php'); diff --git a/webroot/lib/category.class.php b/webroot/lib/category.class.php index 6e5f2da..906b9c4 100644 --- a/webroot/lib/category.class.php +++ b/webroot/lib/category.class.php @@ -3,7 +3,7 @@ * Insipid * Personal web-bookmark-system * - * Copyright 2016-2021 Johannes Keßler + * Copyright 2016-2022 Johannes Keßler * * Development starting from 2011: Johannes Keßler * https://www.bananas-playground.net/projekt/insipid/ @@ -31,21 +31,24 @@ class Category { * the database object * @var mysqli */ - private $DB; + private mysqli $DB; /** * the current loaded category by DB id - * @var int + * @var string */ - private $_id; + private string $_id; /** * current loaded tag data * @var array */ - private $_data; + private array $_data; - public function __construct($databaseConnectionObject) { + /** + * @param mysqli $databaseConnectionObject + */ + public function __construct(mysqli $databaseConnectionObject) { $this->DB = $databaseConnectionObject; } @@ -82,7 +85,7 @@ class Category { } } else { - $ret=3; + $ret = 3; } } } @@ -92,10 +95,10 @@ class Category { /** * by given DB table id load all the info we need * - * @param int $id - * @return integer + * @param string $id + * @return string */ - public function initbyid(int $id): int { + public function initbyid(string $id): string { $this->_id = 0; if(!empty($id)) { @@ -116,10 +119,10 @@ class Category { /** * return all or data for given key on the current loaded category * - * @param bool $key - * @return string + * @param string $key + * @return string|array */ - public function getData(bool $key=false): string { + public function getData(string $key=''): string|array { $ret = $this->_data; if(!empty($key) && isset($this->_data[$key])) { @@ -132,10 +135,10 @@ class Category { /** * set the relation to the given link to the loaded category * - * @param integer $linkid + * @param string $linkid * @return void */ - public function setRelation(int $linkid) { + public function setRelation(string $linkid): void { if(!empty($linkid) && !empty($this->_id)) { $queryStr = "INSERT IGNORE INTO `".DB_PREFIX."_categoryrelation` SET `linkid` = '".$this->DB->real_escape_string($linkid)."', @@ -188,6 +191,7 @@ class Category { $this->DB->query($queryStr); $this->DB->commit(); + $ret = true; } catch (Exception $e) { if(DEBUG) { var_dump($e->getMessage()); @@ -207,7 +211,7 @@ class Category { * @param string $newValue * @return void */ - public function rename(string $newValue) { + public function rename(string $newValue): void { if(!empty($newValue)) { $queryStr = "UPDATE `".DB_PREFIX."_category` SET `name` = '".$this->DB->real_escape_string($newValue)."' diff --git a/webroot/lib/import-export.class.php b/webroot/lib/import-export.class.php index 819771f..642c077 100644 --- a/webroot/lib/import-export.class.php +++ b/webroot/lib/import-export.class.php @@ -3,7 +3,7 @@ * Insipid * Personal web-bookmark-system * - * Copyright 2016-2021 Johannes Keßler + * Copyright 2016-2022 Johannes Keßler * * Development starting from 2011: Johannes Keßler * https://www.bananas-playground.net/projekt/insipid/ @@ -35,15 +35,17 @@ class ImportExport { /** * @var XMLWriter The current memory xmlwriter */ - private $_currentXW; - - private $_xmlImportXSD = 'lib/xmlimport.xsd'; + private XMLWriter $_currentXW; + /** + * @var string + */ + private string $_xmlImportXSD = 'lib/xmlimport.xsd'; /** - * @var + * @var string */ - private $_uploadedData; + private string $_uploadedData; public function __construct() { } @@ -155,7 +157,7 @@ class ImportExport { * @throws Exception * @return void */ - public function loadImportFile(array $file) { + public function loadImportFile(array $file): void { if(!isset($file['name']) || !isset($file['type']) @@ -183,7 +185,7 @@ class ImportExport { if(!empty($this->_uploadedData)) { $_valid = $this->_validateXMLImport(); - if($_valid !== true) { + if(!empty($_valid)) { $this->_uploadedData = ''; throw new Exception('Invalid xml format: '.$_valid); } @@ -251,7 +253,7 @@ class ImportExport { * @param String $value * @return void */ - private function _elementFromKeyValue(string $name, string $key, string $value) { + private function _elementFromKeyValue(string $name, string $key, string $value): void { if(!empty($key) && !empty($value) && !empty($name)) { xmlwriter_start_element($this->_currentXW, $name); @@ -272,10 +274,10 @@ class ImportExport { * validate an import of a export xml with the * saved xsd file _xmlImportXSD * - * @return bool|string + * @return string */ - private function _validateXMLImport() { - $ret = false; + private function _validateXMLImport(): string { + $ret = ''; $xmlReader = new XMLReader(); $xmlReader->XML($this->_uploadedData); if(!empty($xmlReader)) { @@ -286,7 +288,6 @@ class ImportExport { $ret = $this->_xmlErrors(); break; } else { - $ret = true; break; } } diff --git a/webroot/lib/link.class.php b/webroot/lib/link.class.php index 38347ce..0354bd6 100644 --- a/webroot/lib/link.class.php +++ b/webroot/lib/link.class.php @@ -3,7 +3,7 @@ * Insipid * Personal web-bookmark-system * - * Copyright 2016-2021 Johannes Keßler + * Copyright 2016-2022 Johannes Keßler * * Development starting from 2011: Johannes Keßler * https://www.bananas-playground.net/projekt/insipid/ @@ -36,20 +36,19 @@ class Link { * * @var mysqli */ - private $DB; + private mysqli $DB; /** * the current loaded link data * * @var array */ - private $_data; + private array $_data; /** * Link constructor. * * @param mysqli $databaseConnectionObject - * @return void */ public function __construct(mysqli $databaseConnectionObject) { $this->DB = $databaseConnectionObject; @@ -143,10 +142,10 @@ class Link { /** * return all or data for given key on the current loaded link * - * @param bool $key - * @return array|mixed + * @param string $key + * @return string|array */ - public function getData($key = false): array { + public function getData(string $key = ''): string|array { $ret = $this->_data; if (!empty($key) && isset($this->_data[$key])) { @@ -161,7 +160,7 @@ class Link { * * @return void */ - public function reload() { + public function reload(): void { $this->load($this->_data['hash']); } @@ -170,10 +169,10 @@ class Link { * * @param array $data * @param bool $returnId - * @return int + * @return string */ - public function create(array $data, $returnId = false): int { - $ret = 0; + public function create(array $data, bool $returnId = false): string { + $ret = ''; if (!isset($data['link']) || empty($data['link'])) return $ret; if (!isset($data['hash']) || empty($data['hash'])) return $ret; @@ -217,7 +216,6 @@ class Link { * @return boolean */ public function update(array $data): bool { - $ret = false; if (isset($data['title']) && !empty($data['title']) && !empty($this->_data)) { @@ -259,8 +257,8 @@ class Link { $catObj = new Category($this->DB); $tagObj = new Tag($this->DB); // clean the relations first - $this->_removeTagRelation(false); - $this->_removeCategoryRelation(false); + $this->_removeTagRelation(); + $this->_removeCategoryRelation(); if (!empty($catArr)) { foreach ($catArr as $c) { @@ -329,7 +327,6 @@ class Link { } } - $ret = true; } else { $this->DB->rollback(); @@ -347,9 +344,9 @@ class Link { * * @return void */ - public function deleteRelations() { - $this->_removeTagRelation(false); - $this->_removeCategoryRelation(false); + public function deleteRelations(): void { + $this->_removeTagRelation(); + $this->_removeCategoryRelation(); $this->_deleteImage(); $this->_deleteSnapshot(); $this->_deletePageScreenshot(); @@ -361,7 +358,7 @@ class Link { * * @return void */ - private function _tags() { + private function _tags(): void { $ret = array(); if (!empty($this->_data['hash'])) { @@ -389,7 +386,7 @@ class Link { * * @return void */ - private function _categories() { + private function _categories(): void { $ret = array(); if (!empty($this->_data['hash'])) { @@ -413,21 +410,22 @@ class Link { /** * remove all or given tag relation to the current loaded link * - * @param boolean|integer $tagid + * @param string $tagid * @return void */ - private function _removeTagRelation($tagid) { + private function _removeTagRelation(string $tagid = ''): void { if (!empty($this->_data['id'])) { - $queryStr = false; - if ($tagid === false) { - $queryStr = "DELETE - FROM `" . DB_PREFIX . "_tagrelation` - WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "'"; - } elseif (is_numeric($tagid)) { + $queryStr = ''; + if (is_numeric($tagid)) { $queryStr = "DELETE FROM `" . DB_PREFIX . "_tagrelation` WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "' AND `tagid` = '" . $this->DB->real_escape_string($tagid) . "'"; + + } else { + $queryStr = "DELETE + FROM `" . DB_PREFIX . "_tagrelation` + WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "'"; } if (!empty($queryStr)) { $this->DB->query($queryStr); @@ -438,21 +436,21 @@ class Link { /** * remove all or given category relation to the current loaded link * - * @param boolean|integer $categoryid + * @param string $categoryid * @return void */ - private function _removeCategoryRelation($categoryid) { + private function _removeCategoryRelation(string $categoryid=''): void { if (!empty($this->_data['id'])) { - $queryStr = false; - if ($categoryid === false) { - $queryStr = "DELETE - FROM `" . DB_PREFIX . "_categoryrelation` - WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "'"; - } elseif (is_numeric($categoryid)) { + $queryStr = ''; + if (is_numeric($categoryid)) { $queryStr = "DELETE FROM `" . DB_PREFIX . "_categoryrelation` WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "' AND `categoryid` = '" . $this->DB->real_escape_string($categoryid) . "'"; + } else { + $queryStr = "DELETE + FROM `" . DB_PREFIX . "_categoryrelation` + WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "'"; } if (!empty($queryStr)) { $this->DB->query($queryStr); @@ -466,7 +464,7 @@ class Link { * * @return void */ - private function _image() { + private function _image(): void { if (!empty($this->_data['hash'])) { $this->_data['imageToShow'] = $this->_data['image']; $image = ABSOLUTE_PATH.'/'.LOCAL_STORAGE.'/thumbnail-'.$this->_data['hash'].'.jpg'; @@ -483,7 +481,7 @@ class Link { * * @return void */ - private function _snapshot() { + private function _snapshot(): void { if (!empty($this->_data['hash'])) { $snapshot = ABSOLUTE_PATH.'/'.LOCAL_STORAGE.'/snapshot-'.$this->_data['hash'].'.jpg'; if (file_exists($snapshot)) { @@ -499,7 +497,7 @@ class Link { * * @return void */ - private function _pageScreenshot() { + private function _pageScreenshot(): void { if (!empty($this->_data['hash'])) { $pagescreenshot = ABSOLUTE_PATH.'/'.LOCAL_STORAGE.'/pagescreenshot-'.$this->_data['hash'].'.jpg'; if (file_exists($pagescreenshot)) { @@ -514,7 +512,7 @@ class Link { * * @return void */ - private function _deleteImage() { + private function _deleteImage(): void { if (!empty($this->_data['hash']) && !empty($this->_data['imageToShow'])) { $image = ABSOLUTE_PATH.'/'.$this->_data['imageToShow']; if (file_exists($image)) { @@ -528,7 +526,7 @@ class Link { * * @return void */ - private function _deleteSnapshot() { + private function _deleteSnapshot(): void { if (!empty($this->_data['hash']) && !empty($this->_data['snapshotLink'])) { $snapshot = LOCAL_STORAGE.'/snapshot-'.$this->_data['hash'].'.jpg'; if (file_exists($snapshot)) { @@ -542,7 +540,7 @@ class Link { * * @return void */ - private function _deletePageScreenshot() { + private function _deletePageScreenshot(): void { if (!empty($this->_data['hash']) && !empty($this->_data['pagescreenshotLink'])) { $pagescreenshot = LOCAL_STORAGE.'/pagescreenshot-'.$this->_data['hash'].'.jpg'; if (file_exists($pagescreenshot)) { @@ -556,7 +554,7 @@ class Link { * * @return void */ - private function _private() { + private function _private(): void { if (!empty($this->_data['status']) && $this->_data['status'] == "1") { $this->_data['private'] = "1"; } diff --git a/webroot/lib/management.class.php b/webroot/lib/management.class.php index bed89d9..4358c20 100644 --- a/webroot/lib/management.class.php +++ b/webroot/lib/management.class.php @@ -3,7 +3,7 @@ * Insipid * Personal web-bookmark-system * - * Copyright 2016-2021 Johannes Keßler + * Copyright 2016-2022 Johannes Keßler * * Development starting from 2011: Johannes Keßler * https://www.bananas-playground.net/projekt/insipid/ @@ -31,6 +31,9 @@ */ class Management { + /** + * Default value + */ const LINK_QUERY_STATUS = 2; /** @@ -38,14 +41,14 @@ class Management { * * @var mysqli */ - private $DB; + private mysqli $DB; /** * Type of links based on status to show * - * @var bool + * @var int */ - private $_queryStatus = self::LINK_QUERY_STATUS; + private int $_queryStatus = self::LINK_QUERY_STATUS; /** @@ -64,7 +67,7 @@ class Management { * @param boolean $bool * @return void */ - public function setShowPrivate(bool $bool) { + public function setShowPrivate(bool $bool): void { $this->_queryStatus = self::LINK_QUERY_STATUS; if($bool === true) { $this->_queryStatus = 1; @@ -77,7 +80,7 @@ class Management { * @param boolean $bool * @return void */ - public function setShowAwm(bool $bool) { + public function setShowAwm(bool $bool): void { $this->_queryStatus = self::LINK_QUERY_STATUS; if($bool === true) { $this->_queryStatus = 3; @@ -89,11 +92,11 @@ class Management { * optional limit * optional stats * - * @param bool|int $limit + * @param int $limit * @param bool $stats * @return array */ - public function categories($limit=false, $stats=false): array { + public function categories(int $limit=0, bool $stats=false): array { $ret = array(); $statsInfo = array(); @@ -140,11 +143,11 @@ class Management { * optional limit * optional stats * - * @param bool|int $limit + * @param int $limit * @param bool $stats * @return array */ - public function tags($limit=false, $stats=false): array { + public function tags(int $limit=0, bool $stats=false): array { $ret = array(); $statsInfo = array(); @@ -191,7 +194,7 @@ class Management { * @param int $limit * @return array */ - public function latestLinks($limit=5): array { + public function latestLinks(int $limit=5): array { $ret = array(); $queryStr = "SELECT `title`, `link` FROM `".DB_PREFIX."_link` AS t"; @@ -216,7 +219,7 @@ class Management { * @param int $limit * @return array */ - public function randomLink($limit=1): array { + public function randomLink(int $limit=1): array { $ret = array(); $queryStr = "SELECT `title`, `link`, `hash` FROM `".DB_PREFIX."_link` AS t"; @@ -233,7 +236,13 @@ class Management { return $ret; } - public function randomCategory($limit=1): array { + /** + * Get a random category + * + * @param int $limit + * @return array + */ + public function randomCategory(int $limit=1): array { $ret = array(); $queryStr = "SELECT `id`, `name` FROM `".DB_PREFIX."_category`"; @@ -249,7 +258,13 @@ class Management { return $ret; } - public function randomTag($limit=1): array { + /** + * Get a random tag + * + * @param int $limit + * @return array + */ + public function randomTag(int $limit=1): array { $ret = array(); $queryStr = "SELECT `id`, `name` FROM `".DB_PREFIX."_tag`"; @@ -277,7 +292,7 @@ class Management { foreach($categories as $k=>$v) { $latestLink = $this->latestLinkForCategory($k); if(!empty($latestLink)) { - array_push($ret, array('created' => $latestLink[0]['created'], 'id' => $k, 'name' => $v['name'])); + $ret[] = array('created' => $latestLink[0]['created'], 'id' => $k, 'name' => $v['name']); } } @@ -291,11 +306,11 @@ class Management { * find all links by given category string or id. * Return array sorted by creation date DESC * - * @param int $id Category ID + * @param string $id Category ID * @param array $options Array with limit|offset|sort|sortDirection * @return array */ - public function linksByCategory(int $id, $options=array()): array { + public function linksByCategory(string $id, array $options=array()): array { $ret = array(); if(!isset($options['limit'])) $options['limit'] = 5; @@ -358,11 +373,11 @@ class Management { * find all links by given tag string or id. * Return array sorted by creation date DESC * - * @param int $id Tag id + * @param string $id Tag id * @param array $options Array with limit|offset|sort|sortDirection * @return array */ - public function linksByTag(int $id, $options=array()): array { + public function linksByTag(string $id, array $options=array()): array { $ret = array(); if(!isset($options['limit'])) $options['limit'] = 5; @@ -424,11 +439,11 @@ class Management { /** * return all links and Info we have from the combined view * - * @param bool|int $limit + * @param int $limit * @param bool $offset * @return array */ - public function links($limit=10,$offset=false): array { + public function links(int $limit=10, bool $offset=false): array { $ret = array(); $querySelect = "SELECT `hash`"; @@ -461,10 +476,10 @@ class Management { /** * return the latest added link for given category id * - * @param int $categoryid + * @param string $categoryid * @return array */ - public function latestLinkForCategory(int $categoryid): array { + public function latestLinkForCategory(string $categoryid): array { $ret = array(); if(!empty($categoryid) && is_numeric($categoryid)) { @@ -655,7 +670,7 @@ class Management { * @param bool $withObject An array with data and the link obj itself * @return array */ - public function loadLink(string $hash, $fullInfo=true, $withObject=false): array { + public function loadLink(string $hash, bool $fullInfo=true, bool $withObject=false): array { $ret = array(); if (!empty($hash)) { @@ -717,10 +732,10 @@ class Management { * Export given link for download as a xml file * * @param string $hash - * @param bool|Link $linkObj Use already existing link obj + * @param Link|null $linkObj Use already existing link obj * @return bool */ - public function exportLinkData(string $hash, $linkObj=false): bool { + public function exportLinkData(string $hash, Link $linkObj=null): bool { $ret = false; if (!empty($hash)) { @@ -793,11 +808,11 @@ class Management { * process the given xml file. Based on the export file * options are overwrite => true|false * - * @param string $file + * @param array $file * @param array $options * @return array */ - public function processImportFile(string $file, array $options): array { + public function processImportFile(array $file, array $options): array { $ret = array( 'status' => 'error', 'message' => 'Processing error' @@ -901,27 +916,20 @@ class Management { * @return string */ private function _decideLinkTypeForQuery(): string { - switch ($this->_queryStatus) { - case 1: - $ret = "t.status IN (2,1)"; - break; - case 3: - $ret = "t.status = 3"; - break; - - default: - $ret = "t.status = 2"; - } - return $ret; + return match ($this->_queryStatus) { + 1 => "t.status IN (2,1)", + 3 => "t.status = 3", + default => "t.status = 2", + }; } /** * Check if given id (not hash) exists in link database * - * @param integer $id + * @param string $id * @return bool */ - private function _linkExistsById($id): bool { + private function _linkExistsById(string $id): bool { $ret = false; if(!empty($id)) { diff --git a/webroot/lib/shellcommand.class.php b/webroot/lib/shellcommand.class.php index 34b4865..d9a4e03 100644 --- a/webroot/lib/shellcommand.class.php +++ b/webroot/lib/shellcommand.class.php @@ -1,552 +1,562 @@ addArg('--name=', "d'Artagnan"); + * if ($command->execute()) { + * echo $command->getOutput(); + * } else { + * echo $command->getError(); + * $exitCode = $command->getExitCode(); + * } + * ``` + * * @author Michael Härtl * @license http://www.opensource.org/licenses/MIT */ -class ShellCommand +class Command { - /** - * @var bool whether to escape any argument passed through `addArg()`. - * Default is `true`. - */ - public $escapeArgs = true; - - /** - * @var bool whether to escape the command passed to `setCommand()` or the - * constructor. This is only useful if `$escapeArgs` is `false`. Default - * is `false`. - */ - public $escapeCommand = false; - - /** - * @var bool whether to use `exec()` instead of `proc_open()`. This can be - * used on Windows system to workaround some quirks there. Note, that any - * errors from your command will be output directly to the PHP output - * stream. `getStdErr()` will also not work anymore and thus you also won't - * get the error output from `getError()` in this case. You also can't pass - * any environment variables to the command if this is enabled. Default is - * `false`. - */ - public $useExec = false; - - /** - * @var bool whether to capture stderr (2>&1) when `useExec` is true. This - * will try to redirect the stderr to stdout and provide the complete - * output of both in `getStdErr()` and `getError()`. Default is `true`. - */ - public $captureStdErr = true; - - /** - * @var string|null the initial working dir for `proc_open()`. Default is - * `null` for current PHP working dir. - */ - public $procCwd; - - /** - * @var array|null an array with environment variables to pass to - * `proc_open()`. Default is `null` for none. - */ - public $procEnv; - - /** - * @var array|null an array of other_options for `proc_open()`. Default is - * `null` for none. - */ - public $procOptions; - - /** - * @var bool|null whether to set the stdin/stdout/stderr streams to - * non-blocking mode when `proc_open()` is used. This allows to have huge - * inputs/outputs without making the process hang. The default is `null` - * which will enable the feature on Non-Windows systems. Set it to `true` - * or `false` to manually enable/disable it. It does not work on Windows. - */ - public $nonBlockingMode; - - /** - * @var int the time in seconds after which a command should be terminated. - * This only works in non-blocking mode. Default is `null` which means the - * process is never terminated. - */ - public $timeout; - - /** - * @var null|string the locale to temporarily set before calling - * `escapeshellargs()`. Default is `null` for none. - */ - public $locale; - - /** - * @var null|string|resource to pipe to standard input - */ - protected $_stdIn; - - /** - * @var string the command to execute - */ - protected $_command; - - /** - * @var array the list of command arguments - */ - protected $_args = array(); - - /** - * @var string the full command string to execute - */ - protected $_execCommand; - - /** - * @var string the stdout output - */ - protected $_stdOut = ''; - - /** - * @var string the stderr output - */ - protected $_stdErr = ''; - - /** - * @var int the exit code - */ - protected $_exitCode; - - /** - * @var string the error message - */ - protected $_error = ''; - - /** - * @var bool whether the command was successfully executed - */ - protected $_executed = false; - - /** - * @param string|array $options either a command string or an options array - * @see setOptions - */ - public function __construct($options = null) - { - if (is_array($options)) { - $this->setOptions($options); - } elseif (is_string($options)) { - $this->setCommand($options); - } - } - - /** - * @param array $options array of name => value options that should be - * applied to the object You can also pass options that use a setter, e.g. - * you can pass a `fileName` option which will be passed to - * `setFileName()`. - * @throws \Exception - * @return static for method chaining - */ - public function setOptions($options) - { - foreach ($options as $key => $value) { - if (property_exists($this, $key)) { - $this->$key = $value; - } else { - $method = 'set'.ucfirst($key); - if (method_exists($this, $method)) { - call_user_func(array($this,$method), $value); - } else { - throw new \Exception("Unknown configuration option '$key'"); - } - } - } - return $this; - } - - /** - * @param string $command the command or full command string to execute, - * like 'gzip' or 'gzip -d'. You can still call addArg() to add more - * arguments to the command. If $escapeCommand was set to true, the command - * gets escaped with escapeshellcmd(). - * @return static for method chaining - */ - public function setCommand($command) - { - if ($this->escapeCommand) { - $command = escapeshellcmd($command); - } - if ($this->getIsWindows()) { - // Make sure to switch to correct drive like "E:" first if we have - // a full path in command - if (isset($command[1]) && $command[1]===':') { - $position = 1; - // Could be a quoted absolute path because of spaces. - // i.e. "C:\Program Files (x86)\file.exe" - } elseif (isset($command[2]) && $command[2]===':') { - $position = 2; - } else { - $position = false; - } - - // Absolute path. If it's a relative path, let it slide. - if ($position) { - $command = sprintf( - $command[$position - 1] . ': && cd %s && %s', - escapeshellarg(dirname($command)), - escapeshellarg(basename($command)) - ); - } - } - $this->_command = $command; - return $this; - } - - /** - * @param string|resource $stdIn If set, the string will be piped to the - * command via standard input. This enables the same functionality as - * piping on the command line. It can also be a resource like a file - * handle or a stream in which case its content will be piped into the - * command like an input redirection. - * @return static for method chaining - */ - public function setStdIn($stdIn) { - $this->_stdIn = $stdIn; - return $this; - } - - /** - * @return string|null the command that was set through setCommand() or - * passed to the constructor. `null` if none. - */ - public function getCommand() - { - return $this->_command; - } - - /** - * @return string|bool the full command string to execute. If no command - * was set with setCommand() or passed to the constructor it will return - * `false`. - */ - public function getExecCommand() - { - if ($this->_execCommand===null) { - $command = $this->getCommand(); - if (!$command) { - $this->_error = 'Could not locate any executable command'; - return false; - } - $args = $this->getArgs(); - $this->_execCommand = $args ? $command.' '.$args : $command; - } - return $this->_execCommand; - } - - /** - * @param string $args the command arguments as string. Note that these - * will not get escaped! - * @return static for method chaining - */ - public function setArgs($args) - { - $this->_args = array($args); - return $this; - } - - /** - * @return string the command args that where set with setArgs() or added - * with addArg() separated by spaces - */ - public function getArgs() - { - return implode(' ', $this->_args); - } - - /** - * @param string $key the argument key to add e.g. `--feature` or - * `--name=`. If the key does not end with and `=`, the $value will be - * separated by a space, if any. Keys are not escaped unless $value is null - * and $escape is `true`. - * @param string|array|null $value the optional argument value which will - * get escaped if $escapeArgs is true. An array can be passed to add more - * than one value for a key, e.g. `addArg('--exclude', - * array('val1','val2'))` which will create the option `'--exclude' 'val1' - * 'val2'`. - * @param bool|null $escape if set, this overrides the $escapeArgs setting - * and enforces escaping/no escaping - * @return static for method chaining - */ - public function addArg($key, $value = null, $escape = null) - { - $doEscape = $escape !== null ? $escape : $this->escapeArgs; - $useLocale = $doEscape && $this->locale !== null; - - if ($useLocale) { - $locale = setlocale(LC_CTYPE, 0); // Returns current locale setting - setlocale(LC_CTYPE, $this->locale); - } - if ($value === null) { - $this->_args[] = $doEscape ? escapeshellarg($key) : $key; - } else { - if (substr($key, -1) === '=') { - $separator = '='; - $argKey = substr($key, 0, -1); - } else { - $separator = ' '; - $argKey = $key; - } - $argKey = $doEscape ? escapeshellarg($argKey) : $argKey; - - if (is_array($value)) { - $params = array(); - foreach ($value as $v) { - $params[] = $doEscape ? escapeshellarg($v) : $v; - } - $this->_args[] = $argKey . $separator . implode(' ', $params); - } else { - $this->_args[] = $argKey . $separator . - ($doEscape ? escapeshellarg($value) : $value); - } - } - if ($useLocale) { - setlocale(LC_CTYPE, $locale); - } - - return $this; - } - - /** - * @param bool $trim whether to `trim()` the return value. The default is `true`. - * @return string the command output (stdout). Empty if none. - */ - public function getOutput($trim = true) - { - return $trim ? trim($this->_stdOut) : $this->_stdOut; - } - - /** - * @param bool $trim whether to `trim()` the return value. The default is `true`. - * @return string the error message, either stderr or an internal message. - * Empty string if none. - */ - public function getError($trim = true) - { - return $trim ? trim($this->_error) : $this->_error; - } - - /** - * @param bool $trim whether to `trim()` the return value. The default is `true`. - * @return string the stderr output. Empty if none. - */ - public function getStdErr($trim = true) - { - return $trim ? trim($this->_stdErr) : $this->_stdErr; - } - - /** - * @return int|null the exit code or null if command was not executed yet - */ - public function getExitCode() - { - return $this->_exitCode; - } - - /** - * @return string whether the command was successfully executed - */ - public function getExecuted() - { - return $this->_executed; - } - - /** - * Execute the command - * - * @return bool whether execution was successful. If `false`, error details - * can be obtained from getError(), getStdErr() and getExitCode(). - */ - public function execute() - { - $command = $this->getExecCommand(); - - if (!$command) { - return false; - } - - if ($this->useExec) { - $execCommand = $this->captureStdErr ? "$command 2>&1" : $command; - exec($execCommand, $output, $this->_exitCode); - $this->_stdOut = implode("\n", $output); - if ($this->_exitCode !== 0) { - $this->_stdErr = $this->_stdOut; - $this->_error = empty($this->_stdErr) ? 'Command failed' : $this->_stdErr; - return false; - } - } else { - $isInputStream = $this->_stdIn !== null && - is_resource($this->_stdIn) && - in_array(get_resource_type($this->_stdIn), array('file', 'stream')); - $isInputString = is_string($this->_stdIn); - $hasInput = $isInputStream || $isInputString; - $hasTimeout = $this->timeout !== null && $this->timeout > 0; - - $descriptors = array( - 1 => array('pipe','w'), - 2 => array('pipe', $this->getIsWindows() ? 'a' : 'w'), - ); - if ($hasInput) { - $descriptors[0] = array('pipe', 'r'); - } - - - // Issue #20 Set non-blocking mode to fix hanging processes - $nonBlocking = $this->nonBlockingMode === null ? - !$this->getIsWindows() : $this->nonBlockingMode; - - $startTime = $hasTimeout ? time() : 0; - $process = proc_open($command, $descriptors, $pipes, $this->procCwd, $this->procEnv, $this->procOptions); - - if (is_resource($process)) { - - if ($nonBlocking) { - stream_set_blocking($pipes[1], false); - stream_set_blocking($pipes[2], false); - if ($hasInput) { - $writtenBytes = 0; - $isInputOpen = true; - stream_set_blocking($pipes[0], false); - if ($isInputStream) { - stream_set_blocking($this->_stdIn, false); - } - } - - // Due to the non-blocking streams we now have to check in - // a loop if the process is still running. We also need to - // ensure that all the pipes are written/read alternately - // until there's nothing left to write/read. - $isRunning = true; - while ($isRunning) { - $status = proc_get_status($process); - $isRunning = $status['running']; - - // We first write to stdIn if we have an input. For big - // inputs it will only write until the input buffer of - // the command is full (the command may now wait that - // we read the output buffers - see below). So we may - // have to continue writing in another cycle. - // - // After everything is written it's safe to close the - // input pipe. - if ($isRunning && $hasInput && $isInputOpen) { - if ($isInputStream) { - $written = stream_copy_to_stream($this->_stdIn, $pipes[0], 16 * 1024, $writtenBytes); - if ($written === false || $written === 0) { - $isInputOpen = false; - fclose($pipes[0]); - } else { - $writtenBytes += $written; - } - } else { - if ($writtenBytes < strlen($this->_stdIn)) { - $writtenBytes += fwrite($pipes[0], substr($this->_stdIn, $writtenBytes)); - } else { - $isInputOpen = false; - fclose($pipes[0]); - } - } - } - - // Read out the output buffers because if they are full - // the command may block execution. We do this even if - // $isRunning is `false`, because there could be output - // left in the buffers. - // - // The latter is only an assumption and needs to be - // verified - but it does not hurt either and works as - // expected. - // - while (($out = fgets($pipes[1])) !== false) { - $this->_stdOut .= $out; - } - while (($err = fgets($pipes[2])) !== false) { - $this->_stdErr .= $err; - } - - $runTime = $hasTimeout ? time() - $startTime : 0; - if ($isRunning && $hasTimeout && $runTime >= $this->timeout) { - // Only send a SIGTERM and handle status in the next cycle - proc_terminate($process); - } - - if (!$isRunning) { - $this->_exitCode = $status['exitcode']; - if ($this->_exitCode !== 0 && empty($this->_stdErr)) { - if ($status['stopped']) { - $signal = $status['stopsig']; - $this->_stdErr = "Command stopped by signal $signal"; - } elseif ($status['signaled']) { - $signal = $status['termsig']; - $this->_stdErr = "Command terminated by signal $signal"; - } else { - $this->_stdErr = 'Command unexpectedly terminated without error message'; - } - } - fclose($pipes[1]); - fclose($pipes[2]); - proc_close($process); - } else { - // The command is still running. Let's wait some - // time before we start the next cycle. - usleep(10000); - } - } - } else { - if ($hasInput) { - if ($isInputStream) { - stream_copy_to_stream($this->_stdIn, $pipes[0]); - } elseif ($isInputString) { - fwrite($pipes[0], $this->_stdIn); - } - fclose($pipes[0]); - } - $this->_stdOut = stream_get_contents($pipes[1]); - $this->_stdErr = stream_get_contents($pipes[2]); - fclose($pipes[1]); - fclose($pipes[2]); - $this->_exitCode = proc_close($process); - } - - if ($this->_exitCode !== 0) { - $this->_error = $this->_stdErr ? - $this->_stdErr : - "Failed without error message: $command (Exit code: {$this->_exitCode})"; - return false; - } - } else { - $this->_error = "Could not run command $command"; - return false; - } - } - - $this->_executed = true; - - return true; - } - - /** - * @return bool whether we are on a Windows OS - */ - public function getIsWindows() - { - return strncasecmp(PHP_OS, 'WIN', 3)===0; - } - - /** - * @return string the current command string to execute - */ - public function __toString() - { - return (string) $this->getExecCommand(); - } + /** + * @var bool whether to escape any argument passed through `addArg()`. + * Default is `true`. + */ + public $escapeArgs = true; + + /** + * @var bool whether to escape the command passed to `setCommand()` or the + * constructor. This is only useful if `$escapeArgs` is `false`. Default + * is `false`. + */ + public $escapeCommand = false; + + /** + * @var bool whether to use `exec()` instead of `proc_open()`. This can be + * used on Windows system to workaround some quirks there. Note, that any + * errors from your command will be output directly to the PHP output + * stream. `getStdErr()` will also not work anymore and thus you also won't + * get the error output from `getError()` in this case. You also can't pass + * any environment variables to the command if this is enabled. Default is + * `false`. + */ + public $useExec = false; + + /** + * @var bool whether to capture stderr (2>&1) when `useExec` is true. This + * will try to redirect the stderr to stdout and provide the complete + * output of both in `getStdErr()` and `getError()`. Default is `true`. + */ + public $captureStdErr = true; + + /** + * @var string|null the initial working dir for `proc_open()`. Default is + * `null` for current PHP working dir. + */ + public $procCwd; + + /** + * @var array|null an array with environment variables to pass to + * `proc_open()`. Default is `null` for none. + */ + public $procEnv; + + /** + * @var array|null an array of other_options for `proc_open()`. Default is + * `null` for none. + */ + public $procOptions; + + /** + * @var bool|null whether to set the stdin/stdout/stderr streams to + * non-blocking mode when `proc_open()` is used. This allows to have huge + * inputs/outputs without making the process hang. The default is `null` + * which will enable the feature on Non-Windows systems. Set it to `true` + * or `false` to manually enable/disable it. It does not work on Windows. + */ + public $nonBlockingMode; + + /** + * @var int the time in seconds after which a command should be terminated. + * This only works in non-blocking mode. Default is `null` which means the + * process is never terminated. + */ + public $timeout; + + /** + * @var null|string the locale to temporarily set before calling + * `escapeshellargs()`. Default is `null` for none. + */ + public $locale; + + /** + * @var null|string|resource to pipe to standard input + */ + protected $_stdIn; + + /** + * @var string the command to execute + */ + protected $_command; + + /** + * @var array the list of command arguments + */ + protected $_args = array(); + + /** + * @var string the stdout output + */ + protected $_stdOut = ''; + + /** + * @var string the stderr output + */ + protected $_stdErr = ''; + + /** + * @var int the exit code + */ + protected $_exitCode; + + /** + * @var string the error message + */ + protected $_error = ''; + + /** + * @var bool whether the command was successfully executed + */ + protected $_executed = false; + + /** + * @param string|array $options either a command string or an options array + * @see setOptions + */ + public function __construct($options = null) + { + if (is_array($options)) { + $this->setOptions($options); + } elseif (is_string($options)) { + $this->setCommand($options); + } + } + + /** + * @param array $options array of name => value options (i.e. public + * properties) that should be applied to this object. You can also pass + * options that use a setter, e.g. you can pass a `fileName` option which + * will be passed to `setFileName()`. + * @throws \Exception on unknown option keys + * @return static for method chaining + */ + public function setOptions($options) + { + foreach ($options as $key => $value) { + if (property_exists($this, $key)) { + $this->$key = $value; + } else { + $method = 'set'.ucfirst($key); + if (method_exists($this, $method)) { + call_user_func(array($this,$method), $value); + } else { + throw new \Exception("Unknown configuration option '$key'"); + } + } + } + return $this; + } + + /** + * @param string $command the command or full command string to execute, + * like 'gzip' or 'gzip -d'. You can still call addArg() to add more + * arguments to the command. If `$escapeCommand` was set to true, the command + * gets escaped with `escapeshellcmd()`. + * @return static for method chaining + */ + public function setCommand($command) + { + if ($this->escapeCommand) { + $command = escapeshellcmd($command); + } + if ($this->getIsWindows()) { + // Make sure to switch to correct drive like "E:" first if we have + // a full path in command + if (isset($command[1]) && $command[1] === ':') { + $position = 1; + // Could be a quoted absolute path because of spaces. + // i.e. "C:\Program Files (x86)\file.exe" + } elseif (isset($command[2]) && $command[2] === ':') { + $position = 2; + } else { + $position = false; + } + + // Absolute path. If it's a relative path, let it slide. + if ($position) { + $command = sprintf( + $command[$position - 1] . ': && cd %s && %s', + escapeshellarg(dirname($command)), + escapeshellarg(basename($command)) + ); + } + } + $this->_command = $command; + return $this; + } + + /** + * @param string|resource $stdIn If set, the string will be piped to the + * command via standard input. This enables the same functionality as + * piping on the command line. It can also be a resource like a file + * handle or a stream in which case its content will be piped into the + * command like an input redirection. + * @return static for method chaining + */ + public function setStdIn($stdIn) { + $this->_stdIn = $stdIn; + return $this; + } + + /** + * @return string|null the command that was set through `setCommand()` or + * passed to the constructor. `null` if none. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * @return string|bool the full command string to execute. If no command + * was set with `setCommand()` or passed to the constructor it will return + * `false`. + */ + public function getExecCommand() + { + $command = $this->getCommand(); + if (!$command) { + $this->_error = 'Could not locate any executable command'; + return false; + } + + $args = $this->getArgs(); + return $args ? $command.' '.$args : $command; + } + + /** + * @param string $args the command arguments as string like `'--arg1=value1 + * --arg2=value2'`. Note that this string will not get escaped. This will + * overwrite the args added with `addArgs()`. + * @return static for method chaining + */ + public function setArgs($args) + { + $this->_args = array($args); + return $this; + } + + /** + * @return string the command args that where set with `setArgs()` or added + * with `addArg()` separated by spaces. + */ + public function getArgs() + { + return implode(' ', $this->_args); + } + + /** + * @param string $key the argument key to add e.g. `--feature` or + * `--name=`. If the key does not end with `=`, the (optional) $value will + * be separated by a space. The key will get escaped if `$escapeArgs` is `true`. + * @param string|array|null $value the optional argument value which will + * get escaped if $escapeArgs is true. An array can be passed to add more + * than one value for a key, e.g. + * `addArg('--exclude', array('val1','val2'))` + * which will create the option + * `'--exclude' 'val1' 'val2'`. + * @param bool|null $escape if set, this overrides the `$escapeArgs` setting + * and enforces escaping/no escaping of keys and values + * @return static for method chaining + */ + public function addArg($key, $value = null, $escape = null) + { + $doEscape = $escape !== null ? $escape : $this->escapeArgs; + $useLocale = $doEscape && $this->locale !== null; + + if ($useLocale) { + $locale = setlocale(LC_CTYPE, 0); // Returns current locale setting + setlocale(LC_CTYPE, $this->locale); + } + if ($value === null) { + $this->_args[] = $doEscape ? escapeshellarg($key) : $key; + } else { + if (substr($key, -1) === '=') { + $separator = '='; + $argKey = substr($key, 0, -1); + } else { + $separator = ' '; + $argKey = $key; + } + $argKey = $doEscape ? escapeshellarg($argKey) : $argKey; + + if (is_array($value)) { + $params = array(); + foreach ($value as $v) { + $params[] = $doEscape ? escapeshellarg($v) : $v; + } + $this->_args[] = $argKey . $separator . implode(' ', $params); + } else { + $this->_args[] = $argKey . $separator . + ($doEscape ? escapeshellarg($value) : $value); + } + } + if ($useLocale) { + setlocale(LC_CTYPE, $locale); + } + + return $this; + } + + /** + * @param bool $trim whether to `trim()` the return value. The default is `true`. + * @return string the command output (stdout). Empty if none. + */ + public function getOutput($trim = true) + { + return $trim ? trim($this->_stdOut) : $this->_stdOut; + } + + /** + * @param bool $trim whether to `trim()` the return value. The default is `true`. + * @return string the error message, either stderr or an internal message. + * Empty string if none. + */ + public function getError($trim = true) + { + return $trim ? trim($this->_error) : $this->_error; + } + + /** + * @param bool $trim whether to `trim()` the return value. The default is `true`. + * @return string the stderr output. Empty if none. + */ + public function getStdErr($trim = true) + { + return $trim ? trim($this->_stdErr) : $this->_stdErr; + } + + /** + * @return int|null the exit code or null if command was not executed yet + */ + public function getExitCode() + { + return $this->_exitCode; + } + + /** + * @return string whether the command was successfully executed + */ + public function getExecuted() + { + return $this->_executed; + } + + /** + * Execute the command + * + * @return bool whether execution was successful. If `false`, error details + * can be obtained from `getError()`, `getStdErr()` and `getExitCode()`. + */ + public function execute() + { + $command = $this->getExecCommand(); + + if (!$command) { + return false; + } + + if ($this->useExec) { + $execCommand = $this->captureStdErr ? "$command 2>&1" : $command; + exec($execCommand, $output, $this->_exitCode); + $this->_stdOut = implode("\n", $output); + if ($this->_exitCode !== 0) { + $this->_stdErr = $this->_stdOut; + $this->_error = empty($this->_stdErr) ? 'Command failed' : $this->_stdErr; + return false; + } + } else { + $isInputStream = $this->_stdIn !== null && + is_resource($this->_stdIn) && + in_array(get_resource_type($this->_stdIn), array('file', 'stream')); + $isInputString = is_string($this->_stdIn); + $hasInput = $isInputStream || $isInputString; + $hasTimeout = $this->timeout !== null && $this->timeout > 0; + + $descriptors = array( + 1 => array('pipe','w'), + 2 => array('pipe', $this->getIsWindows() ? 'a' : 'w'), + ); + if ($hasInput) { + $descriptors[0] = array('pipe', 'r'); + } + + + // Issue #20 Set non-blocking mode to fix hanging processes + $nonBlocking = $this->nonBlockingMode === null ? + !$this->getIsWindows() : $this->nonBlockingMode; + + $startTime = $hasTimeout ? time() : 0; + $process = proc_open($command, $descriptors, $pipes, $this->procCwd, $this->procEnv, $this->procOptions); + + if (is_resource($process)) { + + if ($nonBlocking) { + stream_set_blocking($pipes[1], false); + stream_set_blocking($pipes[2], false); + if ($hasInput) { + $writtenBytes = 0; + $isInputOpen = true; + stream_set_blocking($pipes[0], false); + if ($isInputStream) { + stream_set_blocking($this->_stdIn, false); + } + } + + // Due to the non-blocking streams we now have to check in + // a loop if the process is still running. We also need to + // ensure that all the pipes are written/read alternately + // until there's nothing left to write/read. + $isRunning = true; + while ($isRunning) { + $status = proc_get_status($process); + $isRunning = $status['running']; + + // We first write to stdIn if we have an input. For big + // inputs it will only write until the input buffer of + // the command is full (the command may now wait that + // we read the output buffers - see below). So we may + // have to continue writing in another cycle. + // + // After everything is written it's safe to close the + // input pipe. + if ($isRunning && $hasInput && $isInputOpen) { + if ($isInputStream) { + $written = stream_copy_to_stream($this->_stdIn, $pipes[0], 16 * 1024, $writtenBytes); + if ($written === false || $written === 0) { + $isInputOpen = false; + fclose($pipes[0]); + } else { + $writtenBytes += $written; + } + } else { + if ($writtenBytes < strlen($this->_stdIn)) { + $writtenBytes += fwrite($pipes[0], substr($this->_stdIn, $writtenBytes)); + } else { + $isInputOpen = false; + fclose($pipes[0]); + } + } + } + + // Read out the output buffers because if they are full + // the command may block execution. We do this even if + // $isRunning is `false`, because there could be output + // left in the buffers. + // + // The latter is only an assumption and needs to be + // verified - but it does not hurt either and works as + // expected. + // + while (($out = fgets($pipes[1])) !== false) { + $this->_stdOut .= $out; + } + while (($err = fgets($pipes[2])) !== false) { + $this->_stdErr .= $err; + } + + $runTime = $hasTimeout ? time() - $startTime : 0; + if ($isRunning && $hasTimeout && $runTime >= $this->timeout) { + // Only send a SIGTERM and handle status in the next cycle + proc_terminate($process); + } + + if (!$isRunning) { + $this->_exitCode = $status['exitcode']; + if ($this->_exitCode !== 0 && empty($this->_stdErr)) { + if ($status['stopped']) { + $signal = $status['stopsig']; + $this->_stdErr = "Command stopped by signal $signal"; + } elseif ($status['signaled']) { + $signal = $status['termsig']; + $this->_stdErr = "Command terminated by signal $signal"; + } else { + $this->_stdErr = 'Command unexpectedly terminated without error message'; + } + } + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + } else { + // The command is still running. Let's wait some + // time before we start the next cycle. + usleep(10000); + } + } + } else { + if ($hasInput) { + if ($isInputStream) { + stream_copy_to_stream($this->_stdIn, $pipes[0]); + } elseif ($isInputString) { + fwrite($pipes[0], $this->_stdIn); + } + fclose($pipes[0]); + } + $this->_stdOut = stream_get_contents($pipes[1]); + $this->_stdErr = stream_get_contents($pipes[2]); + fclose($pipes[1]); + fclose($pipes[2]); + $this->_exitCode = proc_close($process); + } + + if ($this->_exitCode !== 0) { + $this->_error = $this->_stdErr ? + $this->_stdErr : + "Failed without error message: $command (Exit code: {$this->_exitCode})"; + return false; + } + } else { + $this->_error = "Could not run command $command"; + return false; + } + } + + $this->_executed = true; + + return true; + } + + /** + * @return bool whether we are on a Windows OS + */ + public function getIsWindows() + { + return strncasecmp(PHP_OS, 'WIN', 3)===0; + } + + /** + * @return string the current command string to execute + */ + public function __toString() + { + return (string) $this->getExecCommand(); + } } diff --git a/webroot/lib/simple-imap.class.php b/webroot/lib/simple-imap.class.php index ff0a5bc..b85a646 100644 --- a/webroot/lib/simple-imap.class.php +++ b/webroot/lib/simple-imap.class.php @@ -35,15 +35,15 @@ */ class SimpleImap { - private $_connection; + private IMAP\Connection $_connection; - private $_server = EMAIL_SERVER; - private $_user = EMAIL_SERVER_USER; - private $_pass = EMAIL_SERVER_PASS; - private $_port = EMAIL_SERVER_PORT_IMAP; - private $_mailbox = EMAIL_SERVER_MAILBOX; + private string $_server = EMAIL_SERVER; + private string $_user = EMAIL_SERVER_USER; + private string $_pass = EMAIL_SERVER_PASS; + private int $_port = EMAIL_SERVER_PORT_IMAP; + private string $_mailbox = EMAIL_SERVER_MAILBOX; - private $_connectionstring = ''; + private string $_connectionstring = ''; /** * SimpleImap constructor. @@ -67,7 +67,7 @@ class SimpleImap { * @see http://ca.php.net/manual/en/function.imap-open.php * @throws Exception */ - public function connect() { + public function connect(): void { if(empty($this->_server)) { throw new Exception('Missing EMAIL_SERVER'); @@ -134,12 +134,12 @@ class SimpleImap { } /** - * the the current stats about the mail connection and INBOX + * the current status about the mail connection and INBOX * kinda debug only * * @see http://ca.php.net/manual/en/function.imap-status.php */ - public function mailboxStatus() { + public function mailboxStatus(): void { if($this->_connection !== false) { $status = imap_status($this->_connection, $this->_connectionstring.$this->_mailbox, SA_ALL); @@ -181,7 +181,7 @@ class SimpleImap { * @param integer $messagenum * @return object */ - public function emailHeaders_rfc822(int $messagenum) { + public function emailHeaders_rfc822(int $messagenum): object { return imap_rfc822_parse_headers($this->emailHeaders($messagenum)); } @@ -202,7 +202,7 @@ class SimpleImap { * @param integer $messageUid This is the message Uid as an int * @param string $folder This is the target folder. Default is EMAIL_ARCHIVE_FOLDER */ - public function moveMessage(int $messageUid, $folder=EMAIL_ARCHIVE_FOLDER) { + public function moveMessage(int $messageUid, string $folder=EMAIL_ARCHIVE_FOLDER): void { if(!empty($messageUid) && !empty($folder)) { $messageUid = (string)$messageUid; imap_setflag_full($this->_connection,$messageUid,"\SEEN", ST_UID); @@ -286,7 +286,7 @@ class SimpleImap { /** * close the imap connection */ - function close() { + function close(): void { imap_close($this->_connection); } } diff --git a/webroot/lib/snapshot.class.php b/webroot/lib/snapshot.class.php index 895c0c1..1e0f4c9 100644 --- a/webroot/lib/snapshot.class.php +++ b/webroot/lib/snapshot.class.php @@ -3,7 +3,7 @@ * Insipid * Personal web-bookmark-system * - * Copyright 2016-2021 Johannes Keßler + * Copyright 2016-2022 Johannes Keßler * * Development starting from 2011: Johannes Keßler * https://www.bananas-playground.net/projekt/insipid/ diff --git a/webroot/view/editcategories.inc.php b/webroot/view/editcategories.inc.php index 8d1de3b..65e8f45 100644 --- a/webroot/view/editcategories.inc.php +++ b/webroot/view/editcategories.inc.php @@ -131,5 +131,5 @@ if(isset($_POST['category']) && !empty($_POST['category']) && isset($_POST['upda } # show all the categories we have -$categoryCollection = $Management->categories(false, true); +$categoryCollection = $Management->categories(0, true); $subHeadline = $T->t('view.categories').' '; diff --git a/webroot/view/edittags.inc.php b/webroot/view/edittags.inc.php index ae3a12b..71351a8 100644 --- a/webroot/view/edittags.inc.php +++ b/webroot/view/edittags.inc.php @@ -131,5 +131,5 @@ if(isset($_POST['tag']) && !empty($_POST['tag']) && isset($_POST['updateTags'])) } # show all the tags we have -$tagCollection = $Management->tags(false, true); +$tagCollection = $Management->tags(0, true); $subHeadline = $T->t('view.tags').' '; diff --git a/webroot/view/overview.inc.php b/webroot/view/overview.inc.php index 5aa8431..4adedf0 100644 --- a/webroot/view/overview.inc.php +++ b/webroot/view/overview.inc.php @@ -104,7 +104,7 @@ switch($_requestMode) { } else { # show all the tags we have - $tagCollection = $Management->tags(false, true); + $tagCollection = $Management->tags(0, true); $subHeadline = $T->t('view.tags').' '; } break; @@ -122,7 +122,7 @@ switch($_requestMode) { } else { # show all the categories we have - $categoryCollection = $Management->categories(false, true); + $categoryCollection = $Management->categories(0, true); $subHeadline = $T->t('view.categories').' '; } break;