Continued:
authorRoland Haeder <roland@mxchange.org>
Sat, 12 Sep 2015 19:11:08 +0000 (21:11 +0200)
committerRoland Haeder <roland@mxchange.org>
Sat, 12 Sep 2015 19:11:34 +0000 (21:11 +0200)
- 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>
inc/config.php
inc/main/classes/class_BaseFrameworkSystem.php
inc/main/classes/handler/raw_data/class_BaseDataHandler.php [new file with mode: 0644]
inc/main/classes/handler/raw_data/network/.htaccess [new file with mode: 0644]
inc/main/classes/handler/raw_data/network/class_ [new file with mode: 0644]
inc/main/classes/handler/raw_data/network/class_BaseRawDataHandler.php [new file with mode: 0644]
inc/main/classes/listener/class_BaseListener.php
inc/main/classes/listener/socket/class_SocketFileListener.php
inc/main/classes/output/debug/console/class_DebugConsoleOutput.php
inc/main/exceptions/socket/class_SocketBindingException.php [new file with mode: 0644]
inc/main/interfaces/index/stack/class_IndexableStack.ph [new file with mode: 0644]

index bc6f8e1aded156aaf1d675fbb4479d6ca36be539..7d263be3a7e7d61eee7e70e3d4ac544c40aa5f63 100644 (file)
@@ -458,5 +458,8 @@ $cfg->setConfigEntry('menu_template_extension', '.xml');
 // CFG: FEATURE-FUSE-CLASS
 $cfg->setConfigEntry('feature_fuse_class', 'FuseFeature');
 
+// CFG: TEMP-FILE-PATH
+$cfg->setConfigEntry('temp_file_path', sys_get_temp_dir());
+
 // [EOF]
 ?>
index 7bee5ecac0fe15f427db47e30dbb4c57726dc67f..74d92235ee0d9c071e421b741b72576017ab3c9c 100644 (file)
@@ -495,44 +495,52 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
                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) . '!';
@@ -1853,7 +1861,7 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
                // 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>');
@@ -2026,7 +2034,7 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
                // 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)
                        );
@@ -3331,7 +3339,7 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
         * @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;
 
@@ -3374,6 +3382,60 @@ class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
                // 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]
diff --git a/inc/main/classes/handler/raw_data/class_BaseDataHandler.php b/inc/main/classes/handler/raw_data/class_BaseDataHandler.php
new file mode 100644 (file)
index 0000000..b80f1ef
--- /dev/null
@@ -0,0 +1,197 @@
+<?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]
+?>
diff --git a/inc/main/classes/handler/raw_data/network/.htaccess b/inc/main/classes/handler/raw_data/network/.htaccess
new file mode 100644 (file)
index 0000000..3a42882
--- /dev/null
@@ -0,0 +1 @@
+Deny from all
diff --git a/inc/main/classes/handler/raw_data/network/class_ b/inc/main/classes/handler/raw_data/network/class_
new file mode 100644 (file)
index 0000000..b452a2b
--- /dev/null
@@ -0,0 +1,68 @@
+<?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]
+?>
diff --git a/inc/main/classes/handler/raw_data/network/class_BaseRawDataHandler.php b/inc/main/classes/handler/raw_data/network/class_BaseRawDataHandler.php
new file mode 100644 (file)
index 0000000..4a6c3c6
--- /dev/null
@@ -0,0 +1,183 @@
+<?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]
+?>
index 5a5721353c7c11b98aebd638cfd138119468715b..bd807bd7f0abcffd9817554ec55608d981969a7e 100644 (file)
@@ -286,6 +286,219 @@ class BaseListener extends BaseFrameworkSystem implements Visitable {
        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]
index 4dea353cdb831787198152657fffeedefa5a61d5..35469d8c64d282c4e4475dee78e449ef8f57f50f 100644 (file)
@@ -52,10 +52,136 @@ class SocketFileListener extends BaseListener implements Listenable {
         * 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.');
        }
 
        /**
index 3598cc18e74804a62f536d465863638c275d0cdf..d1de0cab09a16e5e2af834dbac0ba4d6e041c72d 100644 (file)
@@ -69,7 +69,7 @@ class DebugConsoleOutput extends BaseDebugOutput implements Debugger, OutputStre
                } // END - if
 
                // And print it out...
-               printf('%s%s', $output, chr(10));
+               printf('%s%s', str_replace('-&gt;', '->', $output), PHP_EOL);
        }
 
        /**
diff --git a/inc/main/exceptions/socket/class_SocketBindingException.php b/inc/main/exceptions/socket/class_SocketBindingException.php
new file mode 100644 (file)
index 0000000..cc52b99
--- /dev/null
@@ -0,0 +1,47 @@
+<?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]
+?>
diff --git a/inc/main/interfaces/index/stack/class_IndexableStack.ph b/inc/main/interfaces/index/stack/class_IndexableStack.ph
new file mode 100644 (file)
index 0000000..ff3d15f
--- /dev/null
@@ -0,0 +1,9 @@
+
+       /**
+        * 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);