- moved some files/methods ("generic" enough to be here) from 'hub'.
- continued with file-based socket listener to allow listening to sockets ...
Signed-off-by: Roland Häder <roland@mxchange.org>
// CFG: FEATURE-FUSE-CLASS
$cfg->setConfigEntry('feature_fuse_class', 'FuseFeature');
+// CFG: TEMP-FILE-PATH
+$cfg->setConfigEntry('temp_file_path', sys_get_temp_dir());
+
// [EOF]
?>
if (is_null($args)) {
// No arguments
$argsString = 'NULL';
- } elseif (empty($args)) {
- // Empty arguments
- $argsString = '(empty)';
} elseif (is_array($args)) {
+ // Start braces
+ $argsString = '(';
+
// Some arguments are there
foreach ($args as $arg) {
- // Add the value itself if not array. This prevents 'array to string conversion' message
- if (is_array($arg)) {
- $argsString .= 'Array';
- } else {
- $argsString .= $arg;
- }
-
// Add data about the argument
- $argsString .= ' (' . gettype($arg);
+ $argsString .= gettype($arg) . ':';
- if (is_string($arg)) {
+ if (is_null($arg)) {
+ // Found a NULL argument
+ $argsString .= 'NULL';
+ } elseif (is_string($arg)) {
// Add length for strings
- $argsString .= ', ' . strlen($arg);
+ $argsString .= strlen($arg);
+ } elseif ((is_int($arg)) || (is_float($arg))) {
+ // ... integer/float
+ $argsString .= $arg;
} elseif (is_array($arg)) {
// .. or size if array
- $argsString .= ', ' . count($arg);
+ $argsString .= count($arg);
+ } elseif (is_object($arg)) {
+ // Get reflection
+ $reflection = new ReflectionClass($arg);
+
+ // Is an other object, maybe no __toString() available
+ $argsString .= $reflection->getName();
} elseif ($arg === TRUE) {
// ... is boolean 'TRUE'
- $argsString .= ', TRUE';
+ $argsString .= 'TRUE';
} elseif ($arg === FALSE) {
// ... is boolean 'FALSE'
- $argsString .= ', FALSE';
+ $argsString .= 'FALSE';
}
- // Closing bracket
- $argsString .= '), ';
+ // Comma for next one
+ $argsString .= ', ';
} // END - foreach
// Remove last comma
if (substr($argsString, -2, 1) == ',') {
$argsString = substr($argsString, 0, -2);
} // END - if
+
+ // Close braces
+ $argsString .= ')';
} else {
// Invalid arguments!
$argsString = '!INVALID:' . gettype($args) . '!';
// Sorry, there is no other way getting this nice backtrace
if (!empty($message)) {
// Output message
- printf('Message: %s<br />' . chr(10), $message);
+ printf('Message: %s<br />' . PHP_EOL, $message);
} // END - if
print('<pre>');
// Add line number to the code
foreach (explode(chr(10), $phpCode) as $lineNo => $code) {
// Add line numbers
- $markedCode .= sprintf('<span id="code_line">%s</span>: %s' . chr(10),
+ $markedCode .= sprintf('<span id="code_line">%s</span>: %s' . PHP_EOL,
($lineNo + 1),
htmlentities($code, ENT_QUOTES)
);
* @param $filePathName Name of the file/path to be checked
* @return $isReachable Whether it is within open_basedir()
*/
- public static function isReachableFilePath ($filePathName) {
+ protected static function isReachableFilePath ($filePathName) {
// Is not reachable by default
$isReachable = FALSE;
// Return status
return $isReadable;
}
+
+ /**
+ * Creates a full-qualified file name (FQFN) for given file name by adding
+ * a configured temporary file path to it.
+ *
+ * @param $fileName Name for temporary file
+ * @return $fqfn Full-qualified file name
+ * @throw PathWriteProtectedException If the path in 'temp_file_path' is write-protected
+ * @throws FileIoException If the file cannot be written
+ */
+ protected static function createTempPathForFile ($fileName) {
+ // Get config entry
+ $basePath = FrameworkConfiguration::getSelfInstance()->getConfigEntry('temp_file_path');
+
+ // Is the path writeable?
+ if (!is_writable($basePath)) {
+ // Path is write-protected
+ throw new PathWriteProtectedException($fileName, self::EXCEPTION_PATH_CANNOT_BE_WRITTEN);
+ } // END - if
+
+ // Add it
+ $fqfn = $basePath . '/' . $fileName;
+
+ // Is it reachable?
+ if (!self::isReachableFilePath($fqfn)) {
+ // Not reachable
+ throw new FileIoException($fqfn, self::EXCEPTION_FILE_NOT_REACHABLE);
+ } // END - if
+
+ // Return it
+ return $fqfn;
+ }
+
+ /**
+ * "Getter" for a printable state name
+ *
+ * @return $stateName Name of the node's state in a printable format
+ */
+ public final function getPrintableState () {
+ // Default is 'null'
+ $stateName = 'null';
+
+ // Get the state instance
+ $stateInstance = $this->getStateInstance();
+
+ // Is it an instance of Stateable?
+ if ($stateInstance instanceof Stateable) {
+ // Then use that state name
+ $stateName = $stateInstance->getStateName();
+ } // END - if
+
+ // Return result
+ return $stateName;
+ }
}
// [EOF]
--- /dev/null
+<?php
+/**
+ * A general data Handler
+ *
+ * @author Roland Haeder <webmaster@shipsimu.org>
+ * @version 0.0.0
+ * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Core Developer Team
+ * @license GNU GPL 3.0 or any newer version
+ * @link http://www.shipsimu.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+abstract class BaseDataHandler extends BaseHandler {
+ /**
+ * Last exception instance from database layer or NULL (default)
+ */
+ private $lastException = NULL;
+
+ /**
+ * Array with search criteria elements
+ */
+ protected $searchData = array();
+
+ /**
+ * Array with all data XML nodes (which hold the actual data) and their values
+ */
+ protected $messageDataElements = array();
+
+ /**
+ * Array for translating message data elements (other node's data mostly)
+ * into configuration elements.
+ */
+ protected $messageToConfig = array();
+
+ /**
+ * Array for copying configuration entries
+ */
+ protected $configCopy = array();
+
+ /**
+ * Protected constructor
+ *
+ * @param $className Name of the class
+ * @return void
+ */
+ protected function __construct ($className) {
+ // Call parent constructor
+ parent::__construct($className);
+
+ // Get a DHT instance
+ $dhtInstance = DhtObjectFactory::createDhtInstance('node');
+
+ // Set it here
+ $this->setDhtInstance($dhtInstance);
+ }
+
+ /**
+ * Getter for search data array
+ *
+ * @return $searchData Search data array
+ */
+ public final function getSearchData () {
+ return $this->searchData;
+ }
+
+ /**
+ * Getter for last exception
+ *
+ * @return $lastException Last thrown exception
+ */
+ public final function getLastException () {
+ return $this->lastException;
+ }
+
+ /**
+ * Setter for last exception
+ *
+ * @param $lastException Last thrown exception
+ * @return void
+ */
+ public final function setLastException (FrameworkException $exceptionInstance = NULL) {
+ $this->lastException = $exceptionInstance;
+ }
+
+ /**
+ * Prepares a message as answer for given message data for delivery.
+ *
+ * @param $messageData An array with all message data
+ * @param $packageInstance An instance of a Deliverable instance
+ * @return void
+ */
+ protected function prepareAnswerMessage (array $messageData, Deliverable $packageInstance) {
+ // Debug message
+ /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Going to send an answer message for ' . $this->getHandlerName() . ' ...');
+
+ // Get a helper instance based on this handler's name
+ $helperInstance = ObjectFactory::createObjectByConfiguredName('node_answer_' . $this->getHandlerName() . '_helper_class', array($messageData));
+
+ // Get node instance
+ $nodeInstance = NodeObjectFactory::createNodeInstance();
+
+ // Load descriptor XML
+ $helperInstance->loadDescriptorXml($nodeInstance);
+
+ /*
+ * Set missing (temporary) configuration data, mostly it needs to be
+ * copied from message data array.
+ */
+ $this->initMessageConfigurationData($messageData);
+
+ // Compile any configuration variables
+ $helperInstance->getTemplateInstance()->compileConfigInVariables();
+
+ // Deliver the package
+ $helperInstance->sendPackage($nodeInstance);
+
+ /*
+ * Remove temporary configuration
+ */
+ $this->removeMessageConfigurationData($messageData);
+
+ // Debug message
+ /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Answer message has been prepared.');
+ }
+
+ /**
+ * Prepares the next message
+ *
+ * @param $messageData An array with all message data
+ * @param $packageInstance An instance of a Deliverable instance
+ * @return void
+ */
+ protected function prepareNextMessage (array $messageData, Deliverable $packageInstance) {
+ // Debug message
+ /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Going to send next message ...');
+
+ // Get a helper instance based on this handler's name
+ $helperInstance = ObjectFactory::createObjectByConfiguredName('node_next_' . $this->getHandlerName() . '_helper_class', array($messageData));
+
+ // Get node instance
+ $nodeInstance = NodeObjectFactory::createNodeInstance();
+
+ // Load descriptor XML
+ $helperInstance->loadDescriptorXml($nodeInstance);
+
+ /*
+ * Set missing (temporary) configuration data, mostly it needs to be
+ * copied from message data array.
+ */
+ $this->initMessageConfigurationData($messageData);
+
+ // Compile any configuration variables
+ $helperInstance->getTemplateInstance()->compileConfigInVariables();
+
+ // Deliver the package
+ $helperInstance->sendPackage($nodeInstance);
+
+ /*
+ * Remove temporary configuration
+ */
+ $this->removeMessageConfigurationData($messageData);
+
+ // Debug message
+ /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Next message has been prepared.');
+ }
+
+ /**
+ * Initializes configuration data from given message data array
+ *
+ * @param $messageData An array with all message data
+ * @return void
+ */
+ abstract protected function initMessageConfigurationData (array $messageData);
+
+ /**
+ * Removes configuration data with given message data array from global
+ * configuration
+ *
+ * @param $messageData An array with all message data
+ * @return void
+ */
+ abstract protected function removeMessageConfigurationData (array $messageData);
+}
+
+// [EOF]
+?>
--- /dev/null
+Deny from all
--- /dev/null
+<?php
+/**
+ * A !!! raw data handler
+ *
+ * @author Roland Haeder <webmaster@shipsimu.org>
+ * @version 0.0.0
+ * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Hub Developer Team
+ * @license GNU GPL 3.0 or any newer version
+ * @link http://www.shipsimu.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+class ???RawDataHandler extends BaseRawDataHandler implements Networkable {
+ /**
+ * Last socket error (default: Success)
+ */
+ private $lastSocketError = 0;
+
+ /**
+ * Protected constructor
+ *
+ * @return void
+ */
+ protected function __construct () {
+ // Call parent constructor
+ parent::__construct(__CLASS__);
+
+ // Set handler name
+ $this->setHandlerName('|||');
+ }
+
+ /**
+ * Creates an instance of this class
+ *
+ * @return $handlerInstance An instance of a Networkable class
+ */
+ public static final function create???RawDataHandler () {
+ // Get new instance
+ $handlerInstance = new ???RawDataHandler();
+
+ // Return the prepared instance
+ return $handlerInstance;
+ }
+
+ /**
+ * Processes raw data from given resource. This is mostly useful for TCP
+ * package handling and is implemented in the ???Listener class
+ *
+ * @param $resource A valid socket resource array
+ * @return void
+ */
+ public function processRawDataFromResource (array $socketArray) {
+ }
+}
+
+// [EOF]
+?>
--- /dev/null
+<?php
+/**
+ * A general Handler for raw data from sockets
+ *
+ * @author Roland Haeder <webmaster@shipsimu.org>
+ * @version 0.0.0
+ * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Hub Developer Team
+ * @license GNU GPL 3.0 or any newer version
+ * @link http://www.shipsimu.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+class BaseRawDataHandler extends BaseHandler {
+ // Error codes:
+ // - Socket raw data stream errors
+ const SOCKET_ERROR_UNKNOWN = 'unknown_error'; // Unknown error (should not happen)
+ const SOCKET_ERROR_TRANSPORT_ENDPOINT = 'transport_endpoint'; // Transport endpoint has closed
+ const SOCKET_ERROR_INVALID_BASE64_MODULO = 'base64_modulo'; // Length is not modulo 4
+ const SOCKET_ERROR_INVALID_BASE64_MESSAGE = 'base64_message'; // Raw data is not Base64-encoded
+ const SOCKET_ERROR_UNHANDLED = 'unhandled_package'; // Unhandled raw data (not bad)
+ const SOCKET_ERROR_CONNECTION_REFUSED = 'connection_refused'; // The name says it: connection refused
+ const SOCKET_ERROR_CONNECTION_TIMED_OUT = 'connection_timed_out'; // The name says it: connection attempt has timed-out
+ const SOCKET_ERROR_OPERATION_IN_PROGRESS = 'operation_in_progress'; // 'Operation now in progress'
+ const SOCKET_ERROR_OPERATION_ALREADY_PROGRESS = 'operation_already_progress'; // 'Operation already in progress'
+ const SOCKET_ERROR_RESOURCE_UNAVAILABLE = 'resource_unavailable'; // 'Resource temporary unavailable'
+ const SOCKET_ERROR_NO_ROUTE_TO_HOST = 'no_route_to_host'; // The name says it: no route to host
+ const SOCKET_ERROR_CONNECTION_RESET_BY_PEER = 'connection_reset_by_peer'; // Connection reset by peer
+ const SOCKET_ERROR_BROKEN_PIPE = 'broken_pipe'; // Broken pipe
+ const SOCKET_ERROR_PERMISSION_DENIED = 'permission_denied'; // Permission denied
+ const SOCKET_CONNECTED = 'connected'; // Nothing errorous happens, socket is connected
+
+ // - Package errors
+ const PACKAGE_ERROR_INVALID_DATA = 'invalid_data'; // Invalid data in package found
+ const PACKAGE_ERROR_INCOMPLETE_DATA = 'incomplete_data'; // Incomplete data sent (e.g. field is missing)
+ const PACKAGE_ERROR_INVALID_CONTENT = 'invalid_content'; // Content is invalid (e.g. not well-formed)
+ const PACKAGE_ERROR_RECIPIENT_MISMATCH = 'recipient_error'; // Recipient is not us
+ const PACKAGE_LEVEL_CHECK_OKAY = 'checked_package'; // Package is fine
+
+ // Package data
+ const PACKAGE_RAW_DATA = 'raw_data';
+ const PACKAGE_ERROR_CODE = 'error_code';
+
+ // Start/end marker
+ const STREAM_START_MARKER = '[[S]]';
+ const STREAM_END_MARKER = '[[E]]';
+
+ /**
+ * Stacker for raw data
+ */
+ const STACKER_NAME_RAW_DATA = 'raw_data';
+
+ /**
+ * Error code from socket
+ */
+ private $errorCode = -1;
+
+ /**
+ * Protected constructor
+ *
+ * @param $className Name of the class
+ * @return void
+ */
+ protected function __construct ($className) {
+ // Call parent constructor
+ parent::__construct($className);
+
+ // Set error code to 'unknown'
+ $this->setErrorCode(self::SOCKET_ERROR_UNKNOWN);
+
+ // Init stacker instance for processed raw data
+ $stackInstance = ObjectFactory::createObjectByConfiguredName('node_raw_data_stacker_class');
+
+ // Remember this in this package handler
+ $this->setStackInstance($stackInstance);
+
+ // Init stacker
+ $this->initStack();
+ }
+
+ /**
+ * Initializes the stacker for raw data
+ *
+ * @return void
+ */
+ protected function initStack () {
+ $this->getStackInstance()->initStack(self::STACKER_NAME_RAW_DATA);
+ }
+
+ /**
+ * Adds given raw data to the raw data stacker
+ *
+ * @param $rawData raw data from the socket resource
+ * @return void
+ */
+ protected function addRawDataToStacker ($rawData) {
+ /*
+ * Add the deocoded data and error code to the stacker so other classes
+ * (e.g. NetworkPackage) can "pop" it from the stacker.
+ */
+ $this->getStackInstance()->pushNamed(self::STACKER_NAME_RAW_DATA, array(
+ self::PACKAGE_RAW_DATA => $rawData,
+ self::PACKAGE_ERROR_CODE => $this->getErrorCode()
+ ));
+ }
+
+ /**
+ * Checks whether raw data is pending for further processing.
+ *
+ * @return $isPending Whether raw data is pending
+ */
+ public function isRawDataPending () {
+ // Does the stacker have some entries (not empty)?
+ $isPending = (!$this->getStackInstance()->isStackEmpty(self::STACKER_NAME_RAW_DATA));
+
+ // Return it
+ return $isPending;
+ }
+
+ /**
+ * "Getter" for next raw data from the stacker
+ *
+ * @return $rawData Raw data from the stacker
+ */
+ public function getNextRawData () {
+ // "Pop" the raw data from the stacker
+ $rawData = $this->getStackInstance()->popNamed(self::STACKER_NAME_RAW_DATA);
+
+ // And return it
+ return $rawData;
+ }
+
+ /**
+ * Checks whether the 'recipient' field matches our own an universal node
+ * locator.
+ *
+ * @param $packageData Raw package data
+ * @return $matches Whether it matches
+ * @todo This method will be moved to a better place
+ */
+ protected function ifRecipientMatchesOwnUniversalNodeLocator (array $packageData) {
+ // Construct own address first
+ $ownAddress = NodeObjectFactory::createNodeInstance()->determineUniversalNodeLocator();
+
+ // Does it match?
+ $matches = ($ownAddress === $packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT]);
+
+ // Return result
+ return $matches;
+ }
+
+ /**
+ * Setter for error code
+ *
+ * @param $errorCode The error code we shall set
+ * @return void
+ */
+ public final function setErrorCode ($errorCode) {
+ $this->errorCode = $errorCode;
+ }
+
+ /**
+ * Getter for error code
+ *
+ * @return $errorCode The error code
+ */
+ public final function getErrorCode () {
+ return $this->errorCode;
+ }
+}
+
+// [EOF]
+?>
public function monitorIncomingRawData () {
throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION);
}
+
+ /**
+ * Constructs a callable method name from given socket error code. If the
+ * method is not found, a generic one is used.
+ *
+ * @param $errorCode Error code from socket_last_error()
+ * @return $handlerName Call-back method name for the error handler
+ * @throws UnsupportedSocketErrorHandlerException If the error handler is not implemented
+ */
+ protected function getSocketErrorHandlerFromCode ($errorCode) {
+ // Create a name from translated error code
+ $handlerName = 'socketError' . self::convertToClassName($this->translateSocketErrorCodeToName($errorCode)) . 'Handler';
+
+ // Is the call-back method there?
+ if (!method_exists($this, $handlerName)) {
+ // Please implement this
+ throw new UnsupportedSocketErrorHandlerException(array($this, $handlerName, $errorCode), BaseConnectionHelper::EXCEPTION_UNSUPPORTED_ERROR_HANDLER);
+ } // END - if
+
+ // Return it
+ return $handlerName;
+ }
+
+ /**
+ * Handles socket error for given socket resource and peer data. This method
+ * validates $socketResource if it is a valid resource (see is_resource())
+ * but assumes valid data in array $recipientData, except that
+ * count($recipientData) is always 2.
+ *
+ * @param $method Value of __METHOD__ from calling method
+ * @param $line Value of __LINE__ from calling method
+ * @param $socketResource A valid socket resource
+ * @param $socketData A valid socket data array (0 = IP/file name, 1 = port)
+ * @return void
+ * @throws InvalidSocketException If $socketResource is no socket resource
+ * @throws NoSocketErrorDetectedException If socket_last_error() gives zero back
+ */
+ protected final function handleSocketError ($method, $line, $socketResource, array $socketData) {
+ // This method handles only socket resources
+ if (!is_resource($socketResource)) {
+ // No resource, abort here
+ throw new InvalidSocketException(array($this, $socketResource), BaseListener::EXCEPTION_INVALID_SOCKET);
+ } // END - if
+
+ // Check socket array, 1st element is mostly IP address (or file name), 2nd is port number
+ //* DEBUG-DIE: */ die(__METHOD__ . ':socketData=' . print_r($socketData, TRUE));
+ assert(isset($socketData[0]));
+ assert(isset($socketData[1]));
+
+ // Get error code for first validation (0 is not an error)
+ $errorCode = socket_last_error($socketResource);
+
+ // If the error code is zero, someone called this method without an error
+ if ($errorCode == 0) {
+ // No error detected (or previously cleared outside this method)
+ throw new NoSocketErrorDetectedException(array($this, $socketResource), BaseListener::EXCEPTION_NO_SOCKET_ERROR);
+ } // END - if
+
+ // Get handler (method) name
+ $handlerName = $this->getSocketErrorHandlerFromCode($errorCode);
+
+ // Call-back the error handler method
+ call_user_func_array(array($this, $handlerName), array($socketResource, $socketData));
+
+ // Finally clear the error because it has been handled
+ socket_clear_error($socketResource);
+ }
+
+ /**
+ * Translates socket error codes into our own internal names which can be
+ * used for call-backs.
+ *
+ * @param $errorCode The error code from socket_last_error() to be translated
+ * @return $errorName The translated name (all lower-case, with underlines)
+ */
+ public function translateSocketErrorCodeToName ($errorCode) {
+ // Nothing bad happened by default
+ $errorName = BaseRawDataHandler::SOCKET_CONNECTED;
+
+ // Is the code a number, then we have to change it
+ switch ($errorCode) {
+ case 0: // Silently ignored, the socket is connected
+ break;
+
+ case 11: // "Resource temporary unavailable"
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_RESOURCE_UNAVAILABLE;
+ break;
+
+ case 13: // "Permission denied"
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_PERMISSION_DENIED;
+ break;
+
+ case 32: // "Broken pipe"
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_BROKEN_PIPE;
+ break;
+
+ case 104: // "Connection reset by peer"
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_RESET_BY_PEER;
+ break;
+
+ case 107: // "Transport end-point not connected"
+ case 134: // On some (?) systems for 'transport end-point not connected'
+ // @TODO On some systems it is 134, on some 107?
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_TRANSPORT_ENDPOINT;
+ break;
+
+ case 110: // "Connection timed out"
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_TIMED_OUT;
+ break;
+
+ case 111: // "Connection refused"
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_REFUSED;
+ break;
+
+ case 113: // "No route to host"
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_NO_ROUTE_TO_HOST;
+ break;
+
+ case 114: // "Operation already in progress"
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_ALREADY_PROGRESS;
+ break;
+
+ case 115: // "Operation now in progress"
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_IN_PROGRESS;
+ break;
+
+ default: // Everything else <> 0
+ // Unhandled error code detected, so first debug it because we may want to handle it like the others
+ self::createDebugInstance(__CLASS__)->debugOutput('BASE-HUB[' . __METHOD__ . ':' . __LINE__ . '] UNKNOWN ERROR CODE = ' . $errorCode . ', MESSAGE = ' . socket_strerror($errorCode));
+
+ // Change it only in this class
+ $errorName = BaseRawDataHandler::SOCKET_ERROR_UNKNOWN;
+ break;
+ }
+
+ // Return translated name
+ return $errorName;
+ }
+
+ /**
+ * Shuts down a given socket resource. This method does only ease calling
+ * the right visitor.
+ *
+ * @param $socketResource A valid socket resource
+ * @return void
+ */
+ public function shutdownSocket ($socketResource) {
+ // Debug message
+ self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
+
+ // Set socket resource
+ $this->setSocketResource($socketResource);
+
+ // Get a visitor instance
+ $visitorInstance = ObjectFactory::createObjectByConfiguredName('shutdown_socket_visitor_class');
+
+ // Debug output
+ self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
+
+ // Call the visitor
+ $this->accept($visitorInstance);
+ }
+
+ /**
+ * Half-shuts down a given socket resource. This method does only ease calling
+ * an other visitor than shutdownSocket() does.
+ *
+ * @param $socketResource A valid socket resource
+ * @return void
+ */
+ public function halfShutdownSocket ($socketResource) {
+ // Debug message
+ self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Half-shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
+
+ // Set socket resource
+ $this->setSocketResource($socketResource);
+
+ // Get a visitor instance
+ $visitorInstance = ObjectFactory::createObjectByConfiguredName('half_shutdown_socket_visitor_class');
+
+ // Debug output
+ self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
+
+ // Call the visitor
+ $this->accept($visitorInstance);
+ }
+
+ // ************************************************************************
+ // Socket error handler call-back methods
+ // ************************************************************************
+
+ /**
+ * Handles socket error 'permission denied', but does not clear it for
+ * later debugging purposes.
+ *
+ * @param $socketResource A valid socket resource
+ * @param $socketData A valid socket data array (0 = IP/file name, 1 = port)
+ * @return void
+ * @throws SocketBindingException The socket could not be bind to
+ */
+ protected function socketErrorPermissionDeniedHandler ($socketResource, array $socketData) {
+ // Get socket error code for verification
+ $socketError = socket_last_error($socketResource);
+
+ // Get error message
+ $errorMessage = socket_strerror($socketError);
+
+ // Shutdown this socket
+ $this->shutdownSocket($socketResource);
+
+ // Throw it again
+ throw new SocketBindingException(array($this, $socketData, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
+ }
}
// [EOF]
* Initializes the listener by setting up the required socket server
*
* @return void
- * @todo 0% done
*/
public function initListener() {
- $this->partialStub('Need to implement this method.');
+ // Init socket
+ $mainSocket = socket_create(AF_UNIX, SOCK_STREAM, 0);
+
+ // Is the socket resource valid?
+ if (!is_resource($mainSocket)) {
+ // Something bad happened
+ throw new InvalidSocketException(array($this, $mainSocket), BaseListener::EXCEPTION_INVALID_SOCKET);
+ } // END - if
+
+ // Get socket error code for verification
+ $socketError = socket_last_error($mainSocket);
+
+ // Check if there was an error else
+ if ($socketError > 0) {
+ // Handle this socket error with a faked recipientData array
+ $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array('null', '0'));
+ } // END - if
+
+ // Create file name
+ $socketFile = self::createTempPathForFile('php_hub_ipc_socket');
+
+ // Debug message
+ self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: socketFile=' . $socketFile . ' ...');
+
+ // File name must not be empty
+ assert(!empty($socketFile));
+
+ // Is the file there?
+ if ((self::isReachableFilePath($socketFile)) && (file_exists($socketFile))) {
+ // Old socket found
+ self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: WARNING: Old socket at ' . $socketFile . ' found. Will not start.');
+
+ // Shutdown this socket
+ $this->shutdownSocket($mainSocket);
+
+ // Quit here
+ exit;
+ } // END - if
+
+ // Debug message
+ self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Binding to ' . $socketFile . ' ...');
+
+ // Try to bind to it
+ if (!socket_bind($mainSocket, $socketFile)) {
+ // Handle error here
+ $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array($socketFile, '0'));
+ /*
+ // Get socket error code for verification
+ $socketError = socket_last_error($mainSocket);
+
+ // Get error message
+ $errorMessage = socket_strerror($socketError);
+
+ // Shutdown this socket
+ $this->shutdownSocket($mainSocket);
+
+ // And throw again
+ throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
+ */
+ } // END - if
+
+ // Start listen for connections
+ self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Listening for connections.');
+ if (!socket_listen($mainSocket)) {
+ // Handle this socket error with a faked recipientData array
+ $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array($socketFile, '0'));
+ /*
+ // Get socket error code for verification
+ $socketError = socket_last_error($mainSocket);
+
+ // Get error message
+ $errorMessage = socket_strerror($socketError);
+
+ // Shutdown this socket
+ $this->shutdownSocket($mainSocket);
+
+ // And throw again
+ throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
+ */
+ } // END - if
+
+ // Now, we want non-blocking mode
+ self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Setting non-blocking mode.');
+ if (!socket_set_nonblock($mainSocket)) {
+ // Handle this socket error with a faked recipientData array
+ $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array($socketFile, '0'));
+ /*
+ // Get socket error code for verification
+ $socketError = socket_last_error($mainSocket);
+
+ // Get error message
+ $errorMessage = socket_strerror($socketError);
+
+ // Shutdown this socket
+ $this->shutdownSocket($mainSocket);
+
+ // And throw again
+ throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET);
+ */
+ } // END - if
+
+ // Set the main socket
+ $this->registerServerSocketResource($mainSocket);
+
+ // Initialize the peer pool instance
+ $poolInstance = ObjectFactory::createObjectByConfiguredName('application_pool_class', array($this));
+
+ // Add main socket
+ $poolInstance->addPeer($mainSocket, BaseConnectionHelper::CONNECTION_TYPE_SERVER);
+
+ // And add it to this listener
+ $this->setPoolInstance($poolInstance);
+
+ // Initialize iterator for listening on packages
+ $iteratorInstance = ObjectFactory::createObjectByConfiguredName('socket_listen_iterator_class', array($poolInstance->getPoolEntriesInstance()));
+
+ // Rewind it and remember it in this class
+ $iteratorInstance->rewind();
+ $this->setIteratorInstance($iteratorInstance);
+
+ // Initialize the raw data handler
+ $handlerInstance = ObjectFactory::createObjectByConfiguredName('socket_raw_data_handler_class');
+
+ // Set it in this class
+ $this->setHandlerInstance($handlerInstance);
+
+ // Output message
+ self::createDebugInstance(__CLASS__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: TCP listener now ready on IP ' . $this->getListenAddress() . ', port ' . $this->getListenPort() . ' for service.');
}
/**
} // END - if
// And print it out...
- printf('%s%s', $output, chr(10));
+ printf('%s%s', str_replace('->', '->', $output), PHP_EOL);
}
/**
--- /dev/null
+<?php
+/**
+ *
+ * @author Roland Haeder <webmaster@shipsimu.org>
+ * @version 0.0.0
+ * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Core Developer Team
+ * @license GNU GPL 3.0 or any newer version
+ * @link http://www.shipsimu.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+class SocketBindingException extends AbstractSocketException {
+ /**
+ * A Constructor for this exception
+ *
+ * @param $messageArray Error message array
+ * @param $code Error code
+ * @return void
+ */
+ public function __construct (array $messageData, $code) {
+ // Construct the message
+ $message = sprintf('[%s:] Could not bind to socket %s: Type=%s, errno=%s, errstr=%s',
+ $messageData[0]->__toString(),
+ $messageData[1][0],
+ gettype($messageData[2]),
+ $messageData[3],
+ $messageData[4]
+ );
+
+ // Call parent exception constructor
+ parent::__construct($message, $code);
+ }
+}
+
+// [EOF]
+?>
--- /dev/null
+
+ /**
+ * Adds given hash to an index file
+ *
+ * @param $stackName Name of stack to add hash
+ * @param $data Hash and gap position to be added to the index
+ * @return void
+ */
+ function addHashToIndex ($stackName, array $data);