--1731:ChangeLog:-- Release r0 @ 2008-06-07 22:18:46+0200> { Heads up! ~~~~~~~~~ - Deprecated xml.XML class (friebe) RFCs ~~~~ - Implemented RFC #0166: XP Runners (friebe) - Implemented RFC #0165: New text.regex package (friebe) - Implemented RFC #0163: Respect ANSI SQL in rdbms package (kiesel) - Implemented RFC #0157: Make product, language and session optional URL parts (kiesel) Bugfixes ~~~~~~~~ - Made HttpScriptlet::process() rethrow HttpScriptletExceptions as-is, while any other exception is wrapped into a HttpScriptletException. (friebe). - Fixed bug when scriptlet.xml.workflow.casters.ToDate accepted an abbreviated date, interpreted it as a time and used the current date with given time instead the given date (kiesel) - Fixed session id initialization in XMLScriptletRequest::initialize() (hinckel) - Fixed rdbms.Record::set() method (Michael Bayer, kiesel) Features ~~~~~~~~ - Added lang.Process::newInstance() method http://news.xp-framework.net/article/258/2008/05/28/New_Process (friebe) - Added " @ {host}" to rdbms.SQLConnectionClosedException's message (friebe) - Changed XAR file format (BC-break-free) to be less memory-consuming, platform-independent and get rid of original filename junk (friebe) - Added a parameter "bool append" (defaulting to FALSE) to io.streams.FileOutputStream's constructor (friebe) - Made io.streams.FileInputStream and io.streams.FileOutputStream's constructors accept either an io.File or a string with the filename (friebe) - Added syntactical support in text.doclet.markup API for ordered and unordered lists. (friebe) } --17433:lang.base.php:-- array(), 'sapi' => array(), 'class.xp' => '', 'class.null' => '', ); // {{{ public string loadClass0(string name) // Loads a class by its fully qualified name function loadClass0($class) { if (isset(xp::$registry['classloader.'.$class])) { return substr(array_search($class, xp::$registry), 6); } $package= NULL; foreach (xp::$registry['classpath'] as $path) { // If path is a directory and the included file exists, load it if (is_dir($path) && file_exists($f= $path.DIRECTORY_SEPARATOR.strtr($class, '.', DIRECTORY_SEPARATOR).xp::CLASS_FILE_EXT)) { if (FALSE === ($r= include($f))) { xp::error('Cannot bootstrap class '.$class.' (from file "'.$f.'")'); } xp::$registry['classloader.'.$class]= 'FileSystemClassLoader://'.$path; break; } else if (is_file($path) && file_exists($f= 'xar://'.$path.'?'.strtr($class, '.', '/').xp::CLASS_FILE_EXT)) { // To to load via bootstrap class loader, if the file cannot provide the class-to-load // skip to the next include_path part if (FALSE === ($r= include($f))) { continue; } xp::$registry['classloader.'.$class]= 'ArchiveClassLoader://'.$path; break; } } // Verify the requested class could be loaded if (!isset(xp::$registry['classloader.'.$class])) { xp::error('Cannot bootstrap class '.$class.' (include_path= '.get_include_path().')'); } // Register class name and call static initializer if available $name= ($package ? strtr($package, '.', '').'' : '').xp::reflect($class); xp::$registry['class.'.$name]= $class; is_callable(array($name, '__static')) && call_user_func(array($name, '__static')); return $name; } // }}} // {{{ public string nameOf(string name) // Returns the fully qualified name static function nameOf($name) { if (!($n= xp::registry('class.'.$name))) { return $name ? 'php.'.$name : NULL; } return $n; } // }}} // {{{ public string typeOf(mixed arg) // Returns the fully qualified type name static function typeOf($arg) { return is_object($arg) ? xp::nameOf(get_class($arg)) : gettype($arg); } // }}} // {{{ public string stringOf(mixed arg [, string indent default '']) // Returns a string representation of the given argument static function stringOf($arg, $indent= '') { static $protect= array(); if (is_string($arg)) { return '"'.$arg.'"'; } else if (is_bool($arg)) { return $arg ? 'true' : 'false'; } else if (is_null($arg)) { return 'null'; } else if ($arg instanceof null) { return ''; } else if (is_int($arg) || is_float($arg)) { return (string)$arg; } else if ($arg instanceof Generic && !isset($protect[$arg->hashCode()])) { $protect[$arg->hashCode()]= TRUE; $s= $arg->toString(); unset($protect[$arg->hashCode()]); return $s; } else if (is_array($arg)) { $ser= serialize($arg); if (isset($protect[$ser])) return '->{:recursion:}'; $protect[$ser]= TRUE; $r= "[\n"; foreach (array_keys($arg) as $key) { $r.= $indent.' '.$key.' => '.xp::stringOf($arg[$key], $indent.' ')."\n"; } unset($protect[$ser]); return $r.$indent.']'; } else if (is_object($arg)) { $ser= serialize($arg); if (isset($protect[$ser])) return '->{:recursion:}'; $protect[$ser]= TRUE; $r= xp::nameOf(get_class($arg))." {\n"; $vars= (array)$arg; foreach (array_keys($vars) as $key) { $r.= $indent.' '.$key.' => '.xp::stringOf($vars[$key], $indent.' ')."\n"; } unset($protect[$ser]); return $r.$indent.'}'; } else if (is_resource($arg)) { return 'resource(type= '.get_resource_type($arg).', id= '.(int)$arg.')'; } } // }}} // {{{ public void gc() // Runs the garbage collector static function gc() { xp::$registry['errors']= array(); } // }}} // {{{ public null() // Runs a fatal-error safe version of NULL static function null() { return xp::$registry['null']; } // }}} // {{{ public bool errorAt(string file [, int line) // Returns whether an error occured at the specified position static function errorAt($file, $line= -1) { $errors= xp::$registry['errors']; // If no line is given, check for an error in the file if ($line < 0) return !empty($errors[$file]); // Otherwise, check for an error in the file on a certain line return !empty($errors[$file][$line]); } // }}} // {{{ public mixed sapi(string* sapis) // Sets an SAPI static function sapi() { foreach ($a= func_get_args() as $name) { foreach (xp::$registry['classpath'] as $path) { $filename= 'sapi'.DIRECTORY_SEPARATOR.strtr($name, '.', DIRECTORY_SEPARATOR).'.sapi.php'; if (is_dir($path) && file_exists($f= $path.DIRECTORY_SEPARATOR.$filename)) { require_once($f); continue 2; } else if (is_file($path) && file_exists($f= 'xar://'.$path.'?'.strtr($filename, DIRECTORY_SEPARATOR, '/'))) { require_once($f); continue 2; } } xp::error('Cannot open SAPI '.$name.' (include_path='.get_include_path().')'); } xp::$registry['sapi']= $a; } // }}} // {{{ internal mixed registry(mixed args*) // Stores static data static function registry() { switch (func_num_args()) { case 0: return xp::$registry; case 1: return @xp::$registry[func_get_arg(0)]; case 2: xp::$registry[func_get_arg(0)]= func_get_arg(1); break; } return NULL; } // }}} // {{{ internal string reflect(string str) // Retrieve PHP conformant name for fqcn static function reflect($str) { $l= array_search($str, xp::$registry); return $l ? substr($l, 6) : substr($str, (FALSE === $p= strrpos($str, '.')) ? 0 : $p+ 1); } // }}} // {{{ internal void error(string message) // Throws a fatal error and exits with exitcode 61 static function error($message) { restore_error_handler(); trigger_error($message, E_USER_ERROR); exit(0x3d); } } // }}} // {{{ final class null class null { // {{{ public object __construct(void) // Constructor to avoid magic __call invokation public function __construct() { if (isset(xp::$registry['null'])) { throw new IllegalAccessException('Cannot create new instances of xp::null()'); } } // {{{ public void __clone(void) // Clone interceptor public function __clone() { throw new NullPointerException('Object cloning intercepted.'); } // }}} // {{{ magic mixed __call(string name, mixed[] args) // Call proxy function __call($name, $args) { throw new NullPointerException('Method.invokation('.$name.')'); } // }}} // {{{ magic void __set(string name, mixed value) // Set proxy function __set($name, $value) { throw new NullPointerException('Property.write('.$name.')'); } // }}} // {{{ magic mixed __get(string name) // Set proxy function __get($name) { throw new NullPointerException('Property.read('.$name.')'); } // }}} } // }}} // {{{ final class xploader class xarloader { public $position = 0, $archive = '', $filename = ''; // {{{ static mixed[] acquire(string archive) // Archive instance handling pool function, opens an archive and reads header only once static function acquire($archive) { static $archives= array(); static $unpack= array( 1 => 'a80id/a80*filename/a80*path/V1size/V1offset/a*reserved', 2 => 'a240id/V1size/V1offset/a*reserved' ); if (!isset($archives[$archive])) { $archives[$archive]= array(); $current= &$archives[$archive]; $current['handle']= fopen($archive, 'rb'); $header= unpack('a3id/c1version/V1indexsize/a*reserved', fread($current['handle'], 0x0100)); if ('CCA' != $header['id']) raise('lang.FormatException', 'Malformed archive '.$archive); for ($current['index']= array(), $i= 0; $i < $header['indexsize']; $i++) { $entry= unpack( $unpack[$header['version']], fread($current['handle'], 0x0100) ); $current['index'][$entry['id']]= array($entry['size'], $entry['offset']); } } return $archives[$archive]; } // }}} // {{{ function bool stream_open(string path, string mode, int options, string opened_path) // Open the given stream and check if file exists function stream_open($path, $mode, $options, $opened_path) { sscanf($path, 'xar://%[^?]?%[^$]', $archive, $file); $this->archive= urldecode($archive); $this->filename= $file; $current= self::acquire($this->archive); return isset($current['index'][$this->filename]); } // }}} // {{{ string stream_read(int count) // Read $count bytes up-to-length of file function stream_read($count) { $current= self::acquire($this->archive); if (!isset($current['index'][$this->filename])) return FALSE; if ($current['index'][$this->filename][0] == $this->position || 0 == $count) return FALSE; fseek($current['handle'], 0x0100 + sizeof($current['index']) * 0x0100 + $current['index'][$this->filename][1] + $this->position, SEEK_SET); $bytes= fread($current['handle'], min($current['index'][$this->filename][0]- $this->position, $count)); $this->position+= strlen($bytes); return $bytes; } // }}} // {{{ bool stream_eof() // Returns whether stream is at end of file function stream_eof() { $current= self::acquire($this->archive); return $this->position >= $current['index'][$this->filename][0]; } // }}} // {{{ stream_stat() // Retrieve status of stream function stream_stat() { $current= self::acquire($this->archive); return array( 'size' => $current['index'][$this->filename][0] ); } // }}} // {{{ bool stream_seek(int offset, int whence) // Callback for fseek function stream_seek($offset, $whence) { switch ($whence) { case SEEK_SET: $this->position= $offset; break; case SEEK_CUR: $this->position+= $offset; break; case SEEK_END: $current= self::acquire($this->archive); $this->position= $current['index'][$this->filename][0] + $offset; break; } return TRUE; } // }}} // {{{ int stream_tell // Callback for ftell function stream_tell() { return $this->position; } // }}} // {{{ url_stat(string path) // Retrieve status of url function url_stat($path) { sscanf($path, 'xar://%[^?]?%[^$]', $archive, $file); $current= self::acquire(urldecode($archive)); return isset($current['index'][$file]) ? array('size' => $current['index'][$file][0]) : FALSE ; } // }}} } // }}} // {{{ internal void __error(int code, string msg, string file, int line) // Error callback function __error($code, $msg, $file, $line) { if (0 == error_reporting() || is_null($file)) return; if (E_RECOVERABLE_ERROR == $code) { throw new IllegalArgumentException($msg.' @ '.$file.':'.$line); } else { @xp::$registry['errors'][$file][$line][$msg]++; } } // }}} // {{{ void uses (string* args) // Uses one or more classes function uses() { foreach (func_get_args() as $str) xp::$registry['loader']->loadClass0($str); } // }}} // {{{ void raise (string classname, mixed* args) // throws an exception by a given class name function raise($classname) { try { $class= XPClass::forName($classname); } catch (ClassNotFoundException $e) { xp::error($e->getMessage()); } $a= func_get_args(); throw call_user_func_array(array($class, 'newInstance'), array_slice($a, 1)); } // }}} // {{{ void finally (void) // Syntactic sugar. Intentionally empty function finally() { } // }}} // {{{ Generic cast (Generic expression, string type) // Casts an expression. function cast(Generic $expression= NULL, $type) { if (NULL === $expression) { return xp::null(); } else if (XPClass::forName($type)->isInstance($expression)) { return $expression; } raise('lang.ClassCastException', 'Cannot cast '.xp::typeOf($expression).' to '.$type); } // {{{ proto bool is(string class, lang.Object object) // Checks whether a given object is of the class, a subclass or implements an interface function is($class, $object) { if (NULL === $class) return $object instanceof null; $class= xp::reflect($class); return $object instanceof $class; } // }}} // {{{ proto void delete(&lang.Object object) // Destroys an object function delete(&$object) { $object= NULL; } // }}} // {{{ proto void with(expr) // Syntactic sugar. Intentionally empty function with() { } // }}} // {{{ proto mixed ref(mixed object) // Creates a "reference" to an object function ref(&$object) { return array(&$object); } // }}} // {{{ proto &mixed deref(&mixed expr) // Dereferences an expression function &deref(&$expr) { if (is_array($expr)) return $expr[0]; else return $expr; } // }}} // {{{ proto lang.Object newinstance(string classname, mixed[] args, string bytes) // Anonymous instance creation function newinstance($classname, $args, $bytes) { static $u= 0; $class= xp::reflect($classname); if (!class_exists($class) && !interface_exists($class)) { xp::error(xp::stringOf(new Error('Class "'.$classname.'" does not exist'))); // Bails } $name= $class.''.(++$u); // Checks whether an interface or a class was given $cl= DynamicClassLoader::instanceFor(__FUNCTION__); if (interface_exists($class)) { $cl->setClassBytes($name, 'class '.$name.' extends Object implements '.$class.' '.$bytes); } else { $cl->setClassBytes($name, 'class '.$name.' extends '.$class.' '.$bytes); } $cl->loadClass0($name); // Build paramstr for evaluation for ($paramstr= '', $i= 0, $m= sizeof($args); $i < $m; $i++) { $paramstr.= ', $args['.$i.']'; } return eval('return new '.$name.'('.substr($paramstr, 2).');'); } // }}} // {{{ lang.Generic create(mixed spec) // Creates a generic object function create($spec) { if ($spec instanceof Generic) return $spec; sscanf($spec, 'new %[^<]<%[^>]>', $classname, $types); $class= xp::reflect($classname); // Check whether class is generic if (!property_exists($class, '__generic')) { throw new IllegalArgumentException('Class '.$classname.' is not generic'); } // Instanciate without invoking the constructor and pass type information. // This is done so that the constructur can already use generic types. $__id= microtime(); $instance= unserialize('O:'.strlen($class).':"'.$class.'":1:{s:4:"__id";s:'.strlen($__id).':"'.$__id.'";}'); foreach (explode(',', $types) as $type) { $instance->__generic[]= xp::reflect(trim($type)); } // Call constructor if available if (is_callable(array($instance, '__construct'))) { $a= func_get_args(); call_user_func_array(array($instance, '__construct'), array_slice($a, 1)); } return $instance; } // }}} // {{{ initialization error_reporting(E_ALL); // Get rid of magic quotes get_magic_quotes_gpc() && xp::error('[xp::core] magic_quotes_gpc enabled'); ini_set('magic_quotes_runtime', FALSE); // Constants define('LONG_MAX', PHP_INT_MAX); define('LONG_MIN', -PHP_INT_MAX - 1); // Hooks set_error_handler('__error'); // Registry initialization xp::$registry['null']= new null(); xp::$registry['loader']= new xp(); xp::$registry['classpath']= array_filter(array_map('realpath', explode(PATH_SEPARATOR, get_include_path()))); // Register stream wrapper for .xar class loading stream_wrapper_register('xar', 'xarloader'); // Omnipresent classes uses( 'lang.Object', 'lang.Error', 'lang.XPException', 'lang.XPClass', 'lang.NullPointerException', 'lang.IllegalAccessException', 'lang.IllegalArgumentException', 'lang.IllegalStateException', 'lang.FormatException', 'lang.ClassLoader' ); // }}} ?> --69:bin/boot.pth:-- .. ../lib/xp-rt-5.6.7RC3.xar ../lib/xp-net.xp_framework-5.6.7RC3.xar --2606189:lib/xp-rt-5.6.7RC3.xar:-- CCAAlang/archive/Archive.class.php#"lang/archive/ArchiveClassLoader.class.php#"lang/archive/ArchiveReader.class.php3=lang/archive/package-info.xp Qlang/ChainedException.class.php- Tlang/ClassCastException.class.php 6\lang/ClassLoader.class.php/B]lang/ClassNotFoundException.class.phplang/CloneNotSupportedException.class.php@lang/Collection.class.php7 lang/DynamicClassLoader.class.phpl"lang/ElementNotFoundException.class.phplang/Enum.class.php lang/Error.class.phplang/FileSystemClassLoader.class.phpYlang/FormatException.class.php lang/Generic.class.php"lang/IClassLoader.class.php-lang/IllegalAccessException.class.php lang/IllegalArgumentException.class.phpvlang/IllegalStateException.class.phplang/IllegalThreadStateException.class.php"lang/IndexOutOfBoundsException.class.phpq7lang/MethodNotImplementedException.class.phplang/NullPointerException.class.phpWd lang/Object.class.php- lang/package-info.xplang/Primitive.class.php, %lang/Process.class.php2lang/reflect/Constructor.class.php Nlang/reflect/Field.class.php| Xlang/reflect/InvocationHandler.class.phpdlang/reflect/Method.class.phpg hlang/reflect/Modifiers.class.phpz qrlang/reflect/package-info.xplang/reflect/Package.class.phplang/reflect/Parameter.class.phpZ lang/reflect/Proxy.class.phplang/reflect/Routine.class.php!lang/reflect/TargetInvocationException.class.php5lang/Runnable.class.phpo lang/Runtime.class.php xlang/RuntimeError.class.php=lang/StackTraceElement.class.phpU :lang/System.class.phplang/SystemException.class.phpV lang/Thread.class.php"lang/Throwable.class.php>x8lang/Type.class.phpQ Mlang/types/ArrayList.class.phpZlang/types/Boolean.class.php0llang/types/Byte.class.php4qlang/types/Bytes.class.phprlang/types/Character.class.php qlang/types/Double.class.phplang/types/Float.class.phplang/types/Integer.class.php=.lang/types/Long.class.php6klang/types/Number.class.phplang/types/package-info.xplang/types/Short.class.php:lang/types/String.class.php"lang/XPClass.class.phpJlang/XPException.class.php1util/AbstractDeferredInvokationHandler.class.php@util/Binford.class.phphCutil/Calendar.class.php!util/ChainedException.class.php^ >util/cmd/Command.class.phplGutil/cmd/Console.class.php QIutil/cmd/package-info.xplAUutil/cmd/ParamString.class.phpYutil/cmd/Runner.class.php+dlutil/cmd/SingleProcess.class.phpN_util/collections/DJBX33AHashImplementation.class.php util/collections/HashImplementation.class.phputil/collections/HashProvider.class.php^util/collections/HashSet.class.php =util/collections/HashTable.class.phpIutil/collections/IList.class.phpJutil/collections/LRUBuffer.class.php util/collections/Map.class.php6 util/collections/MD5HashImplementation.class.phpHutil/collections/package-info.xpQ, util/collections/Queue.class.php}util/collections/Set.class.phpD4$util/collections/Stack.class.phpx+util/collections/Vector.class.php7%;util/Comparator.class.phpo`util/Component.class.php9cutil/Configurable.class.phpfutil/Date.class.phpd*hutil/DateInterval.class.phps-util/DateMath.class.phputil/DateUtil.class.phpKutil/DeferredInitializationException.class.phpSutil/Hashmap.class.phpJ"jutil/HashmapIterator.class.phpCutil/io/VirtualFileManager.class.phpButil/Locale.class.php 9util/log/BufferedAppender.class.phputil/log/ColoredConsoleAppender.class.phputil/log/ConsoleAppender.class.php:util/log/FileAppender.class.phpxutil/log/LogAppender.class.phpXVutil/log/LogCategory.class.php! util/log/Logger.class.phpi+util/log/LogObserver.class.phpFutil/log/package-info.xpVJutil/log/SmtpAppender.class.php0-Outil/log/SyslogAppender.class.phpu]Vutil/log/Traceable.class.php[util/MimeType.class.php*]util/NoSuchElementException.class.phpn=util/Observable.class.php util/Observer.class.php˔util/package-info.xpk qutil/profiling/ClassProfiler.class.phpܠutil/profiling/Timer.class.phputil/Properties.class.php2Xutil/PropertyManager.class.phputil/semaphore/Semaphore.class.phputil/semaphore/SessionSemaphore.class.php tutil/ServiceException.class.phputil/telephony/TelephonyAddress.class.phputil/telephony/TelephonyAddressParser.class.php@=util/telephony/TelephonyCall.class.phpButil/telephony/TelephonyException.class.phpFutil/telephony/TelephonyProvider.class.phpRGutil/telephony/TelephonyTerminal.class.phpUutil/TimeSpan.class.php2Yutil/TimeZone.class.phpwutil/TimeZoneTransition.class.php#util/Visitor.class.phputil/XPIterator.class.php|io/ByteOrder.class.phpYio/collections/CollectionComposite.class.phpO ^io/collections/FileCollection.class.php io/collections/FileElement.class.phpIBio/collections/IOCollection.class.phpio/collections/IOElement.class.phpio/collections/iterate/AbstractCombinedFilter.class.phpv io/collections/iterate/AbstractDateComparisonFilter.class.phpio/collections/iterate/AbstractSizeComparisonFilter.class.phpio/collections/iterate/AccessedAfterFilter.class.phpLio/collections/iterate/AccessedBeforeFilter.class.phpO io/collections/iterate/AllOfFilter.class.php~Xio/collections/iterate/AnyOfFilter.class.phpaio/collections/iterate/CollectionFilter.class.php 7io/collections/iterate/CreatedAfterFilter.class.phpG@io/collections/iterate/CreatedBeforeFilter.class.phpJio/collections/iterate/ExtensionEqualsFilter.class.phpio/collections/iterate/FilteredIOCollectionIterator.class.phpio/collections/iterate/IOCollectionIterator.class.phpio/collections/iterate/IterationFilter.class.phpwio/collections/iterate/ModifiedAfterFilter.class.phpLFio/collections/iterate/ModifiedBeforeFilter.class.phpO io/collections/iterate/NameEqualsFilter.class.php io/collections/iterate/NameMatchesFilter.class.php io/collections/iterate/NegationOfFilter.class.phpio/collections/iterate/RegexFilter.class.phpsio/collections/iterate/SizeBiggerThanFilter.class.php0io/collections/iterate/SizeEqualsFilter.class.php),io/collections/iterate/SizeSmallerThanFilter.class.php2Uio/collections/package-info.xp!io/dba/DBAFile.class.phpI&io/dba/DBAIterator.class.phpAio/EmbeddedFile.class.phpTGio/EncapsedStream.class.phpMio/File.class.phpDeio/FileNotFoundException.class.phpUio/FilePermission.class.phpio/FileUtil.class.phpio/Folder.class.php2رio/IOException.class.php io/OperationTimedOutException.class.phpYio/package-info.xpU io/SearchableStream.class.phpaio/SpoolDirectory.class.phpKio/Stream.class.phpio/streams/BufferedInputStream.class.php- io/streams/BufferedOutputStream.class.php,io/streams/ConsoleOutputStream.class.php0#io/streams/FileInputStream.class.phpSio/streams/FileOutputStream.class.php8%io/streams/InputStream.class.phpo+io/streams/MemoryInputStream.class.php-io/streams/MemoryOutputStream.class.php4io/streams/OutputStream.class.php8io/streams/OutputStreamWriter.class.php:io/streams/package-info.xp>io/streams/Seekable.class.phpsAio/streams/Streams.class.php=Dio/streams/StringWriter.class.phpO &Wio/sys/Ftok.class.phpu`io/sys/IPCMessage.class.phpjfio/sys/IPCQueue.class.php^jio/sys/Semaphore.class.phpk Iio/sys/ShmSegment.class.php io/sys/StdStream.class.php(_io/TempFile.class.phpUio/ZipFile.class.phpOܦsapi/cgi.sapi.php+sapi/class.sapi.phpsapi/cli.sapi.phpZNsapi/gui/gtk.sapi.phpsapi/scriptlet/development.sapi.phpB_sapi/scriptlet/error400.htmlsapi/scriptlet/error403.htmlsapi/scriptlet/error404.htmlsapi/scriptlet/error405.htmlssapi/scriptlet/error406.htmlsapi/scriptlet/error500.htmlsapi/scriptlet/production.sapi.phpsapi/scriptlet/ScriptletRunner.class.phpsapi/soap/client.sapi.phpTsapi/soap/service.sapi.php sapi/strict.sapi.php sapi/synchronized.sapi.php1 sapi/xmlrpc/client.sapi.php sapi/xmlrpc/service.sapi.php peer/AuthenticationException.class.php peer/BSDSocket.class.phph [ peer/ConnectException.class.phpV9 peer/ftp/DefaultFtpListParser.class.php; peer/ftp/FtpConnection.class.php$A peer/ftp/FtpDir.class.phpI%f peer/ftp/FtpDownload.class.phpC peer/ftp/FtpEntry.class.php9 peer/ftp/FtpEntryList.class.phpը peer/ftp/FtpFile.class.php  peer/ftp/FtpListIterator.class.php peer/ftp/FtpListParser.class.php9 peer/ftp/FtpTransfer.class.php! peer/ftp/FtpTransferListener.class.php peer/ftp/FtpUpload.class.php peer/ftp/package-info.xpr peer/ftp/server/FtpConnectionListener.class.php# peer/ftp/server/FtpSession.class.phpA k peer/ftp/server/FtpThread.class.phpw peer/ftp/server/interceptor/DefaultInterceptor.class.php peer/ftp/server/interceptor/EntrynameInterceptor.class.phpȕ peer/ftp/server/interceptor/InterceptorCondition.class.php| peer/ftp/server/interceptor/PathCondition.class.phpi peer/ftp/server/interceptor/StorageActionInterceptor.class.php peer/ftp/server/storage/FilesystemStorage.class.php peer/ftp/server/storage/FilesystemStorageCollection.class.phpcR peer/ftp/server/storage/FilesystemStorageElement.class.php peer/ftp/server/storage/Storage.class.php peer/ftp/server/storage/StorageCollection.class.phpJ peer/ftp/server/storage/StorageElement.class.php peer/ftp/server/storage/StorageEntry.class.php peer/ftp/WindowsFtpListParser.class.php peer/Header.class.phpT peer/http/BasicAuthorization.class.php peer/http/HttpConnection.class.phpt peer/http/HttpConstants.class.php m peer/http/HttpInputStream.class.php{f$ peer/http/HttpRequest.class.phps+ peer/http/HttpRequestFactory.class.phpT= peer/http/HttpResponse.class.phpscriptlet/xml/workflow/checkers/LengthChecker.class.phpCscriptlet/xml/workflow/checkers/NumberRangeChecker.class.phpHscriptlet/xml/workflow/checkers/NumericChecker.class.phpMscriptlet/xml/workflow/checkers/OptionChecker.class.phpkPscriptlet/xml/workflow/checkers/ParamChecker.class.phpTscriptlet/xml/workflow/checkers/RegexpChecker.class.phpUscriptlet/xml/workflow/checkers/WordCountChecker.class.phpYscriptlet/xml/workflow/Context.class.phpeQ]scriptlet/xml/workflow/FileData.class.phprdscriptlet/xml/workflow/generator/common.inc.xsl(kscriptlet/xml/workflow/generator/handler.xslqscriptlet/xml/workflow/generator/wrapper.dtd[scriptlet/xml/workflow/generator/wrapper.xsdG ݄scriptlet/xml/workflow/generator/wrapper.xsl#$scriptlet/xml/workflow/Handler.class.php@scriptlet/xml/workflow/IFormresultAggregate.class.phpscriptlet/xml/workflow/WorkflowScriptletRequest.class.phpscriptlet/xml/workflow/Wrapper.class.php~'scriptlet/xml/XMLScriptlet.class.phpJ!scriptlet/xml/XMLScriptletRequest.class.phpJdscriptlet/xml/XMLScriptletResponse.class.php#0xml/CData.class.phpTxml/dom/Document.class.phphWxml/DomXSLProcessor.class.php-?hxml/IXSLProcessor.class.php xml/meta/Marshaller.class.php xml/meta/Unmarshaller.class.phpxml/Node.class.php"xml/package-info.xpOxml/parser/ParserCallback.class.php1xml/parser/XMLParser.class.php Cxml/PCData.class.phpxml/QName.class.php>vxml/rdf/RDFNewsFeed.class.phpT,xml/Stylesheet.class.phpO 5xml/TransformerException.class.phpWBxml/Tree.class.phpCxml/wsdl/wsdl.xsl^MYxml/XML.class.phpf5xml/XMLFormatException.class.phpxml/XPath.class.phpN xml/XPathException.class.phpFmxml/XSLCallback.class.php xml/xslt/XSLDateCallback.class.phpBxml/xslt/XSLStringCallback.class.phpQremote/beans/Bean.class.phpremote/beans/BeanInterface.class.phpremote/beans/HomeInterface.class.phpremote/beans/RemoteInterface.class.php*Xremote/ClassReference.class.php/remote/ExceptionReference.class.phpremote/HandlerFactory.class.php8Mremote/HandlerInstancePool.class.php remote/InvocationException.class.phpremote/mappings/java/util/GregorianCalendarMapping.class.phpremote/mappings/java/util/ZoneInfoMapping.class.php wremote/NameNotFoundException.class.phpj[remote/package-info.xp=remote/protocol/ArrayListMapping.class.php(remote/protocol/ByteArrayMapping.class.php /remote/protocol/ByteCountedString.class.php; 4remote/protocol/ByteMapping.class.php~?remote/protocol/DateMapping.class.phpueEremote/protocol/DoubleMapping.class.php]Jremote/protocol/ExceptionMapping.class.php# 7Premote/protocol/FloatMapping.class.phpZ[remote/protocol/HashmapMapping.class.phpi`remote/protocol/IntegerMapping.class.phpGfremote/protocol/LongMapping.class.php~kremote/protocol/ProtocolHandler.class.phpUqremote/protocol/RemoteInterfaceMapping.class.phpr muremote/protocol/SerializedData.class.phpL~remote/protocol/Serializer.class.php-+remote/protocol/SerializerMapping.class.phpDremote/protocol/ShortMapping.class.phpremote/protocol/StackTraceElementMapping.class.phpremote/protocol/UnknownProtocolException.class.phpj*remote/protocol/XpProtocolConstants.class.phpremote/protocol/XpProtocolHandler.class.php5'lremote/reflect/BeanDescription.class.phpremote/reflect/ClassWrapper.class.phpIaremote/reflect/DescriptionList.class.phpremote/reflect/InterfaceDescription.class.php/~remote/reflect/MethodDescription.class.phplremote/reflect/TransactionTypeDescription.class.php/ remote/Remote.class.php H&remote/RemoteException.class.phpSb2remote/RemoteInvocationHandler.class.php3remote/RemoteStackTraceElement.class.php>8remote/server/container/BeanContainer.class.php[:remote/server/container/BeanInjector.class.phpABremote/server/container/StatelessSessionBeanContainer.class.php Hremote/server/ContainerInvocationHandler.class.phpRremote/server/ContainerManager.class.phpXVremote/server/deploy/Deployable.class.phpHYremote/server/deploy/Deployer.class.php F[remote/server/deploy/DeployException.class.phpR_eremote/server/deploy/Deployment.class.php fremote/server/deploy/IncompleteDeployment.class.phppremote/server/deploy/scan/DeploymentScanner.class.php!$tremote/server/deploy/scan/FileSystemScanner.class.php Evremote/server/deploy/scan/SharedMemoryScanner.class.phpremote/server/EascProtocol.class.phpQremote/server/InstancePool.class.php{remote/server/message/EascCallMessage.class.phptremote/server/message/EascExceptionMessage.class.phpremote/server/message/EascInitMessage.class.phpremote/server/message/EascLookupMessage.class.phpPCremote/server/message/EascMessage.class.php_remote/server/message/EascMessageFactory.class.php5remote/server/message/EascValueMessage.class.php'remote/server/naming/NamingDirectory.class.phpܵremote/server/RemoteObjectMap.class.phpremote/server/ScannerThread.class.php> $remote/server/ServerHandler.class.phpbremote/server/StatelessSessionBeanContainerInvocationHandler.class.phpFremote/UnknownRemoteObject.class.phpremote/UserTransaction.class.phpLtext/Collator.class.phpYtext/CSVGenerator.class.phpStext/doclet/AnnotatedDoc.class.phpDctext/doclet/AnnotationDoc.class.php text/doclet/ClassDoc.class.php,text/doclet/ClassIterator.class.phpbtext/doclet/Doc.class.phpi @text/doclet/Doclet.class.php!text/doclet/FieldDoc.class.php(text/doclet/markup/CodeProcessor.class.php1,text/doclet/markup/CopyProcessor.class.php:text/doclet/markup/DefaultProcessor.class.php6 >?text/doclet/markup/DelegatingProcessor.class.phptHtext/doclet/markup/MarkupBuilder.class.phpMtext/doclet/markup/MarkupProcessor.class.php[text/doclet/MethodDoc.class.phpw_text/doclet/ModelTag.class.php3ctext/doclet/ModelTaglet.class.phpAdtext/doclet/PackageDoc.class.phpftext/doclet/ParamTag.class.php}mtext/doclet/ParamTaglet.class.phpH ptext/doclet/ReturnTag.class.php Rstext/doclet/ReturnTaglet.class.php6[utext/doclet/RootDoc.class.php+Dxtext/doclet/SeeTag.class.phptext/doclet/SeeTaglet.class.php#otext/doclet/SimpleTaglet.class.phptext/doclet/Tag.class.phpxtext/doclet/Taglet.class.php text/doclet/TagletManager.class.php text/doclet/ThrowsTag.class.phpLtext/doclet/ThrowsTaglet.class.php:text/encode/Base57.class.php=text/encode/Base64.class.phptext/encode/CvsPassword.class.phptext/encode/QuotedPrintable.class.php}text/encode/UTF7.class.phpKtext/encode/UTF8.class.phpatext/encode/UUCode.class.phpCtext/format/ArrayFormat.class.phpttext/format/BinaryFormat.class.phptext/format/ChoiceFormat.class.php> text/format/DateFormat.class.php] text/format/HashFormat.class.php !text/format/IFormat.class.phpqAtext/format/MessageFormat.class.phptext/format/MoneyFormat.class.phpu3text/format/NumberFormat.class.php7text/format/PrintfFormat.class.php;text/parser/CSVParser.class.php|?text/parser/DaemonMailParser.class.phpv]text/parser/DaemonMailParserAutoresponderException.class.phptext/parser/DaemonMailParserException.class.phptext/parser/DaemonMessage.class.php}text/parser/DateParser.class.phptext/parser/generic/AbstractLexer.class.phptext/parser/generic/AbstractParser.class.phptext/parser/generic/ParseException.class.php5text/parser/generic/ParserMessage.class.phptext/parser/InternetAddressParser.class.php! Htext/parser/VFormatParser.class.phpQitext/PHPParser.class.phptext/PHPSyntaxHighlighter.class.php=text/PHPTokenizer.class.phpL zLtext/regex/MatchResult.class.phpDltext/regex/package-info.xp stext/regex/Pattern.class.php }text/StreamTokenizer.class.phpF text/String.class.php<Itext/StringTokenizer.class.phptext/StringUtil.class.phptext/Tokenizer.class.phptext/util/RandomCodeGenerator.class.phptext/util/RandomPasswordGenerator.class.phpFunittest/AssertionFailedError.class.phpdunittest/coverage/Block.class.phpWunittest/coverage/Comment.class.phpunittest/coverage/Expression.class.php unittest/coverage/Fragment.class.phpunittest/coverage/PHPCodeFragmentizer.class.phpunittest/gui/clear.xpm W#unittest/gui/collection.xpm 0unittest/gui/exception.xpm2unittest/gui/gtkui.gladeU!5unittest/gui/GtkUnitTestUI.class.php*1Wunittest/gui/open.xpmkunittest/gui/search.xpmounittest/gui/suite.xpmIRunittest/gui/test.xpmunittest/gui/test_failed.xpmBunittest/gui/test_skipped.xpmunittest/gui/test_succeeded.xpmunittest/gui/traceelement.xpmdunittest/package-info.xpunittest/PrerequisitesNotMetError.class.php6unittest/TestCase.class.phpunittest/TestFailure.class.php; unittest/TestListener.class.phpEunittest/TestResult.class.phpa Zunittest/TestSkipped.class.php@unittest/TestSuccess.class.phpunittest/TestSuite.class.phpF/webservices/json/IJsonDecoder.class.phpQ webservices/json/JsonDecoder.class.php^*Jwebservices/json/JsonException.class.php38webservices/json/JsonFactory.class.php9webservices/json/rpc/JsonClient.class.php<webservices/json/rpc/JsonMessage.class.php 9Dwebservices/json/rpc/JsonRequestMessage.class.phpCQwebservices/json/rpc/JsonResponseMessage.class.php=Wwebservices/json/rpc/JsonRpcRequest.class.php^webservices/json/rpc/JsonRpcResponse.class.phpawebservices/json/rpc/JsonRpcRouter.class.php]dwebservices/soap/CommonSoapFault.class.php-(iwebservices/soap/interop/Round2BaseClient.class.phpDUqwebservices/soap/native/NativeSoapClient.class.phpwebservices/soap/Parameter.class.phpwebservices/soap/rpc/SoapRpcRequest.class.phpY*webservices/soap/rpc/SoapRpcResponse.class.phpwebservices/soap/rpc/SoapRpcRouter.class.php webservices/soap/SoapDriver.class.phpgwebservices/soap/SOAPFaultException.class.phpwebservices/soap/transport/SOAPHTTPSTransport.class.phpwebservices/soap/transport/SOAPHTTPTransport.class.php&@webservices/soap/transport/SOAPTransport.class.php0fwebservices/soap/types/SOAPBase64Binary.class.phpwebservices/soap/types/SOAPDateTime.class.php_webservices/soap/types/SOAPDouble.class.phppwebservices/soap/types/SOAPHashMap.class.phpwebservices/soap/types/SOAPHexBinary.class.phpwebservices/soap/types/SOAPLong.class.phpwebservices/soap/types/SoapType.class.php qwebservices/soap/types/SOAPVector.class.php'{ webservices/soap/xp/XPSoapClient.class.php webservices/soap/xp/XPSoapHeader.class.php webservices/soap/xp/XPSoapHeaderElement.class.php webservices/soap/xp/XPSoapMapping.class.phpey( webservices/soap/xp/XPSoapMessage.class.phphL. webservices/soap/xp/XPSoapNode.class.phpF{ webservices/uddi/Business.class.phpԙ webservices/uddi/BusinessList.class.php& webservices/uddi/FindBusinessesCommand.class.phpJ ۧ webservices/uddi/InquiryCommand.class.php.% webservices/uddi/PublishCommand.class.php.S webservices/uddi/UDDICommand.class.php webservices/uddi/UDDIConstants.class.php1 webservices/uddi/UDDIServer.class.phpA webservices/wddx/transport/WddxHttpTransport.class.php @ webservices/wddx/transport/WddxTransport.class.php webservices/wddx/WddxClient.class.phpL_ webservices/wddx/WddxMessage.class.php webservices/xmlrpc/rpc/XmlRpcRequest.class.php webservices/xmlrpc/rpc/XmlRpcResponse.class.php!webservices/xmlrpc/rpc/XmlRpcRouter.class.phpQ!webservices/xmlrpc/transport/XmlRpcHttpTransport.class.php  !webservices/xmlrpc/transport/XmlRpcTransport.class.php!webservices/xmlrpc/XmlRpcClient.class.php !webservices/xmlrpc/XmlRpcDecoder.class.phphk(!webservices/xmlrpc/XmlRpcEncoder.class.php6!webservices/xmlrpc/XmlRpcFault.class.phpH!webservices/xmlrpc/XmlRpcFaultException.class.phpM!webservices/xmlrpc/XmlRpcMessage.class.php?R!webservices/xmlrpc/XmlRpcRequestMessage.class.phpS @f!webservices/xmlrpc/XmlRpcResponseMessage.class.phpo!img/chart/BarChart.class.php, ox!img/chart/Chart.class.php!img/chart/LineChart.class.phpe 1!img/chart/PieChart.class.php6!img/chart/renderer/GraphRenderer.class.php̦!img/chart/renderer/ImageRenderer.class.phpNY!img/chart/Series.class.phpK!img/Color.class.phpM!img/convert/GrayscaleConverter.class.php^!img/convert/ImageConverter.class.php"img/convert/MatchingPaletteConverter.class.php "img/convert/PaletteConverter.class.php"img/Drawable.class.php"img/filter/ConvolveFilter.class.php 7"img/filter/ImageFilter.class.php "img/filter/Kernel.class.php""img/filter/SharpenFilter.class.php)"img/fonts/BitmapFont.class.php2"img/fonts/TrueTypeFont.class.php5"img/graph/Graph.class.phpP:"img/graph/PieChart.class.php 2="img/graph/PieSlice.class.php@F"img/Image.class.phpr9K"img/ImagingException.class.php E"img/ImgBrush.class.phpJO"img/ImgStyle.class.php"img/io/GD2StreamReader.class.phpP;"img/io/GD2StreamWriter.class.php"img/io/GDStreamReader.class.phpK_"img/io/GDStreamWriter.class.php%"img/io/GifStreamReader.class.phpPϖ"img/io/GifStreamWriter.class.php "img/io/ImageReader.class.php?"img/io/ImageWriter.class.php"img/io/JpegStreamReader.class.phpU֡"img/io/JpegStreamWriter.class.phpZ+"img/io/PngStreamReader.class.phpP"img/io/PngStreamWriter.class.php&թ"img/io/StreamReader.class.php^"img/io/StreamWriter.class.phpY"img/io/WBmpStreamReader.class.phpU"img/io/WBmpStreamWriter.class.phpjK"img/io/XbmStreamReader.class.phpP"img/io/XbmStreamWriter.class.phpe"img/shapes/Arc.class.phpj"img/shapes/Arc3D.class.phphN"img/shapes/Line.class.php"img/shapes/Polygon.class.phpt8"img/shapes/Rectangle.class.php"img/shapes/Text.class.phpb"img/util/ExifData.class.php<w"img/util/ImageInfo.class.phpK&#img/util/IptcData.class.phpd07#security/auth/Authenticator.class.phpQDh#security/auth/AuthenticatorException.class.php9j#security/auth/HtpasswdAuthenticator.class.phpl#security/auth/LdapAuthenticator.class.phplt#security/auth/PropertyAuthenticator.class.phpYz#security/cert/Certificate.class.php }#security/cert/CertificateException.class.phpv#security/cert/CSR.class.php) x#security/cert/X509Certificate.class.php"#security/checksum/Checksum.class.phpç#security/checksum/CRC16.class.phpD#security/checksum/CRC32.class.phpг#security/checksum/HMAC_MD5.class.php]g#security/checksum/MD5.class.php'Ľ#security/checksum/SHA1.class.php2#security/crypto/CryptoException.class.phpr#security/crypto/CryptoKey.class.php#security/crypto/PrivateKey.class.phpM#security/crypto/PublicKey.class.php #security/crypto/UnixCrypt.class.php g#security/KeyPair.class.php J#security/OpenSslUtil.class.php#security/password/Algorithm.class.php#security/password/PasswordStrength.class.php$security/password/StandardAlgorithm.class.phpq $security/Permission.class.phpn$security/Policy.class.phpN!$security/PolicyException.class.php-;$security/Principal.class.php(<$security/sasl/DigestChallenge.class.php""E$security/sasl/DigestResponse.class.php0g$security/SecurityException.class.phpA$gui/GuiException.class.phpB$gui/WidgetNotFoundException.class.php8,$VERSION d$ * $a= new Archive(new File('soap.xar')); * $a->open(ARCHIVE_CREATE); * $a->addFile( * 'webservices/soap/SOAPMessage.class.php' * new File($path, 'xml/soap/SOAPMessage.class.php') * ); * $a->create(); * * * Usage example (Extracting): * * $a= new Archive(new File('soap.xar')); * $bytes= $a->extract('webservices/soap/SOAPMessage.class.php'); * * * @test xp://net.xp_framework.unittest.archive.ArchiveTest * @purpose Provide archiving * @see http://java.sun.com/j2se/1.4/docs/api/java/util/jar/package-summary.html */ class Archive extends Object { public $file = NULL, $version = 2; public $_index = array(); /** * Constructor * * @param io.File file */ public function __construct($file) { $this->file= $file; } /** * Get URI * * @return string uri */ public function getURI() { return $this->file->getURI(); } /** * Add a file * * @param io.File file * @param string id the id under which this entry will be located * @return bool success * @deprecated Use addFile() instead */ public function add($file, $id) { $file->open(FILE_MODE_READ); $data= $file->read($file->size()); $file->close(); $this->_index[$id]= array(strlen($data), -1, $data); return TRUE; } /** * Add a file by its bytes * * @param string id the id under which this entry will be located * @param string path * @param string filename * @param string bytes * @deprecated Use addBytes() instead */ public function addFileBytes($id, $path, $filename, $bytes) { $this->_index[$id]= array(strlen($bytes), -1, $bytes); } /** * Add a file by its bytes * * @param string id the id under which this entry will be located * @param string bytes */ public function addBytes($id, $bytes) { $this->_index[$id]= array(strlen($bytes), -1, $bytes); } /** * Add a file * * @param string id the id under which this entry will be located * @param io.File file */ public function addFile($id, $file) { $file->open(FILE_MODE_READ); $bytes= $file->read($file->size()); $file->close(); $this->_index[$id]= array(strlen($bytes), -1, $bytes); } /** * Create CCA archive * * @return bool success */ public function create() { $this->file->truncate(); $this->file->write(pack( 'a3c1V1a248', 'CCA', $this->version, sizeof(array_keys($this->_index)), "\0" // Reserved for future use )); // Write index $offset= 0; foreach (array_keys($this->_index) as $id) { $this->file->write(pack( 'a240V1V1a8', $id, $this->_index[$id][0], $offset, "\0" // Reserved for future use )); $offset+= $this->_index[$id][0]; } // Write files foreach (array_keys($this->_index) as $id) { $this->file->write($this->_index[$id][2]); } $this->file->close(); return TRUE; } /** * Check whether a given element exists * * @param string id the element's id * @return bool TRUE when the element exists */ public function contains($id) { return isset($this->_index[$id]); } /** * Get entry (iterative use) * * $a= new Archive(new File('port.xar')); * $a->open(ARCHIVE_READ); * while ($id= $a->getEntry()) { * var_dump($id); * } * $a->close(); * * * @return string id or FALSE to indicate the pointer is at the end of the list */ public function getEntry() { $key= key($this->_index); next($this->_index); return $key; } /** * Rewind archive * */ public function rewind() { reset($this->_index); } /** * Extract a file's contents * * @param string id * @return string content * @throws lang.ElementNotFoundException in case the specified id does not exist */ public function extract($id) { if (!$this->contains($id)) { throw new ElementNotFoundException('Element "'.$id.'" not contained in this archive'); } // Calculate starting position $pos= ( ARCHIVE_HEADER_SIZE + sizeof(array_keys($this->_index)) * ARCHIVE_INDEX_ENTRY_SIZE + $this->_index[$id][1] ); try { $this->file->isOpen() || $this->file->open(FILE_MODE_READ); $this->file->seek($pos, SEEK_SET); $data= $this->file->read($this->_index[$id][0]); } catch (XPException $e) { throw new ElementNotFoundException('Element "'.$id.'" cannot be read: '.$e->getMessage()); } return $data; } /** * Fetches a stream to the file in the archive * * @param string id * @return io.Stream * @throws lang.ElementNotFoundException in case the specified id does not exist */ public function getStream($id) { if (!$this->contains($id)) { throw new ElementNotFoundException('Element "'.$id.'" not contained in this archive'); } // Calculate starting position $pos= ( ARCHIVE_HEADER_SIZE + sizeof(array_keys($this->_index)) * ARCHIVE_INDEX_ENTRY_SIZE + $this->_index[$id][1] ); return new EncapsedStream($this->file, $pos, $this->_index[$id][0]); } /** * Open this archive * * @param int mode default ARCHIVE_READ one of ARCHIVE_READ | ARCHIVE_CREATE * @return bool success * @throws lang.IllegalArgumentException in case an illegal mode was specified * @throws lang.FormatException in case the header is malformed */ public function open($mode) { static $unpack= array( 1 => 'a80id/a80*filename/a80*path/V1size/V1offset/a*reserved', 2 => 'a240id/V1size/V1offset/a*reserved' ); switch ($mode) { case ARCHIVE_READ: // Load $this->file->open(FILE_MODE_READ); // Read header $header= $this->file->read(ARCHIVE_HEADER_SIZE); $data= unpack('a3id/c1version/V1indexsize/a*reserved', $header); // Check header integrity if ('CCA' !== $data['id']) throw new FormatException(sprintf( 'Header malformed: "CCA" expected, have "%s"', substr($header, 0, 3) )); // Copy information $this->version= $data['version']; // Read index for ($i= 0; $i < $data['indexsize']; $i++) { $entry= unpack($unpack[$this->version], $this->file->read(ARCHIVE_INDEX_ENTRY_SIZE)); $this->_index[$entry['id']]= array($entry['size'], $entry['offset'], NULL); } return TRUE; case ARCHIVE_CREATE: // Create return $this->file->open(FILE_MODE_WRITE); } throw new IllegalArgumentException('Mode '.$mode.' not recognized'); } /** * Close this archive * * @return bool success */ public function close() { return $this->file->close(); } /** * Checks whether this archive is open * * @return bool TRUE when the archive file is open */ public function isOpen() { return $this->file->isOpen(); } /** * Returns a string representation of this object * * @return string */ public function toString() { return sprintf( '%s(version= %s, index size= %d) { %s }', $this->getClassName(), $this->version, sizeof($this->_index), xp::stringOf($this->file) ); } /** * Destructor * */ public function __destruct() { if ($this->isOpen()) $this->close(); } } ?> * $l= new ArchiveClassLoader(new Archive(new File('soap.xar'))); * try { * $class= $l->loadClass($argv[1]); * } catch (ClassNotFoundException $e) { * $e->printStackTrace(); * exit(-1); * } * * $obj= $class->newInstance(); * * * @test xp://net.xp_framework.unittest.io.ArchiveClassLoaderTest * @purpose Load classes from an archive * @see xp://lang.ClassLoader * @see xp://lang.archive.Archive * @ext tokenize */ class ArchiveClassLoader extends Object implements IClassLoader { protected $archive = NULL; /** * Constructor * * @param mixed archive either a string or a lang.archive.Archive instance */ public function __construct($archive) { $uri= $archive instanceof Archive ? $archive->getURI() : $archive; // Archive within an archive if (0 === strncmp('xar://', $uri, 6)) { $this->archive= 'xar://'.urlencode($uri).'?'; } else { $this->archive= 'xar://'.$uri.'?'; } } /** * Creates a string representation * * @return string */ public function toString() { return $this->getClassName(). '<'.$this->archive.'>'; } /** * Load class bytes * * @param string name fully qualified class name * @return string */ public function loadClassBytes($name) { return file_get_contents($this->archive.strtr($name, '.', '/').xp::CLASS_FILE_EXT); } /** * Load the class by the specified name * * @param string class fully qualified class name io.File * @return lang.XPClass * @throws lang.ClassNotFoundException in case the class can not be found * @throws lang.FormatException in case the class file is malformed */ public function loadClass($class) { return new XPClass($this->loadClass0($class)); } /** * Loads a class * * @param string class fully qualified class name * @return string class name of class loaded * @throws lang.ClassNotFoundException in case the class can not be found */ public function loadClass0($class) { if (isset(xp::$registry['classloader.'.$class])) return xp::reflect($class); xp::$registry['classloader.'.$class]= 'lang.archive.ArchiveClassLoader://'.substr($this->archive, 6, -1); $package= NULL; if (FALSE === include($this->archive.strtr($class, '.', '/').xp::CLASS_FILE_EXT)) { unset(xp::$registry['classloader.'.$class]); throw new FormatException('Cannot define class "'.$class.'"'); } $name= ($package ? strtr($package, '.', '').'' : '').xp::reflect($class); xp::$registry['class.'.$name]= $class; is_callable(array($name, '__static')) && call_user_func(array($name, '__static')); return $name; } /** * Loads a resource. * * @param string string name of resource * @return string * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResource($string) { if (FALSE !== ($r= file_get_contents($this->archive.$string))) { return $r; } return raise('lang.ElementNotFoundException', 'Could not load resource '.$string); } /** * Retrieve a stream to the resource * * @param string string name of resource * @return io.Stream * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResourceAsStream($string) { if (!file_exists($this->archive.$string)) { return raise('lang.ElementNotFoundException', 'Could not load resource '.$string); } return new File($this->archive.$string); } /** * Checks whether this loader can provide the requested class * * @param string class * @return bool */ public function providesClass($class) { return file_exists($this->archive.strtr($class, '.', '/').xp::CLASS_FILE_EXT); } /** * Checks whether this loader can provide the requested resource * * @param string filename * @return bool */ public function providesResource($filename) { return file_exists($this->archive.$filename); } /** * Checks whether this loader can provide the requested package * * @param string package * @return bool */ public function providesPackage($package) { $acquired= xarloader::acquire(urldecode(substr($this->archive, 6, -1))); $cmps= strtr($package, '.', '/'); $cmpl= strlen($cmps); foreach (array_keys($acquired['index']) as $e) { if (strncmp($cmps, $e, $cmpl) === 0) return TRUE; } return FALSE; } /** * Fetch instance of classloader by the path to the archive * * @param string path * @param bool expand default TRUE whether to expand the path using realpath * @return lang.archive.ArchiveClassLoader */ public static function instanceFor($path, $expand= TRUE) { static $pool= array(); $path= $expand && 0 !== strncmp('xar%3A%2F%2F', $path, 12) ? realpath($path) : $path; if (!isset($pool[$path])) { $pool[$path]= new self($path); } return $pool[$path]; } /** * Get package contents * * @param string package * @return string[] filenames */ public function packageContents($package) { $contents= array(); $acquired= xarloader::acquire(urldecode(substr($this->archive, 6, -1))); $cmps= strtr($package, '.', '/'); $cmpl= strlen($cmps); foreach (array_keys($acquired['index']) as $e) { if (strncmp($cmps, $e, $cmpl) != 0) continue; $entry= substr($e, $cmpl+ 1); // Check to see if we're getting something in a subpackage. Imagine the // following structure: // // archive.xar // - tests/ClassOne.class.php // - tests/classes/RecursionTest.class.php // - tests/classes/ng/NextGenerationRecursionTest.class.php // // When this method is invoked with "tests" as name, "ClassOne.class.php" // and "classes/" should be returned (but neither any of the subdirectories // nor their contents) if (FALSE !== ($p= strpos($entry, '/'))) { $entry= substr($entry, 0, $p); if (strstr($entry, '/')) continue; $entry.= '/'; } $contents[$entry]= NULL; } return array_keys($contents); } } ?> file= $filename; } /** * Get URI * * @return string uri */ public function getURI() { return $this->file; } /** * Check whether a given element exists * * @param string id the element's id * @return bool TRUE when the element exists */ public function contains($id) { return isset($this->_index[$id]); } /** * Get entry (iterative use) * * $a= new Archive(new File('port.xar')); * $a->open(ARCHIVE_READ); * while ($id= $a->getEntry()) { * var_dump($id); * } * $a->close(); * * * @return string id or FALSE to indicate the pointer is at the end of the list */ public function getEntry() { $key= key($this->_index); next($this->_index); return $key; } /** * Rewind archive * */ public function rewind() { reset($this->_index); } /** * Extract a file's contents * * @param string id * @return string content */ public function extract($id) { if (!$this->contains($id)) { return FALSE; } // Calculate starting position $pos= ( ARCHIVE_HEADER_SIZE + sizeof(array_keys($this->_index)) * ARCHIVE_INDEX_ENTRY_SIZE + $this->_index[$id][3] ); fseek($this->_hdl, $pos, SEEK_SET); $data= fread($this->_hdl, $this->_index[$id][2]); return $data; } /** * Fetches a stream to the file in the archive * * @param string id * @return io.Stream */ public function getStream($id) { if (!$this->contains($id)) { return FALSE; } // Calculate starting position $pos= ( ARCHIVE_HEADER_SIZE + sizeof(array_keys($this->_index)) * ARCHIVE_INDEX_ENTRY_SIZE + $this->_index[$id][3] ); // Load the class only at runtime to keep hardcoded dependencies to // external (ie. != "lang.") classes at a minimum to not affect // core startup time. $class= XPClass::forName('io.EncapsedStream'); $file= XPClass::forName('io.File')->newInstance($this->file); $file->open(FILE_MODE_READ); $s= $class->newInstance($file, $pos, $this->_index[$id][2]); return $s; } /** * Open this archive. * * Note: this light-weight implementation of an ArchiveReader * only supports opening the archive in ARCHIVE_READ mode. * * @param int mode default ARCHIVE_READ one of ARCHIVE_READ | ARCHIVE_CREATE * @return bool success */ public function open($mode) { switch ($mode) { case ARCHIVE_READ: // Load $this->_hdl= fopen($this->file, 'rb'); $header= fread($this->_hdl, ARCHIVE_HEADER_SIZE); $data= unpack('a3id/c1version/i1indexsize/a*reserved', $header); // Check header integrity if ('CCA' !== $data['id']) throw(new FormatException(sprintf( 'Header malformed: "CCA" expected, have "%s"', substr($header, 0, 3) ))); // Copy information $this->version = $data['version']; // Read index for ($i= 0; $i < $data['indexsize']; $i++) { $entry= unpack( 'a80id/a80filename/a80path/i1size/i1offset/a*reserved', fread($this->_hdl, ARCHIVE_INDEX_ENTRY_SIZE) ); $this->_index[$entry['id']]= array( $entry['filename'], $entry['path'], $entry['size'], $entry['offset'], NULL // Will not be read, use extract() ); } return TRUE; } return FALSE; } /** * Close this archive * * @return bool success */ public function close() { return fclose($this->_hdl); } /** * Checks whether this archive is open * * @return bool TRUE when the archive file is open */ public function isOpen() { return is_resource($this->_hdl); } /** * Returns a string representation of this object * * @return string */ public function toString() { return sprintf( '%s(version= %s, index size= %d) { %s }', $this->getClassName(), $this->version, sizeof($this->_index), xp::stringOf($this->_hdl) ); } } ?> cause= $cause; } /** * Set cause * * @param lang.Throwable cause */ public function setCause($cause) { $this->cause= $cause; } /** * Get cause * * @return lang.Throwable */ public function getCause() { return $this->cause; } /** * Return string representation of this exception * * @return string */ public function toString() { $s= $this->compoundMessage()."\n"; $t= sizeof($this->trace); for ($i= 0; $i < $t; $i++) { $s.= $this->trace[$i]->toString(); } if (!$this->cause) return $s; $loop= $this->cause; while ($loop) { // String of cause $s.= 'Caused by '.$loop->compoundMessage()."\n"; // Find common stack trace elements for ($ct= $cc= sizeof($loop->trace)- 1, $t= sizeof($this->trace)- 1; $ct > 0, $t > 0; $cc--, $t--) { if (!$loop->trace[$cc]->equals($this->trace[$t])) break; } // Output uncommon elements only and one line how many common elements exist! for ($i= 0; $i < $cc; $i++) { $s.= xp::stringOf($loop->trace[$i]); } if ($cc != $ct) $s.= ' ... '.($ct - $cc + 1)." more\n"; $loop= $loop instanceof ChainedException ? $loop->cause : NULL; } return $s; } } ?> loadClass(). * * Given the following code * * $class= ClassLoader::getDefault()->loadClass($name); * * ...and the following include_path setting: *
   *   ".:/usr/local/lib/xp/xp-rt-5.4.0.xar:/home/classes/"
   * 
* ...the classloader will ask the class loader delegates: *
   * - FileSystemClassLoader(.)
   * - ArchiveClassLoader(/usr/local/lib/xp/xp-rt-5.4.0.xar)
   * - FileSystemClassLoader(/home/classes/)
   * 
* ...in the stated order. The first delegate to provide the class * will be asked to load it. In case none of the delegates are able * to provide the class, a ClassNotFoundException will be thrown. * * @test xp://net.xp_framework.unittest.reflection.ClassLoaderTest * @test xp://net.xp_framework.unittest.reflection.ResourcesTest * @test xp://net.xp_framework.unittest.reflection.PackageTest * @test xp://net.xp_framework.unittest.reflection.RuntimeClassDefinitionTest * @test xp://net.xp_framework.unittest.reflection.FullyQualifiedTest * @see xp://lang.XPClass#forName * @see xp://lang.reflect.Package#loadClass * @purpose Class loading */ final class ClassLoader extends Object implements IClassLoader { protected static $delegates = array(); static function __static() { xp::$registry['loader']= new self(); // Scan include-path, setting up classloaders for each element foreach (xp::$registry['classpath'] as $element) { if (is_dir($element)) { self::registerLoader(FileSystemClassLoader::instanceFor($element, FALSE)); } else if (is_file($element)) { self::registerLoader(ArchiveClassLoader::instanceFor($element, FALSE)); } } } /** * Retrieve the default class loader * * @return lang.ClassLoader */ public static function getDefault() { return xp::$registry['loader']; } /** * Register a class loader from a path * * @param string element * @param bool before default FALSE whether to register this as the first loader * @return lang.IClassLoader the registered loader * @throws lang.ElementNotFoundException if the path cannot be found */ public static function registerPath($element, $before= FALSE) { if (is_dir($element)) { return self::registerLoader(FileSystemClassLoader::instanceFor($element, $before)); } else if (is_file($element)) { return self::registerLoader(ArchiveClassLoader::instanceFor($element, $before)); } raise('lang.ElementNotFoundException', 'Element "'.$element.'" not found'); } /** * Register a class loader as a delegate * * @param lang.IClassLoader l * @param bool before default FALSE whether to register this as the first loader * @return lang.IClassLoader the registered loader */ public static function registerLoader(IClassLoader $l, $before= FALSE) { if ($before) { self::$delegates= array_merge(array($l->hashCode() => $l), self::$delegates); } else { self::$delegates[$l->hashCode()]= $l; } return $l; } /** * Unregister a class loader as a delegate * * @param lang.IClassLoader l * @return bool TRUE if the delegate was unregistered */ public static function removeLoader(IClassLoader $l) { if (!isset(self::$delegates[$l->hashCode()])) return FALSE; unset(self::$delegates[$l->hashCode()]); return TRUE; } /** * Get class loader delegates * * @return lang.IClassLoader[] */ public static function getLoaders() { return array_values(self::$delegates); } /** * Define a class with a given name * * @param string class fully qualified class name * @param string parent either sourcecode of the class or FQCN of parent * @param string[] interfaces FQCNs of implemented interfaces * @param string bytes default "{}" inner sourcecode of class (containing {}) * @return lang.XPClass * @throws lang.FormatException in case the class cannot be defined * @throws lang.ClassNotFoundException if given parent class does not exist */ public static function defineClass($class, $parent, $interfaces, $bytes= '{}') { $name= xp::reflect($class); if (!isset(xp::$registry['classloader.'.$class])) { $super= xp::reflect($parent); // Test for existance if (!class_exists($super)) { throw new ClassNotFoundException('Parent class "'.$parent.'" does not exist.'); } if (!empty($interfaces)) { $if= array_map(array('xp', 'reflect'), $interfaces); foreach ($if as $implemented) { if (interface_exists($implemented)) continue; throw new ClassNotFoundException('Implemented interface "'.$implemented.'" does not exist.'); } } with ($dyn= DynamicClassLoader::instanceFor(__METHOD__)); { $dyn->setClassBytes($class, sprintf( 'class %s extends %s%s %s', $name, $super, $interfaces ? ' implements '.implode(', ', $if) : '', $bytes )); return $dyn->loadClass($class); } } return new XPClass($name); } /** * Define an interface with a given name * * @param string class fully qualified class name * @param string[] parents FQCNs of parent interfaces * @param string bytes default "{}" inner sourcecode of class (containing {}) * @return lang.XPClass * @throws lang.FormatException in case the class cannot be defined * @throws lang.ClassNotFoundException if given parent class does not exist */ public static function defineInterface($class, $parents, $bytes= '{}') { $name= xp::reflect($class); $if= array(); if (!isset(xp::$registry['classloader.'.$class])) { if (!empty($parents)) { $if= array_map(array('xp', 'reflect'), (array)$parents); foreach ($if as $super) { if (interface_exists($super)) continue; throw new ClassNotFoundException('Superinterface "'.$super.'" does not exist.'); } } with ($dyn= DynamicClassLoader::instanceFor(__METHOD__)); { $dyn->setClassBytes($class, sprintf( 'interface %s%s %s', $name, sizeof($if) ? ' extends '.implode(', ', $if) : '', $bytes )); return $dyn->loadClass($class); } } return new XPClass($name); } /** * Loads a class * * @param string class fully qualified class name * @return string class name of class loaded * @throws lang.ClassNotFoundException in case the class can not be found */ public function loadClass0($class) { if (isset(xp::$registry['classloader.'.$class])) return xp::reflect($class); // Ask delegates foreach (self::$delegates as $delegate) { if ($delegate->providesClass($class)) return $delegate->loadClass0($class); } throw new ClassNotFoundException(sprintf( 'No classloader provides class "%s" {%s}', $class, xp::stringOf(self::getLoaders()) )); } /** * Checks whether this loader can provide the requested class * * @param string class * @return bool */ public function providesClass($class) { foreach (self::$delegates as $delegate) { if ($delegate->providesClass($class)) return TRUE; } return FALSE; } /** * Checks whether this loader can provide the requested resource * * @param string filename * @return bool */ public function providesResource($filename) { foreach (self::$delegates as $delegate) { if ($delegate->providesResource($filename)) return TRUE; } return FALSE; } /** * Checks whether this loader can provide the requested package * * @param string package * @return bool */ public function providesPackage($package) { foreach (self::$delegates as $delegate) { if ($delegate->providesPackage($package)) return TRUE; } return FALSE; } /** * Find the class by the specified name * * @param string class fully qualified class name * @return lang.IClassLoader the classloader that provides this class */ public function findClass($class) { foreach (self::$delegates as $delegate) { if ($delegate->providesClass($class)) return $delegate; } return xp::null(); } /** * Find the package by the specified name * * @param string package fully qualified package name * @return lang.IClassLoader the classloader that provides this class */ public function findPackage($package) { foreach (self::$delegates as $delegate) { if ($delegate->providesPackage($package)) return $delegate; } return xp::null(); } /** * Load the class by the specified name * * @param string class fully qualified class name * @return lang.XPClass * @throws lang.ClassNotFoundException in case the class can not be found */ public function loadClass($class) { return new XPClass($this->loadClass0($class)); } /** * Find the resource by the specified name * * @param string name resource name * @return lang.IClassLoader the classloader that provides this resource */ public function findResource($name) { foreach (self::$delegates as $delegate) { if ($delegate->providesResource($name)) return $delegate; } return xp::null(); } /** * Loads a resource. * * @param string string name of resource * @return string * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResource($string) { foreach (self::$delegates as $delegate) { if ($delegate->providesResource($string)) return $delegate->getResource($string); } raise('lang.ElementNotFoundException', sprintf( 'No classloader provides resource "%s" {%s}', $string, xp::stringOf(self::getLoaders()) )); } /** * Retrieve a stream to the resource * * @param string string name of resource * @return io.Stream * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResourceAsStream($string) { foreach (self::$delegates as $delegate) { if ($delegate->providesResource($string)) return $delegate->getResourceAsStream($string); } raise('lang.ElementNotFoundException', sprintf( 'No classloader provides resource "%s" {%s}', $string, xp::stringOf(self::getLoaders()) )); } /** * Get package contents * * @param string package * @return string[] filenames */ public function packageContents($package) { $contents= array(); foreach (self::$delegates as $delegate) { $contents= array_merge($contents, $delegate->packageContents($package)); } return array_unique($contents); } } ?> * $coll= Collection::forClass('rdbms.DBConnection'); * * $coll->add(new SybaseConnection(...)); // Works * $coll->add(new MySQLConnection(...)); // Works, too * $coll->add(Date::now()); // Fails * $coll->add(1); // Fails * * * @test xp://net.xp_framework.unittest.core.CollectionTest * @purpose "Type-safe" array */ class Collection extends Object { public $class = '', $list = array(); public $_name = ''; /** * Constructor * * @param string class */ protected function __construct($class) { $this->class= $class; $this->_name= xp::reflect($class); } /** * Returns a new Collection object for a specified class * * @param string class the fully qualified class name * @return lang.Collection * @throws lang.ClassNotFoundException */ public static function forClass($class) { if (!class_exists(xp::reflect($class))) { throw(new ClassNotFoundException('Class "'.$class.'" does not exist')); } return new self($class); } /** * Returns the number of elements in this list. * * @return int */ public function size() { return sizeof($this->list); } /** * Returns the element's class name * * @return string */ public function getElementClassName() { return $this->class; } /** * Returns the element's class name * * @return lang.XPClass */ public function getElementClass() { return XPClass::forName($this->class); } /** * Tests if this list has no elements. * * @return bool */ public function isEmpty() { return empty($this->list); } /** * Adds an element to this list * * @param lang.Object element * @return lang.Object the added element * @throws lang.IllegalArgumentException */ public function add($element) { if (!is($this->_name, $element)) { throw(new IllegalArgumentException(sprintf( 'Element is not a %s (but %s)', $this->class, xp::typeOf($element) ))); } $this->list[]= $element; return $element; } /** * Adds an element to the beginning of this list * * @param lang.Object element * @return lang.Object the prepended element * @throws lang.IllegalArgumentException */ public function prepend($element) { if (!is($this->_name, $element)) { throw(new IllegalArgumentException(sprintf( 'Element is not a %s (but %s)', $this->class, xp::typeOf($element) ))); } array_unshift($this->list, $element); return $element; } /** * Adds an array of elements to this list * * @param lang.Object[] array * @throws lang.IllegalArgumentException */ public function addAll($array) { $original= $this->list; for ($i= 0, $s= sizeof($array); $i < $s; $i++) { if (!is($this->_name, $array[$i])) { $this->list= $original; // Rollback throw(new IllegalArgumentException(sprintf( 'Element %d is not a %s (but %s)', $i, $this->class, xp::typeOf($array[$i]) ))); } $this->list[]= $array[$i]; } } /** * Prepend an array of elements to this list * * @param lang.Object[] array * @throws lang.IllegalArgumentException */ public function prependAll($array) { $original= $this->list; for ($i= 0, $s= sizeof($array); $i < $s; $i++) { if (!is($this->_name, $array[$i])) { $this->list= $original; // Rollback throw(new IllegalArgumentException(sprintf( 'Element %d is not a %s (but %s)', $i, $this->class, xp::typeOf($array[$i]) ))); } array_unshift($this->list, $array[$i]); } } /** * Replaces the element at the specified position in this list with * the specified element. * * @param int index * @param lang.Object element * @return lang.Object the element previously at the specified position. */ public function set($index, $element) { $orig= $this->list[$index]; $this->list[$index]= $element; return $orig; } /** * Returns the element at the specified position in this list. * * @param int index * @return lang.Object */ public function get($index) { return @$this->list[$index]; } /** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one * from their indices). * * @param int index * @return lang.Object the element that was removed from the list */ public function remove($index) { $element= $this->list[$index]; unset($this->list[$index]); $this->list= array_values($this->list); return $element; } /** * Removes all of the elements from this list. The list will be empty * after this call returns. * */ public function clear() { $this->list= array(); } /** * Returns an array of this list's elements * * @return lang.Object[] */ public function values() { return array_values($this->list); } /** * Checks if a value exists in this array * * @param lang.Object element * @return bool */ public function contains($element) { for ($i= 0, $s= sizeof($this->list); $i < $s; $i++) { if ($this->list[$i]->equals($element)) return TRUE; } return FALSE; } /** * Searches for the first occurence of the given argument * * @param lang.Object element * @return int offset where the element was found or FALSE */ public function indexOf($element) { // Note: array_search() does NOT work for objects: // // // if (Z_TYPE_PP(value) == IS_OBJECT) { // php_error_docref(NULL TSRMLS_CC, E_WARNING, "Wrong datatype for first argument"); // RETURN_FALSE; // } // for ($i= 0, $s= sizeof($this->list); $i < $s; $i++) { if ($this->list[$i]->equals($element)) return $i; } return FALSE; } /** * Searches for the last occurence of the given argument * * @param lang.Object element * @return int offset where the element was found or FALSE */ public function lastIndexOf($element) { for ($i= sizeof($this->list)- 1; $i > -1; $i--) { if ($this->list[$i]->equals($element)) return $i; } return FALSE; } /** * Creates a string representation of this object * * @return string */ public function toString() { $r= $this->getClassName().'<'.$this->class.">@{\n"; for ($i= 0, $s= sizeof($this->list); $i < $s; $i++) { $r.= ' '.$i.': '.str_replace("\n", "\n ", xp::stringOf($this->list[$i]))."\n"; } return $r.'}'; } /** * Checks if a specified object is equal to this object. * * @param lang.Object collection * @return bool */ public function equals($collection) { if ( !$collection instanceof self || $this->size() != $collection->size() ) return FALSE; // Compare element by element for ($i= 0, $s= sizeof($this->list); $i < $s; $i++) { if ($this->list[$i]->equals($collection->list[$i])) continue; return FALSE; } return TRUE; } } ?> context= $context; } /** * Register new class' bytes * * @param string fqcn * @param string bytes */ public function setClassBytes($fqcn, $bytes) { self::$bytes[$fqcn]= ''; } /** * Checks whether this loader can provide the requested class * * @param string class * @return bool */ public function providesClass($class) { return isset(self::$bytes[$class]); } /** * Checks whether this loader can provide the requested resource * * @param string filename * @return bool */ public function providesResource($filename) { return FALSE; } /** * Checks whether this loader can provide the requested package * * @param string package * @return bool */ public function providesPackage($package) { return FALSE; } /** * Load class bytes * * @param string name fully qualified class name * @return string */ public function loadClassBytes($name) { return self::$bytes[$name]; } /** * Loads a class * * @param string class fully qualified class name * @return string class name of class loaded * @throws lang.ClassNotFoundException in case the class can not be found */ public function loadClass0($class) { if (isset(xp::$registry['classloader.'.$class])) return xp::reflect($class); xp::$registry['classloader.'.$class]= 'lang.DynamicClassLoader://'.$this->context; if (!isset(self::$bytes[$class])) { unset(xp::$registry['classloader.'.$class]); throw new ClassNotFoundException('Unknown class "'.$class.'"'); } $package= NULL; if (FALSE === include('dyn://'.$class)) { throw new FormatException('Cannot define class "'.$class.'"'); } $name= ($package ? strtr($package, '.', '').'' : '').xp::reflect($class); xp::$registry['class.'.$name]= $class; is_callable(array($name, '__static')) && call_user_func(array($name, '__static')); return $name; } /** * Load the class by the specified name * * @param string class fully qualified class name io.File * @return lang.XPClass * @throws lang.ClassNotFoundException in case the class can not be found */ public function loadClass($class) { return new XPClass($this->loadClass0($class)); } /** * Fetch instance of classloader by path * * @param string path the identifier * @return lang.IClassLoader */ public static function instanceFor($path) { static $pool= array(); if (!isset($pool[$path])) { $pool[$path]= new self($path); } return $pool[$path]; } /** * Get package contents * * @param string package * @return string[] filenames */ public function packageContents($package) { return array(); } /** * Loads a resource. * * @param string filename name of resource * @return string * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResource($filename) { raise('lang.ElementNotFoundException', 'Could not load resource '.$filename); } /** * Retrieve a stream to the resource * * @param string filename name of resource * @return io.File * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResourceAsStream($filename) { raise('lang.ElementNotFoundException', 'Could not load resource '.$filename); } /** * Stream wrapper method stream_open * * @param string path * @param int mode * @param int options * @param string opened_path * @return bool */ public function stream_open($path, $mode, $options, $opened_path) { sscanf($path, 'dyn://%[^$]', $this->current); if (!isset(self::$bytes[$this->current])) { raise('lang.ElementNotFoundException', 'Could not load '.$this->current); } return TRUE; } /** * Stream wrapper method stream_read * * @param int count * @return string */ public function stream_read($count) { $bytes= substr(self::$bytes[$this->current], $this->position, $count); $this->position+= strlen($bytes); return $bytes; } /** * Stream wrapper method stream_eof * * @return bool */ public function stream_eof() { // Leave function body empty to optimize speed // See http://bugs.php.net/40047 // // return $this->position >= strlen(self::$bytes[$this->current]); } /** * Stream wrapper method stream_stat * * @return array */ public function stream_stat() { return array( 'size' => strlen(self::$bytes[$this->current]) ); } /** * Stream wrapper method stream_seek * * @param int offset * @param int whence * @return bool */ public function stream_seek($offset, $whence) { switch ($whence) { case SEEK_SET: $this->position= $offset; break; case SEEK_CUR: $this->position+= $offset; break; case SEEK_END: $this->position= strlen(self::$bytes[$this->current]); break; } return TRUE; } /** * Stream wrapper method stream_tell * * @return int offset */ public function stream_tell() { return $this->position; } /** * Stream wrapper method url_stat * * @param string path * @return array */ public function url_stat($path) { list($name)= sscanf($path, 'dyn://%s'); return array( 'size' => strlen(self::$bytes[$name]) ); } } ?> ordinal= $ordinal; $this->name= $name; } /** * Returns the enumeration member uniquely identified by * * @param lang.XPClass class class object * @param string name enumeration member * @return lang.Enum * @throws lang.IllegalArgumentException in case the enum member does not exist or when the given class is not an enum */ public static function valueOf(XPClass $class, $name) { if (!$class->isEnum()) { throw new IllegalArgumentException('Argument class must be lang.XPClass'); } try { return $class->_reflect->getStaticPropertyValue($name); } catch (ReflectionException $e) { throw new IllegalArgumentException($e->getMessage()); } } /** * Returns the enumeration member uniquely identified by * * @param lang.XPClass class class object * @return lang.Enum[] * @throws lang.IllegalArgumentException in case the given class is not an enum */ public static function valuesOf(XPClass $class) { if (!$class->isEnum()) { throw new IllegalArgumentException('Argument class must be lang.XPClass'); } try { return array_values($class->_reflect->getStaticProperties()); } catch (ReflectionException $e) { throw new IllegalArgumentException($e->getMessage()); } } /** * Clone interceptor - ensures enums cannot be cloned * * @throws lang.CloneNotSupportedException */ public final function __clone() { raise('lang.CloneNotSupportedException', 'Enums cannot be cloned'); } /** * Returns the name of this enum constant, exactly as declared in its * enum declaration. * * @return string */ public function name() { return $this->name; } /** * Returns the ordinal of this enumeration constant (its position in * its enum declaration, where the initial constant is assigned an * ordinal of zero). * * @return int */ public function ordinal() { return $this->ordinal; } /** * Create a string representation of this enum * * @return string */ public function toString() { return $this->name; } /** * Returns all members for a given enum. * * @param string class * @return lang.Enum[] */ protected static function membersOf($class) { $c= new ReflectionClass($class); return array_values($c->getStaticProperties()); } } ?> path= rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; } /** * Checks whether two class loaders are equal * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $cmp->path === $this->path; } /** * Creates a string representation * * @return string */ public function toString() { return $this->getClassName(). '<'.$this->path.'>'; } /** * Load class bytes * * @param string name fully qualified class name * @return string */ public function loadClassBytes($name) { return file_get_contents($this->path.strtr($name, '.', DIRECTORY_SEPARATOR).xp::CLASS_FILE_EXT); } /** * Checks whether this loader can provide the requested class * * @param string class * @return bool */ public function providesClass($class) { return is_file($this->path.strtr($class, '.', DIRECTORY_SEPARATOR).xp::CLASS_FILE_EXT); } /** * Checks whether this loader can provide the requested resource * * @param string filename * @return bool */ public function providesResource($filename) { return is_file($this->path.$filename); } /** * Checks whether this loader can provide the requested package * * @param string package * @return bool */ public function providesPackage($package) { return is_dir($this->path.strtr($package, '.', DIRECTORY_SEPARATOR)); } /** * Load the class by the specified name * * @param string class fully qualified class name io.File * @return lang.XPClass * @throws lang.ClassNotFoundException in case the class can not be found */ public function loadClass($class) { return new XPClass($this->loadClass0($class)); } /** * Load the class by the specified name * * @param string class fully qualified class name io.File * @return string class name * @throws lang.ClassNotFoundException in case the class can not be found */ public function loadClass0($class) { if (isset(xp::$registry['classloader.'.$class])) return xp::reflect($class); xp::$registry['classloader.'.$class]= 'lang.FileSystemClassLoader://'.$this->path; $package= NULL; if (FALSE === include($this->path.strtr($class, '.', DIRECTORY_SEPARATOR).xp::CLASS_FILE_EXT)) { unset(xp::$registry['classloader.'.$class]); throw new ClassNotFoundException('Class "'.$class.'" not found'); } $name= ($package ? strtr($package, '.', '').'' : '').xp::reflect($class); xp::$registry['class.'.$name]= $class; is_callable(array($name, '__static')) && call_user_func(array($name, '__static')); return $name; } /** * Loads a resource. * * @param string filename name of resource * @return string * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResource($filename) { if (!is_file($this->path.strtr($filename, '/', DIRECTORY_SEPARATOR))) { return raise('lang.ElementNotFoundException', 'Could not load resource '.$filename); } return file_get_contents($this->path.$filename); } /** * Retrieve a stream to the resource * * @param string filename name of resource * @return io.File * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResourceAsStream($filename) { if (!is_file($this->path.strtr($filename, '/', DIRECTORY_SEPARATOR))) { return raise('lang.ElementNotFoundException', 'Could not load resource '.$filename); } return new File($this->path.$filename); } /** * Fetch instance of classloader by the path to the archive * * @param string path * @param bool expand default TRUE whether to expand the path using realpath * @return lang.FileSystemClassLoader */ public static function instanceFor($path, $expand= TRUE) { static $pool= array(); $path= $expand ? realpath($path) : $path; if (!isset($pool[$path])) { $pool[$path]= new self($path); } return $pool[$path]; } /** * Get package contents * * @param string package * @return string[] filenames */ public function packageContents($package) { $contents= array(); if ($d= @dir($this->path.strtr($package, '.', DIRECTORY_SEPARATOR))) { while ($e= $d->read()) { if ('.' != $e{0}) $contents[]= $e.(is_dir($d->path.DIRECTORY_SEPARATOR.$e) ? '/' : ''); } $d->close(); } return $contents; } } ?> * [fully-qualified-class-name]@[serialized-object] * * * Example: * * lang.Object@class object { * var $__id = '0.06823200 1062749651'; * } * * * @return string */ public function toString(); } ?> method= $method; } /** * Return compound message of this exception. * * @return string */ public function compoundMessage() { return sprintf( 'Exception %s (method %s(): %s)', $this->getClassName(), $this->method, $this->message ); } } ?> __id) $this->__id= microtime(); $this->__id= microtime(); } /** * Returns a hashcode for this object * * @return string */ public function hashCode() { if (!$this->__id) $this->__id= microtime(); return $this->__id; } /** * Indicates whether some other object is "equal to" this one. * * @param lang.Object cmp * @return bool TRUE if the compared object is equal to this object */ public function equals($cmp) { if (!$this->__id) $this->__id= microtime(); if (!$cmp->__id) $cmp->__id= microtime(); return $this === $cmp; } /** * Returns the fully qualified class name for this class * (e.g. "io.File") * * @return string fully qualified class name */ public function getClassName() { return xp::nameOf(get_class($this)); } /** * Returns the runtime class of an object. * * @return lang.XPClass runtime class * @see xp://lang.XPClass */ public function getClass() { return new XPClass($this); } /** * Creates a string representation of this object. In general, the toString * method returns a string that "textually represents" this object. The result * should be a concise but informative representation that is easy for a * person to read. It is recommended that all subclasses override this method. * * Per default, this method returns: * * [fully-qualified-class-name] '{' [members-and-value-list] '}' * * * Example: * * lang.Object { * __id => "0.43080500 1158148350" * } * * * @return string */ public function toString() { if (!$this->__id) $this->__id= microtime(); return xp::stringOf($this); } } ?> *
  • public string hashCode()
  • *
  • public bool equals(lang.Object cmp)
  • *
  • public string getClassName()
  • *
  • public lang.XPClass getClass()
  • *
  • public string toString()
  • * * * Note: getClassName() is not only a shorthand for getClass()->getName() * but also significantly faster than the latter. * * Classes and reflection * ====================== * Every object has an XPClass instance associated with it, providing * reflective information about the runtime class. This includes a fully * qualified class name (e.g. "util.Date"), annotations support, class * fields, methods, constructors and their modifiers as well as methods * to dynamically access these. * * Class loading * ============= * The lang package contains the default classloader class which takes * care of loading classes from the file system. Loading classes from * archive files (xars) is supported by the lang.archive package. * * Process control * =============== * The Process and Thread classes implement process execution and * control. They are complemented with the System class which provides * information about the underlying operating system. * * Chained exceptions * ================== * Not all exceptions in the framework have a cause. Exceptions that * choose to have a cause may extend the lang.ChainedException class. * * Example (abbreviated): * * class FinderException extends ChainedException { } * * class NewsFinder extends Finder { * public function findByPK($pk) { * try { * $r= $this->conn->query('select * from news where news_id= %d', $pk)->next(); * } catch (SQLException $e) { * throw new FinderException('Error while finding news id #'.$pk, $e); * } * if (FALSE === $r) { * throw new FinderException( * 'News id #'.$pk.' does not exist', * new ElementNotFoundException($pk) * ); * } * return $r; * } * } * * $news= new NewsFinder(); * try { * $news->findByPK(10250); * } catch (FinderException $e) { * Console::writeLine('*** ', $e->getMessage(), ' caused by: ', $e->getCause()); * } * * * Classes loaded per default * ========================== * These classes are guaranteed to be loaded at any given time: * * lang.Object, lang.StackTraceElement, lang.Throwable, lang.Error, * lang.XPException, lang.Type, lang.reflect.Routine, lang.reflect.Parameter, * lang.reflect.TargetInvocationException, lang.reflect.Method, * lang.reflect.Field, lang.reflect.Constructor, lang.reflect.Modifiers, * lang.reflect.Package, lang.XPClass, lang.FileSystemClassLoader, * lang.DynamicClassLoader, lang.archive.ArchiveClassLoader, lang.ClassLoader * * These exceptions are guaranteed to be loaded at any given time: * * lang.ChainedException, lang.NullPointerException, lang.IllegalAccessException, * lang.IllegalArgumentException, lang.IllegalStateException, * lang.FormatException, lang.ClassNotFoundException, * * @see http://news.xp-framework.net/article/11/2004/11/23/ * @see xp://lang.Throwable * @see xp://lang.Object * @see xp://lang.Generic * @see xp://lang.XPClass * @see xp://lang.Process * @purpose Core */ package lang { } *
  • string
  • *
  • integer
  • *
  • double
  • *
  • boolean
  • *
  • array
  • * * * @test xp://net.xp_framework.unittest.reflection.PrimitiveTest * @see xp://lang.Type * @purpose Type implementation */ class Primitive extends Type { public static $STRING = NULL, $INTEGER = NULL, $DOUBLE = NULL, $BOOLEAN = NULL, $ARRAY = NULL; static function __static() { self::$STRING= new self('string'); self::$INTEGER= new self('integer'); self::$DOUBLE= new self('double'); self::$BOOLEAN= new self('boolean'); self::$ARRAY= new self('array'); } /** * Boxes a type - that is, turns Generics into primitives * * @param mixed in * @return mixed the primitive if not already primitive * @throws lang.IllegalArgumentException in case in cannot be unboxed. */ public static function unboxed($in) { if ($in instanceof String) return $in->toString(); if ($in instanceof Double) return $in->floatValue(); if ($in instanceof Integer) return $in->intValue(); if ($in instanceof Boolean) return $in->value; if ($in instanceof ArrayList) return $in->values; if ($in instanceof Generic) { throw new IllegalArgumentException('Cannot unbox '.xp::typeOf($in)); } return $in; // Already primitive } /** * Boxes a type - that is, turns primitives into Generics * * @param mixed in * @return lang.Generic the Generic if not already generic * @throws lang.IllegalArgumentException in case in cannot be boxed. */ public static function boxed($in) { if (NULL === $in || $in instanceof Generic) return $in; $t= gettype($in); if ('string' === $t) return new String($in); if ('integer' === $t) return new Integer($in); if ('double' === $t) return new Double($in); if ('boolean' === $t) return new Boolean($in); if ('array' === $t) return ArrayList::newInstance($in); throw new IllegalArgumentException('Cannot box '.xp::typeOf($in)); } /** * Get a type instance for a given name * * @param string name * @return lang.Type * @throws lang.IllegalArgumentException if the given name does not correspond to a primitive */ public static function forName($name) { switch ($name) { case 'string': return self::$STRING; case 'integer': return self::$INTEGER; case 'double': return self::$DOUBLE; case 'boolean': return self::$BOOLEAN; case 'array': return self::$ARRAY; default: throw new IllegalArgumentException('Not a primitive: '.$name); } } } ?> * $p= new Process('uptime'); * $uptime= $p->out->readLine(); * $p->close(); * * var_dump($uptime); * * * @test xp://net.xp_framework.unittest.core.ProcessTest * @see xp://lang.Runtime#getExecutable * @see php://proc_open * @purpose Execute external programs */ class Process extends Object { public $in = NULL, $out = NULL, $err = NULL, $exitv = -1; protected $_proc = NULL, $status = array(); /** * Constructor * * @param string command default NULL * @param string[] arguments default [] * @param string cwd default NULL the working directory * @param array default NULL the environment * @throws io.IOException in case the command could not be executed */ public function __construct($command= NULL, $arguments= array(), $cwd= NULL, $env= NULL) { static $spec= array( 0 => array('pipe', 'r'), // stdin 1 => array('pipe', 'w'), // stdout 2 => array('pipe', 'w') // stderr ); // For `new self()` used in getProcessById() if (NULL === $command) return; // Check whether the given command is executable. $binary= $this->resolve($command); if (!is_executable($binary)) { throw new IOException('Command "'.$binary.'" is not executable'); } // Build command line $cmd= $command.' '.implode(' ', $arguments); // Open process if (!is_resource($this->_proc= proc_open($cmd, $spec, $pipes, $cwd, $env))) { throw new IOException('Could not execute "'.$cmd.'"'); } $this->status= proc_get_status($this->_proc); $this->status['exe']= $binary; // Assign in, out and err members $this->in= new File($pipes[0]); $this->out= new File($pipes[1]); $this->err= new File($pipes[2]); } /** * Create a new instance of this process. * * @param string[] arguments default [] * @param string cwd default NULL the working directory * @param array default NULL the environment * @throws io.IOException in case the command could not be executed */ public function newInstance($arguments= array(), $cwd= NULL, $env= NULL) { return new self($this->status['exe'], $arguments, $cwd, $env); } /** * Resolve path for a command * * @param string command * @return string executable * @throws io.IOException in case the command could not be found */ public function resolve($command) { clearstatcache(); // If the command is in fully qualified form and refers to a file // that does not exist (e.g. "C:\DoesNotExist.exe", "\DoesNotExist.com" // or /usr/bin/doesnotexist), do not attempt to search for it. if ( (strncasecmp(PHP_OS, 'Win', 3) === 0 && ':' === $command{1}) || (DIRECTORY_SEPARATOR === $command{0}) ) { if (file_exists($command)) return realpath($command); throw new IOException('"'.$command.'" does not exist'); } // Check the PATH environment setting for possible locations of the // executable if its name is not a fully qualified path name. $paths= explode(PATH_SEPARATOR, getenv('PATH')); $extensions= array('') + explode(PATH_SEPARATOR, getenv('PATHEXT')); foreach ($paths as $path) { foreach ($extensions as $ext) { if (!file_exists($q= $path.DIRECTORY_SEPARATOR.$command.$ext)) continue; return realpath($q); } } throw new IOException('Could not find "'.$command.'" in path'); } /** * Get a process by process ID * * @param int pid process id * @return lang.Process * @throws lang.IllegalStateException */ public static function getProcessById($pid) { $self= new self(); $self->status= array( 'pid' => $pid, 'running' => TRUE ); // Determine executable and command line: // * On Windows, use Windows Management Instrumentation API - see // http://en.wikipedia.org/wiki/Windows_Management_Instrumentation // // * On systems with a /proc filesystem, use information from /proc/self // See http://en.wikipedia.org/wiki/Procfs // // * Fall back to use the "_" environment variable and /bin/ps to retrieve // the command line (please note unfortunately any quote signs have been // lost and it can thus be only used for display purposes) // // Note: It would be really nice to have a getmyexe() function in PHP // complementing getmypid(). if (strncasecmp(PHP_OS, 'Win', 3) === 0) { try { $c= new Com('winmgmts:'); $p= $c->get('//./root/cimv2:Win32_Process.Handle="'.$pid.'"'); $self->status['exe']= $p->executablePath; $self->status['command']= $p->commandLine; } catch (Exception $e) { throw new IllegalStateException('Cannot find executable: '.$e->getMessage()); } } else if (file_exists($proc= '/proc/'.$pid)) { $self->status['exe']= readlink($proc.'/exe'); $self->status['command']= strtr($proc.'/cmdline', "\0", ' '); } else if ($_= getenv('_')) { $self->status['exe']= realpath($_); $self->status['command']= exec('ps -p '.$pid.' -ocommand'); } else { throw new IllegalStateException('Cannot find executable'); } $self->in= xp::null(); $self->out= xp::null(); $self->err= xp::null(); return $self; } /** * Get process ID * * @return int */ public function getProcessId() { return $this->status['pid']; } /** * Get filename of executable * * @return string */ public function getFilename() { return $this->status['exe']; } /** * Get command line * * @return string */ public function getCommandLine() { return $this->status['command']; } /** * Get error stream * * @return io.File STDERR */ public function getErrorStream() { return $this->err; } /** * Get input stream * * @return io.File STDIN */ public function getInputStream() { return $this->in; } /** * Get output stream * * @return io.File STDOUT */ public function getOutputStream() { return $this->out; } /** * Returns the exit value for the process * * @return int */ public function exitValue() { return $this->exitv; } /** * Close this process * * @return int exit value of process */ public function close() { $this->in->isOpen() && $this->in->close(); $this->out->isOpen() && $this->out->close(); $this->err->isOpen() && $this->err->close(); $this->exitv= proc_close($this->_proc); return $this->exitv; } } ?> * $constructor= XPClass::forName('utl.Binford')->getConstructor(); * * var_dump($constructor->newInstance()); * * * @param mixed[] args * @return lang.Generic * @throws lang.IllegalAccessException in case the constructor is not public or if it is abstract * @throws lang.reflect.TargetInvocationException in case the constructor throws an exception */ public function newInstance(array $args= array()) { // Check whether class is abstract if ($this->_reflect->getDeclaringClass()->isAbstract()) { throw new IllegalAccessException('Cannot instantiate abstract class '.$this->_class); } // Check modifers $m= $this->_reflect->getModifiers(); if (!($m & MODIFIER_PUBLIC) || $m & MODIFIER_ABSTRACT) { throw new IllegalAccessException(sprintf( 'Cannot invoke %s constructor of class %s', Modifiers::stringOf($this->getModifiers()), $this->_class )); } $paramstr= ''; for ($i= 0, $m= sizeof($args); $i < $m; $i++) { $paramstr.= ', $args['.$i.']'; } try { return eval('return new '.$this->_class.'('.substr($paramstr, 2).');'); } catch (Throwable $e) { throw new TargetInvocationException($this->_class.'::', $e); } } /** * Retrieve return type * * @return lang.Type */ public function getReturnType() { return XPClass::forName(xp::nameOf($this->_class)); } /** * Retrieve return type * * @return string */ public function getReturnTypeName() { return xp::nameOf($this->_class); } } ?> _class= $class; $this->_reflect= $reflect; } /** * Get field's name. * * @return string */ public function getName() { return $this->_reflect->getName(); } /** * Gets field type * * @return string */ public function getType() { if ($details= XPClass::detailsForField($this->_class, $this->_reflect->getName())) { if (isset($details[DETAIL_ANNOTATIONS]['type'])) return $details[DETAIL_ANNOTATIONS]['type']; } return NULL; } /** * Returns the XPClass object representing the class or interface * that declares the field represented by this Field object. * * @return lang.XPClass */ public function getDeclaringClass() { return new XPClass($this->_reflect->getDeclaringClass()->getName()); } /** * Returns the value of the field represented by this Field, on the * specified object. * * @param lang.Object instance * @return mixed * @throws lang.IllegalArgumentException in case the passed object is not an instance of the declaring class * @throws lang.IllegalAccessException in case this field is not public */ public function get($instance) { // Verify the field is public if (!($this->_reflect->getModifiers() & MODIFIER_PUBLIC)) { throw new IllegalAccessException('Cannot read '.$this->toString()); } // Short-circuit further checks for static members if ($this->_reflect->isStatic()) { return $this->_reflect->getValue(NULL); } // Verify given instance is instance of the class declaring this // property if (!($instance instanceof $this->_class)) { throw new IllegalArgumentException(sprintf( 'Passed argument is not a %s class (%s)', xp::nameOf($this->_class), xp::typeOf($instance) )); } return $this->_reflect->getValue($instance); } /** * Retrieve this field's modifiers * * @see xp://lang.reflect.Modifiers * @return int */ public function getModifiers() { return $this->_reflect->getModifiers(); } /** * Creates a string representation of this field * * @return string */ public function toString() { $t= $this->getType(); return sprintf( '%s%s %s::$%s', Modifiers::stringOf($this->getModifiers()), $t ? ' '.$t : '', $this->getDeclaringClass()->getName(), $this->getName() ); } } ?> * $method= XPClass::forName('lang.Object')->getMethod('toString'); * * var_dump($method->invoke(new Object())); * * * Example (passing arguments) * * $method= XPClass::forName('lang.types.String')->getMethod('concat'); * * var_dump($method->invoke(new String('Hello'), array('World'))); * * * Example (static invokation): * * $method= XPClass::forName('util.log.Logger')->getMethod('getInstance'); * * var_dump($method->invoke(NULL)); * * * @param lang.Object obj * @param mixed[] args default array() * @return mixed * @throws lang.IllegalArgumentException in case the passed object is not an instance of the declaring class * @throws lang.IllegalAccessException in case the method is not public or if it is abstract */ public function invoke($obj, $args= array()) { if (NULL !== $obj && !($obj instanceof $this->_class)) { throw new IllegalArgumentException(sprintf( 'Passed argument is not a %s class (%s)', xp::nameOf($this->_class), xp::typeOf($obj) )); } // Check modifers $m= $this->_reflect->getModifiers(); if (!($m & MODIFIER_PUBLIC) || $m & MODIFIER_ABSTRACT) { throw new IllegalAccessException(sprintf( 'Cannot invoke %s %s::%s', Modifiers::stringOf($this->getModifiers()), $this->_class, $this->_reflect->getName() )); } try { return $this->_reflect->invokeArgs($obj, (array)$args); } catch (Throwable $e) { throw new TargetInvocationException($this->_class.'::'.$this->_reflect->getName().'() ~ '.$e->getMessage(), $e); } catch (ReflectionException $e) { // This should never occur, we checked everything beforehand... throw new TargetInvocationException($this->_class.'::'.$this->_reflect->getName().'() ~ '.$e->getMessage(), new XPException($e->getMessage())); } } } ?> * [access] static abstract final * * [access] is one on public, private or protected. * * @param int m modifiers bitfield * @return string[] */ public static function namesOf($m) { $names= array(); switch ($m & (MODIFIER_PUBLIC | MODIFIER_PROTECTED | MODIFIER_PRIVATE)) { case MODIFIER_PRIVATE: $names[]= 'private'; break; case MODIFIER_PROTECTED: $names[]= 'protected'; break; case MODIFIER_PUBLIC: default: $names[]= 'public'; break; } if ($m & MODIFIER_STATIC) $names[]= 'static'; if ($m & MODIFIER_ABSTRACT) $names[]= 'abstract'; if ($m & MODIFIER_FINAL) $names[]= 'final'; return $names; } /** * Retrieves modifier names as a string * * @param int m modifiers bitfield * @return string */ public static function stringOf($m) { return implode(' ', self::namesOf($m)); } } ?> getClass(), XPClass::forName() * or by ClassLoader methods. * * Common use-cases * ================ * Instantiating a class by its name: * * $now= XPClass::forName('util.Date')->newInstance(); * * * Invoking a method by its name: * * $c= XPClass::forName('util.Binford'); * $c->getMethod('setPoweredBy')->invoke($c->newInstance(), array(6100)); * * * Retrieving annotations: * * $s= $service->getClass()->getMethod($invoked)->getAnnotation('security'); * if (!in_array($role, $s['roles'])) { * throw new IllegalAccessException('Access denied to '.$invoked); * } * * * Dynamic Proxies * =============== * The proxy class serves the purpose of dynamically creating instances * of interfaces. Use cases are remote method invocations, deferred * initialization or debugging. * * Around-Invoke: * * // Exchange the following: * $account= new Account(); * * // ...with this: * $account= Proxy::newProxyInstance( * ClassLoader::getDefault(), * array(XPClass::forName('com.acme.banking.IAccount')), * newinstance('lang.reflect.InvocationHandler', array(new Account()), '{ * public function __construct($wrapped) { * $this->wrapped= $wrapped; * } * * public function invoke($proxy, $method, $args) { * if ("transfer" == $method && $args[0] > 1000000) { * throw new IllegalAccessException("Too much money"); * } * return call_user_func_array(array($this->wrapped, $method), $args); * } * }') * ); * * $account->transfer(50); * $account->transfer(1000001); // BLAM * * * @see http://developer.xp-framework.net/xml/rfc/view?0030 * @see xp://lang.Generic#getClass * @see xp://lang.XPClass#forName * @see xp://lang.ClassLoader#loadClass * @see xp://lang.ClassLoader#defineClass * @see xp://lang.reflect.Proxy * @purpose Reflection */ package lang.reflect { } name; } /** * Checks if a specific class is provided by this package * * @param string name * @return bool */ public function providesClass($name) { return ClassLoader::getDefault()->providesClass($this->name.'.'.$name); } /** * Checks if a specific subpackage is provided by this package * * @param string name * @return bool */ public function providesPackage($name) { return ClassLoader::getDefault()->providesPackage($this->name.'.'.$name); } /** * Checks if a specific resource is provided by this package * * @param string name * @return bool */ public function providesResource($name) { return ClassLoader::getDefault()->providesResource(strtr($this->name, '.', '/').'/'.$name); } /** * Get all classes in this package. Loads classes if not already * loaded. * * @return lang.XPClass[] */ public function getClasses() { return array_map(array(xp::reflect('lang.XPClass'), 'forName'), $this->getClassNames()); } /** * Get the names of classes in this package, not loading them. * * @return string[] */ public function getClassNames() { $classes= array(); foreach (ClassLoader::getDefault()->packageContents($this->name) as $file) { if (xp::CLASS_FILE_EXT == substr($file, -10)) $classes[]= $this->name.'.'.substr($file, 0, -10); } return $classes; } /** * Load a specific class by its name, which may be either locally * qualified (without dots) or fully qualified (with dots). * * @param string name * @return lang.XPClass * @throws lang.IllegalArgumentException */ public function loadClass($name) { // Handle fully qualified names if (FALSE !== ($p= strrpos($name, '.'))) { if (substr($name, 0, $p) != $this->name) { throw new IllegalArgumentException('Class '.$name.' is not in '.$this->name); } $name= substr($name, $p+ 1); } return XPClass::forName($this->name.'.'.$name); } /** * Returns a list of subpackages in this package. * * @return lang.reflect.Package[] */ public function getPackages() { return array_map(array(xp::reflect('lang.reflect.Package'), 'forName'), $this->getPackageNames()); } /** * Returns a list of subpackages in this package. * * @return string[] */ public function getPackageNames() { $packages= array(); foreach (ClassLoader::getDefault()->packageContents($this->name) as $file) { if ('/' == substr($file, -1)) $packages[]= $this->name.'.'.substr($file, 0, -1); } return $packages; } /** * Returns a list of resources in this package. * * @return string[] */ public function getResources() { $resources= array(); foreach (ClassLoader::getDefault()->packageContents($this->name) as $file) { if ('/' == substr($file, -1) || xp::CLASS_FILE_EXT == substr($file, -10)) continue; $resources[]= strtr($this->name, '.', '/').'/'.$file; } return $resources; } /** * Get a specific subpackage of this package by its name, which * may be either locally qualified (without dots) or fully * qualified (with dots). * * @param string name * @return lang.reflect.Package * @throws lang.IllegalArgumentException */ public function getPackage($name) { // Handle fully qualified names if (FALSE !== ($p= strrpos($name, '.'))) { if (substr($name, 0, $p) != $this->name) { throw new IllegalArgumentException('Package '.$name.' is not in '.$this->name); } $name= substr($name, $p+ 1); } return self::forName($this->name.'.'.$name); } /** * Returns a Package object for a given fully qualified name. * * @param string name * @return lang.reflect.Package * @throws lang.ElementNotFoundException */ public static function forName($name) { $p= new self(); $p->name= rtrim($name, '.'); // Normalize if (!ClassLoader::getDefault()->providesPackage($p->name)) { raise('lang.ElementNotFoundException', 'No classloaders provide '.$name); } return $p; } /** * Loads a resource. * * @param string filename name of resource * @return string * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResource($filename) { // Handle fully qualified names if (FALSE !== ($p= strrpos($filename, '/'))) { if (substr($filename, 0, $p) != strtr($this->name, '.', '/')) { throw new IllegalArgumentException('Resource '.$filename.' is not in '.$this->name); } $filename= substr($filename, $p+ 1); } return ClassLoader::getDefault()->getResource(strtr($this->name, '.', '/').'/'.$filename); } /** * Retrieve a stream to the resource * * @param string filename name of resource * @return io.File * @throws lang.ElementNotFoundException in case the resource cannot be found */ public function getResourceAsStream($filename) { // Handle fully qualified names if (FALSE !== ($p= strrpos($filename, '/'))) { if (substr($filename, 0, $p) != strtr($this->name, '.', '/')) { throw new IllegalArgumentException('Resource '.$filename.' is not in '.$this->name); } $filename= substr($filename, $p+ 1); } return ClassLoader::getDefault()->getResourceAsStream(strtr($this->name, '.', '/').'/'.$filename); } /** * Creates a string representation of this package * * Example: *
         *   lang.reflect.Package
         * 
    * * @return string */ public function toString() { return $this->getClassName().'<'.$this->name.'>'; } /** * Checks whether a given object is equal to this Package instance. * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $this->name === $cmp->name; } /** * Creates a hashcode for this package * * @return string */ public function hashCode() { return 'P['.$this->name; } } ?> _reflect= $reflect; $this->_details= $details; } /** * Get parameter's name. * * @return string */ public function getName() { return $this->_reflect->getName(); } /** * Get parameter's type. * * @return lang.Type */ public function getType() { if ( !($details= XPClass::detailsForMethod($this->_details[0], $this->_details[1])) || !isset($details[DETAIL_ARGUMENTS][$this->_details[2]]) ) { // Unknown or unparseable, return ANYTYPE return Type::$ANY; } return Type::forName(ltrim($details[DETAIL_ARGUMENTS][$this->_details[2]], '&')); } /** * Get parameter's type. * * @return string */ public function getTypeName() { if ( !($details= XPClass::detailsForMethod($this->_details[0], $this->_details[1])) || !isset($details[DETAIL_ARGUMENTS][$this->_details[2]]) ) { // Unknown or unparseable, return ANYTYPE return '*'; } return ltrim($details[DETAIL_ARGUMENTS][$this->_details[2]], '&'); } /** * Get parameter's type restriction. * * @return lang.Type or NULL if there is no restriction */ public function getTypeRestriction() { if ($this->_reflect->isArray()) { return Primitive::$ARRAY; } else if ($c= $this->_reflect->getClass()) { return new XPClass($c); } else { return NULL; } } /** * Retrieve whether this argument is optional * * @return bool */ public function isOptional() { return $this->_reflect->isOptional(); } /** * Get default value. * * @throws lang.IllegalStateException in case this argument is not optional * @return mixed */ public function getDefaultValue() { if ($this->_reflect->isOptional()) { return $this->_reflect->getDefaultValue(); } throw new IllegalStateException('Parameter "'.$this->_reflect->getName().'" has no default value'); } /** * Creates a string representation * * @return string */ public function toString() { return sprintf( '%s<%s %s%s>', $this->getClassName(), $this->getType()->toString(), $this->_reflect->getName(), $this->_reflect->isOptional() ? '= '.xp::stringOf($this->_reflect->getDefaultValue()) : '' ); } } ?> _h= $handler; } /** * Returns the XPClass object for a proxy class given a class loader * and an array of interfaces. The proxy class will be defined by the * specified class loader and will implement all of the supplied * interfaces (also loaded by the classloader). * * @param lang.ClassLoader classloader * @param lang.XPClass[] interfaces names of the interfaces to implement * @return lang.XPClass * @throws lang.IllegalArgumentException */ public static function getProxyClass($classloader, $interfaces) { static $num= 0; static $cache= array(); // Calculate cache key (composed of the names of all interfaces) $key= $classloader->hashCode().':'.implode(';', array_map( create_function('$i', 'return $i->getName();'), $interfaces )); if (isset($cache[$key])) return $cache[$key]; // Create proxy class' name, using a unique identifier and a prefix $name= PROXY_PREFIX.($num++); $bytes= 'class '.$name.' extends '.xp::reflect('lang.reflect.Proxy').' implements '; $added= array(); for ($j= 0, $t= sizeof($interfaces); $j < $t; $j++) { $bytes.= xp::reflect($interfaces[$j]->getName()).', '; } $bytes= substr($bytes, 0, -2)." {\n"; for ($j= 0, $t= sizeof($interfaces); $j < $t; $j++) { $if= $interfaces[$j]; // Verify that the Class object actually represents an interface if (!$if->isInterface()) { throw new IllegalArgumentException($if->getName().' is not an interface'); } // Implement all the interface's methods foreach ($if->getMethods() as $m) { // Check for already declared methods, do not redeclare them if (isset($added[$m->getName()])) continue; $added[$m->getName()]= TRUE; // Build signature and argument list if ($m->hasAnnotation('overloaded')) { $signatures= $m->getAnnotation('overloaded', 'signatures'); $max= 0; $cases= array(); foreach ($signatures as $signature) { $args= sizeof($signature); $max= max($max, $args- 1); if (isset($cases[$args])) continue; $cases[$args]= ( 'case '.$args.': '. 'return $this->_h->invoke($this, \''.$m->getName(TRUE).'\', array('. ($args ? '$_'.implode(', $_', range(0, $args- 1)) : '').'));' ); } // Create method $bytes.= ( 'function '.$m->getName().'($_'.implode('= NULL, $_', range(0, $max)).'= NULL) { '. 'switch (func_num_args()) {'.implode("\n", $cases). ' default: throw new IllegalArgumentException(\'Illegal number of arguments\'); }'. '}'."\n" ); } else { $signature= $args= ''; foreach ($m->getParameters() as $param) { $restriction= $param->getTypeRestriction(); $signature.= ', '.($restriction ? xp::reflect($restriction->getName()) : '').' $'.$param->getName(); $args.= ', $'.$param->getName(); $param->isOptional() && $signature.= '= '.var_export($param->getDefaultValue(), TRUE); } $signature= substr($signature, 2); $args= substr($args, 2); // Create method $bytes.= ( 'function '.$m->getName().'('.$signature.') { '. 'return $this->_h->invoke($this, \''.$m->getName(TRUE).'\', array('.$args.')); '. '}'."\n" ); } } } $bytes.= ' }'; // Define the generated class try { $dyn= DynamicClassLoader::instanceFor(__METHOD__); $dyn->setClassBytes($name, $bytes); $class= $dyn->loadClass($name); } catch (FormatException $e) { throw new IllegalArgumentException($e->getMessage()); } // Update cache and return XPClass object $cache[$key]= $class; return $class; } /** * Returns an instance of a proxy class for the specified interfaces * that dispatches method invocations to the specified invocation * handler. * * @param lang.ClassLoader classloader * @param lang.XPClass[] interfaces * @param lang.reflect.InvocationHandler handler * @return lang.XPClass * @throws lang.IllegalArgumentException */ public static function newProxyInstance($classloader, $interfaces, $handler) { return self::getProxyClass($classloader, $interfaces)->newInstance($handler); } } ?> _class= $class; $this->_reflect= $reflect; } /** * Get routine's name. * * @return string */ public function getName() { return $this->_reflect->getName(); } /** * Retrieve this method's modifiers * * @see xp://lang.reflect.Modifiers * @return int */ public function getModifiers() { // Note: ReflectionMethod::getModifiers() returns whatever PHP reflection // returns, but the numeric value changed since 5.0.0 as the zend_function // struct's fn_flags now contains not only ZEND_ACC_(PPP, STATIC, FINAL, // ABSTRACT) but also some internal information about how this method needs // to be called. // // == List of fn_flags we don't want to return from this method == // #define ZEND_ACC_IMPLEMENTED_ABSTRACT 0x08 // #define ZEND_ACC_IMPLICIT_PUBLIC 0x1000 // #define ZEND_ACC_CTOR 0x2000 // #define ZEND_ACC_DTOR 0x4000 // #define ZEND_ACC_CLONE 0x8000 // #define ZEND_ACC_ALLOW_STATIC 0x10000 // #define ZEND_ACC_SHADOW 0x20000 // #define ZEND_ACC_DEPRECATED 0x40000 // == return $this->_reflect->getModifiers() & ~0x7f008; } /** * Returns this method's parameters * * @return lang.reflect.Parameter[] */ public function getParameters() { $r= array(); foreach ($this->_reflect->getParameters() as $offset => $param) { $r[]= new langreflectParameter($param, array($this->_class, $this->_reflect->getName(), $offset)); } return $r; } /** * Retrieve one of this method's parameters by its offset * * @param int offset * @return lang.reflect.Parameter or NULL if it does not exist */ public function getParameter($offset) { $list= $this->_reflect->getParameters(); return isset($list[$offset]) ? new langreflectParameter($list[$offset], array($this->_class, $this->_reflect->getName(), $offset)) : NULL ; } /** * Retrieve how many parameters this method declares (including optional * ones) * * @return int */ public function numParameters() { return $this->_reflect->getNumberOfParameters(); } /** * Retrieve return type * * @return string */ public function getReturnType() { if (!($details= XPClass::detailsForMethod($this->_class, $this->_reflect->getName()))) return Type::$ANY; return Type::forName(ltrim($details[DETAIL_RETURNS], '&')); } /** * Retrieve return type name * * @return string */ public function getReturnTypeName() { if (!($details= XPClass::detailsForMethod($this->_class, $this->_reflect->getName()))) return NULL; return ltrim($details[DETAIL_RETURNS], '&'); } /** * Retrieve exception names * * @return string[] */ public function getExceptionNames() { $details= XPClass::detailsForMethod($this->_class, $this->_reflect->getName()); return $details ? $details[DETAIL_THROWS] : array(); } /** * Retrieve exception types * * @return lang.XPClass[] */ public function getExceptionTypes() { $details= XPClass::detailsForMethod($this->_class, $this->_reflect->getName()); return $details ? array_map(array('XPClass', 'forName'), $details[DETAIL_THROWS]) : array(); } /** * Returns the XPClass object representing the class or interface * that declares the method represented by this Method object. * * @return lang.XPClass */ public function getDeclaringClass() { return new XPClass($this->_reflect->getDeclaringClass()); } /** * Retrieves the api doc comment for this method. Returns NULL if * no documentation is present. * * @return string */ public function getComment() { if (!($details= XPClass::detailsForMethod($this->_class, $this->_reflect->getName()))) return NULL; return $details[DETAIL_COMMENT]; } /** * Check whether an annotation exists * * @param string name * @param string key default NULL * @return bool */ public function hasAnnotation($name, $key= NULL) { $details= XPClass::detailsForMethod($this->_class, $this->_reflect->getName()); return $details && ($key ? array_key_exists($key, (array)@$details[DETAIL_ANNOTATIONS][$name]) : array_key_exists($name, (array)@$details[DETAIL_ANNOTATIONS]) ); } /** * Retrieve annotation by name * * @param string name * @param string key default NULL * @return mixed * @throws lang.ElementNotFoundException */ public function getAnnotation($name, $key= NULL) { $details= XPClass::detailsForMethod($this->_class, $this->_reflect->getName()); if (!$details || !($key ? array_key_exists($key, @$details[DETAIL_ANNOTATIONS][$name]) : array_key_exists($name, @$details[DETAIL_ANNOTATIONS]) )) return raise( 'lang.ElementNotFoundException', 'Annotation "'.$name.($key ? '.'.$key : '').'" does not exist' ); return ($key ? $details[DETAIL_ANNOTATIONS][$name][$key] : $details[DETAIL_ANNOTATIONS][$name] ); } /** * Retrieve whether a method has annotations * * @return bool */ public function hasAnnotations() { $details= XPClass::detailsForMethod($this->_class, $this->_reflect->getName()); return $details ? !empty($details[DETAIL_ANNOTATIONS]) : FALSE; } /** * Retrieve all of a method's annotations * * @return array annotations */ public function getAnnotations() { $details= XPClass::detailsForMethod($this->_class, $this->_reflect->getName()); return $details ? $details[DETAIL_ANNOTATIONS] : array(); } /** * Returns whether an object is equal to this routine * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return ( $cmp instanceof self && $cmp->_reflect->getName() === $this->_reflect->getName() && $cmp->getDeclaringClass()->equals($this->getDeclaringClass()) ); } /** * Retrieve string representation. Examples: * *
         *   public lang.XPClass getClass()
         *   public static util.Date now()
         *   public open(string $mode) throws io.FileNotFoundException, io.IOException
         * 
    * * @return string */ public function toString() { $signature= ''; foreach ($this->getParameters() as $param) { if ($param->isOptional()) { $signature.= ', ['.$param->getTypeName().' $'.$param->getName().'= '.xp::stringOf($param->getDefaultValue()).']'; } else { $signature.= ', '.$param->getTypeName().' $'.$param->getName(); } } if ($exceptions= $this->getExceptionNames()) { $throws= ' throws '.implode(', ', $exceptions); } else { $throws= ''; } return sprintf( '%s %s %s(%s)%s', Modifiers::stringOf($this->getModifiers()), $this->getReturnTypeName(), $this->getName(), substr($signature, 2), $throws ); } } ?> ., . if (file_exists($lib= $path.'php_'.$filename)) { // E.g. php_sybase_ct.dll } else if (file_exists($lib= $path.$filename)) { // E.g. sybase_ct.so } else { throw new ElementNotFoundException('Cannot find library "'.$name.'" in "'.$path.'"'); } // Found library, try to load it. dl() expects given argument to not contain // a path and will failt with "Temporary module name should contain only // filename" if it does. if (!dl(basename($lib))) { throw new RuntimeError('dl() failed for '.$lib); } return TRUE; } /** * Check whether a given extension is available * * @see php://extension_loaded * @param string name * @return bool */ public function extensionAvailable($name) { return extension_loaded($name); } /** * Register a shutdown hook - a piece of code that will be run before * the runtime shuts down (e.g. with exit). * * @see php://register_shutdown_function * @param lang.Runnable r * @return lang.Runnable the given runnable */ public function addShutdownHook(Runnable $r) { register_shutdown_function(array($r, 'run')); return $r; } /** * Retrieve the executable associated with this runtime. * * @return string */ public function getExecutable() { if (NULL === $this->executable) { // Lazy-init $this->executable= Process::getProcessById(getmypid()); } return $this->executable; } } ?> file = $file; $this->class = $class; $this->method = $method; $this->line = $line; $this->args = $args; $this->message = $message; } /** * Returns qualified class name * * @param string class unqualified name * @return string */ protected function qualifiedClassName($class) { return xp::nameOf($class); } /** * Create string representation * * @return string */ public function toString() { $args= array(); if (isset($this->args)) { for ($j= 0, $a= sizeof($this->args); $j < $a; $j++) { if (is_array($this->args[$j])) { $args[]= 'array['.sizeof($this->args[$j]).']'; } else if (is_object($this->args[$j])) { $args[]= $this->qualifiedClassName(get_class($this->args[$j])).'{}'; } else if (is_string($this->args[$j])) { $display= str_replace('%', '%%', addcslashes(substr($this->args[$j], 0, min( (FALSE === $p= strpos($this->args[$j], "\n")) ? 0x40 : $p, 0x40 )), "\0..\17")); $args[]= ( '(0x'.dechex(strlen($this->args[$j])).")'". $display. "'" ); } else if (is_null($this->args[$j])) { $args[]= 'NULL'; } else if (is_scalar($this->args[$j])) { $args[]= (string)$this->args[$j]; } else if (is_resource($this->args[$j])) { $args[]= (string)$this->args[$j]; } else { $args[]= '<'.gettype($this->args[$j]).'>'; } } } return sprintf( " at %s::%s(%s) [line %d of %s] %s\n", isset($this->class) ? $this->qualifiedClassName($this->class) : '
    ', isset($this->method) ? $this->method : '
    ', implode(', ', $args), $this->line, basename(isset($this->file) ? $this->file : __FILE__), $this->message ); } /** * Compares this stacktrace element to another object * * @param lang.Object cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $this->toString() == $cmp->toString(); } } ?> * php.version PHP version * php.api PHP api * os.name Operating system name * os.tempdir System-wide temporary directory * host.name Host name * host.arch Host architecture * user.home Current user's home directory * user.name Current user's name * file.separator File separator ("/" on UNIX) * path.separator Path separator (":" on UNIX) * * * @param string name * @return mixed */ public static function getProperty($name) { static $prop= array(); if (!isset($prop[$name])) switch ($name) { case 'php.version': $prop[$name]= PHP_VERSION; break; case 'php.api': $prop[$name]= PHP_SAPI; break; case 'os.name': $prop[$name]= PHP_OS; break; case 'os.tempdir': $prop[$name]= self::tempDir(); break; case 'host.name': if (extension_loaded('posix')) { $uname= posix_uname(); $prop[$name]= $uname['nodename'].(isset ($uname['domainname']) ? '.'.$uname['domainname'] : '' ); break; } $prop[$name]= self::_env('HOSTNAME', 'COMPUTERNAME'); break; case 'host.arch': if (extension_loaded('posix')) { $uname= posix_uname(); $prop[$name]= $uname['machine']; break; } $prop[$name]= self::_env('HOSTTYPE', 'PROCESSOR_ARCHITECTURE'); break; case 'user.home': if (extension_loaded('posix')) { $pwuid= posix_getpwuid(posix_getuid()); $prop[$name]= $pwuid['dir']; break; } $prop[$name]= str_replace('\\', DIRECTORY_SEPARATOR, self::_env('HOME', 'HOMEPATH')); break; case 'user.name': $prop[$name]= get_current_user(); break; case 'file.separator': return DIRECTORY_SEPARATOR; case 'path.separator': return PATH_SEPARATOR; } return $prop[$name]; } /** * Sets an environment variable * * @param string name * @param mixed var * @return bool success */ public static function putEnv($name, $var) { return putenv($name.'='.$var); } /** * Returns the contents of an environment variable, or in case it does * not exist, FALSE. * * @param string name * @return mixed var */ public static function getEnv($name) { return getenv($name); } /** * Retrieve location of temporary directory. This method looks at the * environment variables TEMP and TMP, and, if these cannot be found, * uses '/tmp' as location on Un*x systems, c:\ on Windows. * * @see php://tempnam * @return string */ public static function tempDir() { if (getenv('TEMP')) { $dir= getenv('TEMP'); } else if (getenv('TMP')) { $dir= getenv('TMP'); } else { $dir= (0 == strcasecmp(substr(PHP_OS, 0, 3), 'WIN')) ? 'c:\\' : '/tmp'; } return rtrim($dir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; } /** * Execute an external program * * Copied from man bash, some information on the exit code *
         * EXIT STATUS
         * For  the  shell's  purposes,  a command which exits with a
         * zero exit status has succeeded.  An exit  status  of  zero
         * indicates success.  A non-zero exit status indicates fail
         * ure.  When a command terminates on a fatal signal N,  bash
         * uses the value of 128+N as the exit status.
         *
         * If  a  command  is not found, the child process created to
         * execute it returns a status of 127.  If a command is found
         * but is not executable, the return status is 126.
         *
         * If a command fails because of an error during expansion or
         * redirection, the exit status is greater than zero.
         *
         * Shell builtin commands return a status of 0 (true) if suc
         * cessful,  and  non-zero  (false)  if an error occurs while
         * they execute.  All builtins return an exit status of 2  to
         * indicate incorrect usage.
         *
         * Bash  itself  returns  the exit status of the last command
         * executed, unless a syntax error occurs, in which  case  it
         * exits  with  a  non-zero value.  See also the exit builtin
         * command below.
         * 
    * * @param string cmdLine the command * @param string redirect default '2>&1' redirection * @param bool background * @return array lines * @throws lang.SystemException in case the return code is not zero * @see php://exec * @see xp://lang.Process */ public static function exec($cmdLine, $redirect= '2>&1', $background= FALSE) { $cmdLine= escapeshellcmd($cmdLine).' '.$redirect.($background ? ' &' : ''); if (!($pd= popen($cmdLine, 'r'))) throw(new XPException( 'cannot execute "'.$cmdLine.'"' )); $buf= array(); while ( (!feof($pd)) && (FALSE !== ($line= fgets($pd, 4096))) ) { $buf[]= chop($line); } $retCode= (pclose($pd) >> 8) & 0xFF; if ($retCode != 0) throw(new SystemException( 'System.exec('.$cmdLine.') err #'.$retCode.' ['.implode('', $buf).']', $retCode )); return $buf; } } ?> code= $code; parent::__construct($message); } } ?> * uses('lang.Thread'); * * class TimerThread extends Thread { * public * $ticks = 0, * $timeout = 0; * * public function __construct($timeout) { * $this->timeout= $timeout; * parent::__construct('timer.'.$timeout); * } * * public function run() { * while ($this->ticks < $this->timeout) { * Thread::sleep(1000); * $this->ticks++; * printf("<%s> tick\n", $this->name); * } * printf("<%s> time's up!\n", $this->name); * } * } * * $t[0]= new TimerThread(5); * $t[0]->start(); * $t[1]= new TimerThread(2); * $t[1]->start(); * * for ($i= 0; $i < 3; $i++) { * echo "
    Waiting...\n"; * sleep(1); * } * * $t[0]->join(); * $t[1]->join(); * * * @ext pcntl * @ext posix * @see http://news.xp-framework.net/article/168/2007/04/05/ * @see xp://lang.Runnable * @purpose Base class */ class Thread extends Object { public $name = '', $running = FALSE; protected $target = NULL, $_id = -1, $_pid = -1; /** * Constructor * * Implementation by subclassing: * * class ComputeThread extends Thread { * public function run() { * // ... * } * } * * $thread= new ComputeThread('computr1'); * $thread->start(); * * * Implementation by passing a Runnable: * * $thread= new Thread(newinstance('lang.Runnable', array(), '{ * public function run() { * // ... * } * }')); * $thread->start(); * * * @param mixed arg default NULL */ public function __construct($arg= NULL) { if ($arg instanceof Runnable) { $this->target= $arg; $this->name= $arg->getClassName(); } else { $this->target= $this; $this->name= $arg ? $arg : $this->getClassName(); } } /** * Returns whether this thread is running * * @return bool */ public function isRunning() { return $this->running; } /** * Set Name * * @param string name */ public function setName($name) { $this->name= $name; } /** * Get Name * * @return string */ public function getName() { return $this->name; } /** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds. * * @param int millis */ public static function sleep($millis) { usleep($millis * 1000); } /** * Starts thread execution * * @throws lang.IllegalThreadStateException if this thread is already running * @throws lang.SystemException if the thread cannot be started */ public function start() { if ($this->isRunning()) { throw(new IllegalThreadStateException('Already running')); } $parent= getmypid(); $pid= pcntl_fork(); if (-1 == $pid) { // Cannot fork throw(new SystemException('Cannot fork')); } else if ($pid) { // Parent $this->running= TRUE; $this->_id= $pid; $this->_pid= $parent; } else { // Child $this->running= TRUE; $this->_id= getmypid(); $this->_pid= $parent; call_user_func(array($this->target, 'run')); exit(); } } /** * Join this thread. The optional parameter wait may be set to FALSE to * return immediately if this thread hasn't terminated yet. * * @param bool wait default TRUE * @return int status * @see php://pcntl_waitpid */ public function join($wait= TRUE) { if (0 == pcntl_waitpid($this->_id, $status, $wait ? WUNTRACED : WNOHANG)) return -1; $this->running= FALSE; $this->_id= $this->_pid= -1; return $status; } /** * Stop this thread * * @param int signal default SIGINT * @throws lang.IllegalThreadStateException */ public function stop($signal= SIGINT) { if ($this->_id <= 0) { throw(new IllegalThreadStateException('Illegal thread id '.$this->_id)); } posix_kill($this->_id, $signal); $this->running= FALSE; $this->_id= $this->_pid= -1; } /** * Returns thread id or -1 if this thread is not running * * @return int */ public function getId() { return $this->_id; } /** * Returns thread's parent id * * @return int */ public function getParentId() { return $this->_pid; } /** * Creates a string representation * * @return string */ public function toString() { return sprintf('%s[%d]@%s', $this->getClassName(), $this->_id, var_export($this, 1)); } /** * Subclasses of Thread should override this method. * */ public function run() { } } ?> 1, 'call_user_func' => 1, 'object' => 1 ); $this->__id= microtime(); $this->message= $message; $errors= xp::$registry['errors']; foreach (debug_backtrace() as $trace) { if (!isset($trace['function']) || isset($except[$trace['function']])) continue; if (isset($trace['object']) && '__construct' == $trace['function'] && $trace['object'] instanceof self) continue; // Not all of these are always set: debug_backtrace() should // initialize these - at least - to NULL, IMO => Workaround. $this->addStackTraceFor( isset($trace['file']) ? $trace['file'] : NULL, isset($trace['class']) ? $trace['class'] : NULL, isset($trace['function']) ? $trace['function'] : NULL, isset($trace['line']) ? $trace['line'] : NULL, isset($trace['args']) ? $trace['args'] : NULL, array(array('' => 1)) ); } // Remaining error messages foreach ($errors as $file => $list) { $this->addStackTraceFor($file, NULL, NULL, NULL, array(), $list); } } /** * Adds new stacktrace elements to the internal list of stacktrace * elements, each for one error. * * @param string file * @param string class * @param string function * @param int originalline * @param mixed[] args * @param mixed[] errors */ protected function addStackTraceFor($file, $class, $function, $originalline, $args, $errors) { foreach ($errors as $line => $errormsg) { foreach ($errormsg as $message => $amount) { $this->trace[]= new StackTraceElement( $file, $class, $function, $originalline ? $originalline : $line, $args, $message.($amount > 1 ? ' (... '.($amount - 1).' more)' : '') ); } } } /** * Return an array of stack trace elements * * @return lang.StackTraceElement[] array of stack trace elements * @see xp://lang.StackTraceElement */ public function getStackTrace() { return $this->trace; } /** * Print "stacktrace" to standard error * * @see xp://lang.Throwable#toString * @param resource fd default STDERR */ public function printStackTrace($fd= STDERR) { fputs($fd, $this->toString()); } /** * Return compound message of this exception. In this default * implementation, returns the following: * *
         *   Exception [FULLY-QUALIFIED-CLASSNAME] ([MESSAGE])
         * 
    * * May be overriden by subclasses * * @return string */ public function compoundMessage() { return sprintf( 'Exception %s (%s)', $this->getClassName(), $this->message ); } /** * Return compound message followed by the formatted output of this * exception's stacktrace. * * Example: *
         * Exception lang.ClassNotFoundException (class "" [] not found)
         *   at lang.ClassNotFoundException::__construct((0x15)'class "" [] not found') \
         *   [line 79 of StackTraceElement.class.php] 
         *   at lang.ClassLoader::loadclass(NULL) [line 143 of XPClass.class.php] 
         *   at lang.XPClass::forname(NULL) [line 6 of base_test.php] \
         *   Undefined variable:  nam
         * 
    * * Usually not overridden by subclasses unless stacktrace format * should differ - otherwise overwrite compoundMessage() instead!. * * @return string */ public function toString() { $s= $this->compoundMessage()."\n"; for ($i= 0, $t= sizeof($this->trace); $i < $t; $i++) { $s.= $this->trace[$i]->toString(); } return $s; } /** * Returns a hashcode for this object * * @return string */ public function hashCode() { return $this->__id; } /** * Indicates whether some other object is "equal to" this one. * * @param lang.Object cmp * @return bool TRUE if the compared object is equal to this object */ public function equals($cmp) { return $this === $cmp; } /** * Returns the fully qualified class name for this class * (e.g. "io.File") * * @return string fully qualified class name */ public function getClassName() { return xp::nameOf(get_class($this)); } /** * Returns the runtime class of an object. * * @return lang.XPClass runtime class * @see xp://lang.XPClass */ public function getClass() { return new XPClass($this); } } ?> name= $name; } /** * Retrieves the fully qualified class name for this class. * * @return string name - e.g. "io.File", "rdbms.mysql.MySQL" */ public function getName() { return $this->name; } /** * Creates a string representation of this object * * @return string */ public function toString() { return $this->getClassName().'<'.$this->name.'>'; } /** * Checks whether a given object is equal to this type * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $cmp->name === $this->name; } /** * Returns a hashcode for this object * * @return string */ public function hashCode() { return get_class($this).':'.$this->name; } /** * Gets a type for a given name * * Checks for: *
      *
    • Primitive types (string, integer, double, boolean, array)
    • *
    • Array notations (string[] or string*)
    • *
    • Resources
    • *
    • Any type (mixed or *)
    • *
    • Generic notations (util.collections.HashTable)
    • *
    • Anything else will be passed to XPClass::forName()
    • *
    * * @param string name * @return lang.Type */ public static function forName($name) { switch ($name) { case 'string': case 'char': return Primitive::$STRING; case 'integer': case 'int': return Primitive::$INTEGER; case 'double': case 'float': return Primitive::$DOUBLE; case 'boolean': case 'bool': return Primitive::$BOOLEAN; case '*': case 'mixed': return self::$ANY; case 'array': case '*' == substr($name, -1): case '[]' === substr($name, -2): return Primitive::$ARRAY; case 'resource': // XXX FIXME return Primitive::$INTEGER; case 'void': return self::$VOID; case FALSE !== ($p= strpos($name, '<')): $base= substr($name, 0, $p); return 'array' == $base ? Primitive::$ARRAY : XPClass::forName($base); case FALSE === strpos($name, '.'): return new XPClass(new ReflectionClass($name)); default: return XPClass::forName($name); } } } ?> values or an int => size * @return lang.types.ArrayList */ public static function newInstance($arg) { if (is_array($arg)) { $self= new self(); $self->values= array_values($arg); $self->length= sizeof($self->values); } else { $self= new self(); $self->length= (int)$arg; } return $self; } /** * Returns a hashcode for this number * * @return string */ public function hashCode() { return $this->length.'['.serialize($this->values); } /** * Constructor * * @param mixed* values */ public function __construct() { if (0 != ($this->length= func_num_args())) { $this->values= func_get_args(); } } /** * Returns an iterator for use in foreach() * * @see php://language.oop5.iterations * @return php.Iterator */ public function getIterator() { if (!$this->iterator) $this->iterator= newinstance('Iterator', array($this), '{ private $i= 0, $v; public function __construct($v) { $this->v= $v; } public function current() { return $this->v->values[$this->i]; } public function key() { return $this->i; } public function next() { $this->i++; } public function rewind() { $this->i= 0; } public function valid() { return $this->i < $this->v->length; } }'); return $this->iterator; } /** * = list[] overloading * * @param int offset * @return mixed * @throws lang.IndexOutOfBoundsException if key does not exist */ public function offsetGet($offset) { if ($offset >= $this->length || $offset < 0) { raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds'); } return $this->values[$offset]; } /** * list[]= overloading * * @param int offset * @param mixed value * @throws lang.IllegalArgumentException if key is neither numeric (set) nor NULL (add) */ public function offsetSet($offset, $value) { if (!is_int($offset)) { throw new IllegalArgumentException('Incorrect type '.gettype($offset).' for index'); } if ($offset >= $this->length || $offset < 0) { raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds'); } $this->values[$offset]= $value; } /** * isset() overloading * * @param int offset * @return bool */ public function offsetExists($offset) { return ($offset >= 0 && $offset < $this->length); } /** * unset() overloading * * @param int offset */ public function offsetUnset($offset) { throw new IllegalArgumentException('Cannot remove from immutable list'); } /** * Helper method to compare two arrays recursively * * @param array a1 * @param array a2 * @return bool */ protected function arrayequals($a1, $a2) { if (sizeof($a1) != sizeof($a2)) return FALSE; foreach (array_keys((array)$a1) as $k) { switch (TRUE) { case !array_key_exists($k, $a2): return FALSE; case is_array($a1[$k]): if (!$this->arrayequals($a1[$k], $a2[$k])) return FALSE; break; case $a1[$k] instanceof Generic: if (!$a1[$k]->equals($a2[$k])) return FALSE; break; case $a1[$k] !== $a2[$k]: return FALSE; } } return TRUE; } /** * Checks whether a given object is equal to this arraylist * * @param lang.Object cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $this->arrayequals($this->values, $cmp->values); } /** * Returns a string representation of this object * * @return string */ public function toString() { return ( $this->getClassName().'['.sizeof($this->values)."]@{". implode(', ', array_map(array('xp', 'stringOf'), $this->values)). '}' ); } } ?> value= (bool)$value; } /** * Returns the value of this number as an int. * * @return int */ public function intValue() { return (int)$this->value; } /** * Returns a hashcode for this number * * @return string */ public function hashCode() { return $this->value ? 'true' : 'false'; } /** * Returns a string representation of this number object * * @return string */ public function toString() { return $this->getClassName().'('.$this->value.')'; } /** * Indicates whether some other object is "equal to" this one. * * @param lang.Object cmp * @return bool TRUE if the compared object is equal to this object */ public function equals($cmp) { return $cmp instanceof self && $this->value === $cmp->value; } } ?> value) : $in{0}) ; } /** * Constructor * * @param mixed initial default NULL * @throws lang.IllegalArgumentException in case argument is of incorrect type. */ public function __construct($initial= NULL) { if (NULL === $initial) { // Intentionally empty } else if (is_array($initial)) { $this->buffer= implode('', array_map(array($this, 'asByte'), $initial)); } else if (is_string($initial)) { $this->buffer= $initial; } else { throw new IllegalArgumentException('Expected either Byte[], char[], int[] or string'); } $this->size= strlen($this->buffer); } /** * Returns an iterator for use in foreach() * * @see php://language.oop5.iterations * @return php.Iterator */ public function getIterator() { if (!$this->iterator) $this->iterator= newinstance('Iterator', array($this), '{ private $i= 0, $v; public function __construct($v) { $this->v= $v; } public function current() { $n= ord($this->v->buffer{$this->i}); return new Byte($n < 128 ? $n : $n - 256); } public function key() { return $this->i; } public function next() { $this->i++; } public function rewind() { $this->i= 0; } public function valid() { return $this->i < $this->v->size; } }'); return $this->iterator; } /** * = list[] overloading * * @param int offset * @return lang.types.Byte * @throws lang.IndexOutOfBoundsException if offset does not exist */ public function offsetGet($offset) { if ($offset >= $this->size || $offset < 0) { raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds'); } $n= ord($this->buffer{$offset}); return new Byte($n < 128 ? $n : $n - 256); } /** * list[]= overloading * * @param int offset * @param mixed value * @throws lang.IllegalArgumentException if key is neither numeric (set) nor NULL (add) * @throws lang.IndexOutOfBoundsException if key does not exist */ public function offsetSet($offset, $value) { if (NULL === $offset) { $this->buffer.= $this->asByte($value); $this->size++; } else if ($offset >= $this->size || $offset < 0) { raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds'); } else { $this->buffer{$offset}= $this->asByte($value); } } /** * isset() overloading * * @param int offset * @return bool */ public function offsetExists($offset) { return ($offset >= 0 && $offset < $this->size); } /** * unset() overloading * * @param int offset * @throws lang.IndexOutOfBoundsException if offset does not exist */ public function offsetUnset($offset) { if ($offset >= $this->size || $offset < 0) { raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds'); } $this->buffer= ( substr($this->buffer, 0, $offset). substr($this->buffer, $offset+ 1, $this->size) ); $this->size--; } /** * Returns this byte list's size * * @return string */ public function size() { return $this->size; } /** * Returns whether a given object is equal to this object * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return ( $cmp instanceof self && $this->size === $cmp->size && $this->buffer === $cmp->buffer ); } /** * Returns a hashcode for this bytes object * * @return string */ public function hashCode() { return md5($this->buffer); } /** * Returns a string representation of this string. * * @return string */ public function toString() { return $this->getClassName().'('.$this->size.')@{'.addcslashes($this->buffer, "\0..\37\177..\377").'}'; } /** * String conversion overloading. This is for use with fwrite() * * @return string */ public function __toString() { return $this->buffer; } } ?> * $c= new Character(8364); // The EUR symbol (U+20AC) * $c= new Character(0x20AC); // ...same, using hexadecimal * $c= new Character('', 'ISO-8859-1'); // The German Umlaut A (capital) * * $s= new String('bercoder', 'ISO-8859-1'); * $c= $s->charAt(0); // The German Umlaut U (capital) * $c= $s[0]; // ...same, via [] operator * * $c= $s->charAt(1); // "b" * $c= $s[1]; // "b" * * * @ext iconv * @test xp://net.xp_framework.unittest.core.types.CharacterTest * @purpose Wrapper type */ class Character extends Object { protected $buffer= ''; /** * Constructor * * @param mixed arg either a string or an int * @param string charset default NULL */ public function __construct($arg, $charset= NULL) { if (is_int($arg)) { $this->buffer= iconv('UCS-4BE', 'UTF-8', pack('N', $arg)); return; } $charset= strtoupper($charset ? $charset : iconv_get_encoding('input_encoding')); // Convert the input to internal encoding $this->buffer= iconv($charset, 'UTF-8', $arg); if (xp::errorAt(__FILE__, __LINE__ - 1)) { $message= key(xp::$registry['errors'][__FILE__][__LINE__ - 2]); xp::gc(); throw new FormatException($message.($charset == 'UTF-8' ? ' with charset '.$charset : $message.' while converting input from '.$charset.' to '.'UTF-8' )); } if (1 != ($l= iconv_strlen($this->buffer, 'UTF-8'))) { throw new IllegalArgumentException('Given argument is too long ('.$l.')'); } } /** * Returns whether a given object is equal to this object * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $this->buffer === $cmp->buffer; } /** * Returns a hashcode for this string object * * @return string */ public function hashCode() { return $this->buffer; } /** * Returns a string representation of this string. Uses the current * output encoding and transliteration. * * @return string */ public function toString() { return iconv('UTF-8', iconv_get_encoding('output_encoding').'//TRANSLIT', $this->buffer); } /** * Returns a string representation of this string. Uses the current * output encoding and transliteration. * * @return string */ public function __toString() { return iconv(STR_ENC, iconv_get_encoding('output_encoding').'//TRANSLIT', $this->buffer); } /** * Returns the bytes representing this character * * @param string charset default NULL * @return lang.types.Bytes */ public function getBytes($charset= NULL) { $charset= strtoupper($charset ? $charset : iconv_get_encoding('input_encoding')); return new Bytes(STR_ENC === $charset ? $this->buffer : iconv(STR_ENC, $charset, $this->buffer) ); } } ?> value= (string)$value; } /** * Returns the value of this number as an int. * * @return int */ public function intValue() { return (int)$this->value; } /** * Returns the value of this number as a float. * * @return float */ public function floatValue() { return (float)$this->value; } /** * Returns a hashcode for this number * * @return string */ public function hashCode() { return $this->value; } /** * Returns a string representation of this number object * * @return string */ public function toString() { return $this->getClassName().'('.$this->value.')'; } /** * Indicates whether some other object is "equal to" this one. * * @param lang.Object cmp * @return bool TRUE if the compared object is equal to this object */ public function equals($cmp) { return $cmp instanceof self && $this->value === $cmp->value; } } ?> *
  • Byte
  • *
  • Float
  • *
  • Long
  • *
  • Short
  • *
  • Integer
  • * * ...and map to whatever the "remote" type is defined as. They all take * strings as arguments to preserve the value exactly. The respective * classes extend from the base class lang.reflect.Number. * * ArrayList * ========= * This class represents a zero-indexed list of elements of any type. * It is used to give marshalling utilities an easy way to see whether * an array is "numeric" or not (PHP's array types can be either numeric, * hashes or a mix of both and aren't necessarily zero-indexed). * * @see http://news.xp-framework.net/article/52/2005/05/29/ * @see http://news.xp-framework.net/article/54/2005/06/12/ * @see http://developer.xp-framework.net/xml/rfc/view?0038 * @see xp://lang.types.Number * @see xp://lang.types.ArrayList * @purpose Type wrappers */ package lang.types { } buffer; } else if ($arg instanceof Character) { return $arg->getBytes(STR_ENC); } else if (is_string($arg) || $arg instanceof Bytes) { $charset= strtoupper($charset ? $charset : iconv_get_encoding('input_encoding')); // Convert the input to internal encoding $buffer= iconv($charset, STR_ENC, $arg); if (xp::errorAt(__FILE__, __LINE__ - 1)) { $message= key(xp::$registry['errors'][__FILE__][__LINE__ - 2]); xp::gc(); throw new FormatException($message.($charset == STR_ENC ? ' with charset '.$charset : $message.' while converting input from '.$charset.' to '.STR_ENC )); } return $buffer; } else { return (string)$arg; } } /** * Constructor * * @param string initial default '' * @param string charset default NULL */ public function __construct($initial= '', $charset= NULL) { $this->buffer= $this->asIntern($initial, $charset); $this->length= iconv_strlen($this->buffer, STR_ENC); } /** * = list[] overloading * * @param int offset * @return lang.types.Character * @throws lang.IndexOutOfBoundsException if key does not exist */ public function offsetGet($offset) { return $this->charAt($offset); } /** * list[]= overloading * * @param int offset * @param mixed value * @throws lang.IllegalArgumentException if key is neither numeric (set) nor NULL (add) */ public function offsetSet($offset, $value) { if (!is_int($offset)) { throw new IllegalArgumentException('Incorrect type '.gettype($offset).' for index'); } if ($offset >= $this->length || $offset < 0) { raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds'); } $char= $this->asIntern($value); if (1 != iconv_strlen($char, STR_ENC)) { throw new IllegalArgumentException('Set only allows to set one character!'); } $this->buffer= ( iconv_substr($this->buffer, 0, $offset, STR_ENC). $char. iconv_substr($this->buffer, $offset+ 1, $this->length, STR_ENC) ); } /** * isset() overloading * * @param int offset * @return bool */ public function offsetExists($offset) { return ($offset >= 0 && $offset < $this->length); } /** * unset() overloading * * @param int offset */ public function offsetUnset($offset) { if ($offset >= $this->length || $offset < 0) { raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds'); } $this->buffer= ( iconv_substr($this->buffer, 0, $offset, STR_ENC). iconv_substr($this->buffer, $offset+ 1, $this->length, STR_ENC) ); $this->length= iconv_strlen($this->buffer, STR_ENC); } /** * Returns the string's length (the number of characters in this * string, not the number of bytes) * * @return string */ public function length() { return $this->length; } /** * Returns the character at the given position * * @param int offset * @return lang.types.Character * @throws lang.IndexOutOfBoundsException if key does not exist */ public function charAt($offset) { if ($offset >= $this->length || $offset < 0) { raise('lang.IndexOutOfBoundsException', 'Offset '.$offset.' out of bounds'); } return new Character(iconv_substr($this->buffer, $offset, 1, STR_ENC), STR_ENC); } /** * Returns the index within this string of the first occurrence of * the specified substring. * * @param mixed arg either a string or a String * @param int start default 0 * @return bool */ public function indexOf($arg, $start= 0) { $r= iconv_strpos($this->buffer, $this->asIntern($arg), $start, STR_ENC); return FALSE === $r ? -1 : $r; } /** * Returns the index within this string of the last occurrence of * the specified substring. * * @param mixed arg either a string or a String * @return bool */ public function lastIndexOf($arg) { $r= iconv_strrpos($this->buffer, $this->asIntern($arg), STR_ENC); return FALSE === $r ? -1 : $r; } /** * Returns a new string that is a substring of this string. * * @param int start * @param int length default 0 * @return lang.types.String */ public function substring($start, $length= 0) { if (0 === $length) $length= $this->length; return new self(iconv_substr($this->buffer, $start, $length, STR_ENC), STR_ENC); } /** * Returns whether a given substring is contained in this string * * @param mixed arg * @return bool */ public function contains($arg) { return -1 != $this->indexOf($arg); } /** * Returns whether a given substring is contained in this string * * @param mixed old * @param mixed new default '' * @return lang.types.String this string */ public function replace($old, $new= '') { $this->buffer= str_replace($this->asIntern($old), $this->asIntern($new), $this->buffer); $this->length= iconv_strlen($this->buffer, STR_ENC); return $this; } /** * Concatenates the given argument to the end of this string and returns * this String so it can be used in chained calls: * * * $s= new String('Hello'); * $s->concat(' ')->concat('World'); * * * @param mixed arg * @return lang.types.String this string */ public function concat($arg) { $this->buffer.= $this->asIntern($arg); $this->length= iconv_strlen($this->buffer, STR_ENC); return $this; } /** * Returns whether this string starts with a given argument. * * @param mixed arg either a string or a String * @return bool */ public function startsWith($arg) { return 0 == $this->indexOf($arg); } /** * Returns whether this string starts with a given argument. * * @param mixed arg either a string or a String * @return bool */ public function endsWith($arg) { $bytes= $this->asIntern($arg); return ( iconv_strlen($this->buffer, STR_ENC) - iconv_strlen($bytes, STR_ENC) === iconv_strrpos($this->buffer, $bytes, STR_ENC) ); } /** * Returns whether a given object is equal to this object * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $this->buffer === $cmp->buffer; } /** * Returns a hashcode for this string object * * @return string */ public function hashCode() { return md5($this->buffer); } /** * Returns a string representation of this string. Uses the current * output encoding and transliteration. * * @return string */ public function toString() { return iconv(STR_ENC, iconv_get_encoding('output_encoding').'//TRANSLIT', $this->buffer); } /** * Returns a string representation of this string. Uses the current * output encoding and transliteration. * * @return string */ public function __toString() { return iconv(STR_ENC, iconv_get_encoding('output_encoding').'//TRANSLIT', $this->buffer); } /** * Returns the bytes representing this string * * @param string charset default 'UTF-8' * @return lang.types.Bytes */ public function getBytes($charset= NULL) { $charset= strtoupper($charset ? $charset : iconv_get_encoding('input_encoding')); return new Bytes(STR_ENC === $charset ? $this->buffer : iconv(STR_ENC, $charset, $this->buffer) ); } } ?> getClass() syntax or the static method * $class= XPClass::forName('fully.qualified.Name') * * Examples * ======== * To retrieve the fully qualified name of a class, use this: * * $o= new File('...'); * echo 'The class name for $o is '.$o->getClass()->getName(); * * * Create an instance of a class: * * $instance= XPClass::forName('util.Binford')->newInstance(); * * * Invoke a method by its name: * * try { * $instance->getClass()->getMethod('connect')->invoke($instance); * } catch (TargetInvocationException $e) { * $e->getCause()->printStackTrace(); * } * * * @see xp://lang.Object#getClass * @see xp://lang.XPClass#forName * @test xp://net.xp_framework.unittest.reflection.ReflectionTest * @test xp://net.xp_framework.unittest.reflection.ClassDetailsTest * @purpose Reflection */ class XPClass extends Type { protected $_class = NULL; public $_reflect = NULL; /** * Constructor * * @param mixed ref either a class name, a ReflectionClass instance or an object */ public function __construct($ref) { if ($ref instanceof ReflectionClass) { $this->_class= $ref->getName(); $this->_reflect= $ref; } else if (is_object($ref)) { $this->_class= get_class($ref); $this->_reflect= new ReflectionClass($ref); } else { $this->_class= $ref; $this->_reflect= new ReflectionClass($ref); } parent::__construct(xp::nameOf($this->_class)); } /** * Retrieves the package associated with this class * * @return lang.reflect.Package */ public function getPackage() { return Package::forName(substr($this->name, 0, strrpos($this->name, '.'))); } /** * Creates a new instance of the class represented by this Class object. * The class is instantiated as if by a new expression with an empty argument list. * * Example * ======= * * try { * $o= XPClass::forName($name)->newInstance(); * } catch (ClassNotFoundException $e) { * // handle it! * } * * * Example (passing arguments) * =========================== * * try { * $o= XPClass::forName('peer.Socket')->newInstance('localhost', 6100); * } catch (ClassNotFoundException $e) { * // handle it! * } * * * @param mixed* args * @return lang.Object * @throws lang.IllegalAccessException in case this class cannot be instantiated */ public function newInstance() { if ($this->_reflect->isInterface()) { throw new IllegalAccessException('Cannot instantiate interfaces'); } else if ($this->_reflect->isAbstract()) { throw new IllegalAccessException('Cannot instantiate abstract classes'); } try { if (!$this->hasConstructor()) return $this->_reflect->newInstance(); $args= func_get_args(); return $this->_reflect->newInstanceArgs($args); } catch (ReflectionException $e) { throw new IllegalAccessException($e->getMessage()); } } /** * Gets class methods for this class * * @return lang.reflect.Method[] */ public function getMethods() { $list= array(); foreach ($this->_reflect->getMethods() as $m) { if (0 == strncmp('__', $m->getName(), 2)) continue; $list[]= new Method($this->_class, $m); } return $list; } /** * Gets a method by a specified name. Returns NULL if the specified * method does not exist. * * @param string name * @return lang.reflect.Method * @see xp://lang.reflect.Method */ public function getMethod($name) { if ($this->hasMethod($name)) { return new Method($this->_class, $this->_reflect->getMethod($name)); } return NULL; } /** * Checks whether this class has a method named "$method" or not. * * Note * ==== * Since in PHP, methods are case-insensitive, calling hasMethod('toString') * will provide the same result as hasMethod('tostring') * * @param string method the method's name * @return bool TRUE if method exists */ public function hasMethod($method) { return ((0 === strncmp('__', $method, 2)) ? FALSE : $this->_reflect->hasMethod($method) ); } /** * Retrieve if a constructor exists * * @return bool */ public function hasConstructor() { return $this->_reflect->hasMethod('__construct'); } /** * Retrieves this class' constructor. Returns NULL if no constructor * exists. * * @return lang.reflect.Constructor * @see xp://lang.reflect.Constructor */ public function getConstructor() { if ($this->hasConstructor()) { return new Constructor($this->_class, $this->_reflect->getMethod('__construct')); } return NULL; } /** * Retrieve a list of all member variables * * @return lang.reflect.Field[] array of field objects */ public function getFields() { $f= array(); foreach ($this->_reflect->getProperties() as $p) { if ('__id' === $p->getName()) continue; $f[]= new Field($this->_class, $p); } return $f; } /** * Retrieve a field by a specified name. Returns NULL if the specified * field does not exist * * @param string name * @return lang.reflect.Field */ public function getField($name) { if (!$this->hasField($name)) return NULL; return new Field($this->_class, $this->_reflect->getProperty($name)); } /** * Checks whether this class has a field named "$field" or not. * * @param string field the fields's name * @return bool TRUE if field exists */ public function hasField($field) { return '__id' == $field ? FALSE : $this->_reflect->hasProperty($field); } /** * Retrieve the parent class's class object. Returns NULL if there * is no parent class. * * @return lang.XPClass class object */ public function getParentclass() { return ($parent= $this->_reflect->getParentClass()) ? new self($parent) : NULL; } /** * Cast a given object to the class represented by this object * * @param lang.Generic expression * @return lang.Generic the given expression * @throws lang.ClassCastException */ public function cast(Generic $expression= NULL) { if (NULL === $expression) { return xp::null(); } else if (is($this->name, $expression)) { return $expression; } raise('lang.ClassCastException', 'Cannot cast '.xp::typeOf($expression).' to '.$this->name); } /** * Tests whether this class is a subclass of a specified class. * * @param string name class name * @return bool */ public function isSubclassOf($name) { if ($name == $this->name) return FALSE; // Catch bordercase (ZE bug?) return $this->_reflect->isSubclassOf(XPClass::forName($name)->_reflect); } /** * Determines whether the specified object is an instance of this * class. This is the equivalent of the is() core functionality. * * Examples * ======== * * uses('io.File', 'io.TempFile'); * $class= XPClass::forName('io.File'); * * var_dump($class->isInstance(new TempFile())); // TRUE * var_dump($class->isInstance(new File())); // TRUE * var_dump($class->isInstance(new Object())); // FALSE * * * @param lang.Object obj * @return bool */ public function isInstance($obj) { return is($this->name, $obj); } /** * Determines if this XPClass object represents an interface type. * * @return bool */ public function isInterface() { return $this->_reflect->isInterface(); } /** * Determines if this XPClass object represents an interface type. * * @return bool */ public function isEnum() { return $this->_reflect->isSubclassOf(xp::reflect('lang.Enum')); } /** * Retrieve interfaces this class implements * * @return lang.XPClass[] */ public function getInterfaces() { $r= array(); foreach ($this->_reflect->getInterfaces() as $iface) { $r[]= new self($iface->getName()); } return $r; } /** * Retrieves the api doc comment for this class. Returns NULL if * no documentation is present. * * @return string */ public function getComment() { if (!($details= self::detailsForClass($this->name))) return NULL; return $details['class'][DETAIL_COMMENT]; } /** * Retrieves this class' modifiers * * @see xp://lang.reflect.Modifiers * @return int */ public function getModifiers() { $r= MODIFIER_PUBLIC; // Map PHP reflection modifiers to generic form $m= $this->_reflect->getModifiers(); $m & ReflectionClass::IS_EXPLICIT_ABSTRACT && $r |= MODIFIER_ABSTRACT; $m & ReflectionClass::IS_IMPLICIT_ABSTRACT && $r |= MODIFIER_ABSTRACT; $m & ReflectionClass::IS_FINAL && $r |= MODIFIER_FINAL; return $r; } /** * Check whether an annotation exists * * @param string name * @param string key default NULL * @return bool */ public function hasAnnotation($name, $key= NULL) { $details= self::detailsForClass($this->name); return $details && ($key ? array_key_exists($key, @$details['class'][DETAIL_ANNOTATIONS][$name]) : array_key_exists($name, @$details['class'][DETAIL_ANNOTATIONS]) ); } /** * Retrieve annotation by name * * @param string name * @param string key default NULL * @return mixed * @throws lang.ElementNotFoundException */ public function getAnnotation($name, $key= NULL) { $details= self::detailsForClass($this->name); if (!$details || !($key ? array_key_exists($key, @$details['class'][DETAIL_ANNOTATIONS][$name]) : array_key_exists($name, @$details['class'][DETAIL_ANNOTATIONS]) )) return raise( 'lang.ElementNotFoundException', 'Annotation "'.$name.($key ? '.'.$key : '').'" does not exist' ); return ($key ? $details['class'][DETAIL_ANNOTATIONS][$name][$key] : $details['class'][DETAIL_ANNOTATIONS][$name] ); } /** * Retrieve whether a method has annotations * * @return bool */ public function hasAnnotations() { $details= self::detailsForClass($this->name); return $details ? !empty($details['class'][DETAIL_ANNOTATIONS]) : FALSE; } /** * Retrieve all of a method's annotations * * @return array annotations */ public function getAnnotations() { $details= self::detailsForClass($this->name); return $details ? $details['class'][DETAIL_ANNOTATIONS] : array(); } /** * Retrieve the class loader a class was loaded with * * @return lang.IClassLoader */ public function getClassLoader() { return self::_classLoaderFor($this->name); } /** * Fetch a class' classloader by its name * * @param string name fqcn of class * @return lang.IClassLoader */ protected static function _classLoaderFor($name) { sscanf(xp::$registry['classloader.'.$name], '%[^:]://%[^$]', $cl, $argument); return call_user_func(array(xp::reflect($cl), 'instanceFor'), $argument); } /** * Retrieve details for a specified class. Note: Results from this * method are cached! * * @param string class fully qualified class name * @return array or NULL to indicate no details are available */ public static function detailsForClass($class) { static $details= array(); if (!$class) return NULL; // Border case if (isset($details[$class])) return $details[$class]; // Retrieve class' sourcecode if (!($bytes= self::_classLoaderFor($class)->loadClassBytes($class))) return NULL; $details[$class]= array(array(), array()); $annotations= array(); $comment= NULL; $members= TRUE; $tokens= token_get_all($bytes); for ($i= 0, $s= sizeof($tokens); $i < $s; $i++) { switch ($tokens[$i][0]) { case T_DOC_COMMENT: $comment= $tokens[$i][1]; break; case T_COMMENT: // Annotations if (strncmp('#[@', $tokens[$i][1], 3) == 0) { $annotations[0]= substr($tokens[$i][1], 2); } else if (strncmp('#', $tokens[$i][1], 1) == 0) { $annotations[0].= substr($tokens[$i][1], 1); } // End of annotations if (']' == substr(rtrim($tokens[$i][1]), -1)) { $annotations= eval('return array('.preg_replace( array('/@([a-z_]+),/i', '/@([a-z_]+)\(\'([^\']+)\'\)/i', '/@([a-z_]+)\(/i', '/([^a-z_@])([a-z_]+) *= */i'), array('\'$1\' => NULL,', '\'$1\' => \'$2\'', '\'$1\' => array(', '$1\'$2\' => '), trim($annotations[0], "[]# \t\n\r").',' ).');'); } break; case T_CLASS: case T_INTERFACE: $details[$class]['class']= array( DETAIL_COMMENT => trim(preg_replace('/\n \* ?/', "\n", "\n".substr( $comment, 4, // "/**\n" strpos($comment, '* @')- 2 // position of first details token ))), DETAIL_ANNOTATIONS => $annotations ); $annotations= array(); $comment= NULL; break; case T_VARIABLE: if (!$members) break; // Have a member variable $name= substr($tokens[$i][1], 1); $details[$class][0][$name]= array( DETAIL_ANNOTATIONS => $annotations ); $annotations= array(); break; case T_FUNCTION: $members= FALSE; while (T_STRING !== $tokens[$i][0]) $i++; $m= $tokens[$i][1]; $details[$class][1][$m]= array( DETAIL_ARGUMENTS => array(), DETAIL_RETURNS => 'void', DETAIL_THROWS => array(), DETAIL_COMMENT => trim(preg_replace('/\n \* ?/', "\n", "\n".substr( $comment, 4, // "/**\n" strpos($comment, '* @')- 2 // position of first details token ))), DETAIL_ANNOTATIONS => $annotations, DETAIL_NAME => $tokens[$i][1] ); $matches= NULL; preg_match_all( '/@([a-z]+)\s*([^<\r\n]+<[^>]+>|[^\r\n ]+) ?([^\r\n ]+)?/', $comment, $matches, PREG_SET_ORDER ); $annotations= array(); $comment= NULL; foreach ($matches as $match) { switch ($match[1]) { case 'param': $details[$class][1][$m][DETAIL_ARGUMENTS][]= $match[2]; break; case 'return': $details[$class][1][$m][DETAIL_RETURNS]= $match[2]; break; case 'throws': $details[$class][1][$m][DETAIL_THROWS][]= $match[2]; break; } } break; default: // Empty } } // Return details for specified class return $details[$class]; } /** * Retrieve details for a specified class and method. Note: Results * from this method are cached! * * @param string class unqualified class name * @param string method * @return array */ public static function detailsForMethod($class, $method) { while ($details= self::detailsForClass(xp::nameOf($class))) { if (isset($details[1][$method])) return $details[1][$method]; $class= get_parent_class($class); } return NULL; } /** * Retrieve details for a specified class and field. Note: Results * from this method are cached! * * @param string class unqualified class name * @param string method * @return array */ public static function detailsForField($class, $field) { while ($details= self::detailsForClass(xp::nameOf($class))) { if (isset($details[0][$field])) return $details[0][$field]; $class= get_parent_class($class); } return NULL; } /** * Returns the XPClass object associated with the class with the given * string name. Uses the default classloader if none is specified. * * @param string name - e.g. "io.File", "rdbms.mysql.MySQL" * @param lang.IClassLoader classloader default NULL * @return lang.XPClass class object * @throws lang.ClassNotFoundException when there is no such class */ public static function forName($name, IClassLoader $classloader= NULL) { if (NULL === $classloader) { $classloader= ClassLoader::getDefault(); } return $classloader->loadClass($name); } /** * Returns an array containing class objects representing all the * public classes * * @return lang.XPClass[] class objects */ public static function getClasses() { $ret= array(); foreach (get_declared_classes() as $name) { if (xp::registry('class.'.$name)) $ret[]= new self($name); } return $ret; } } ?> _instance)) { try { $this->_instance= $this->initialize(); } catch (Throwable $e) { $this->_instance= NULL; throw(new DeferredInitializationException($method, $e)); } } return call_user_func_array(array($this->_instance, $method), $args); } } ?> setPoweredBy($poweredBy); } /** * Set the power * * @param int p power * @throws lang.IllegalArgumentException in case the parameter p contains an illegal value */ public function setPoweredBy($p) { if (!($x= log10($p / 6.1)) || (floor($x) != $x)) { throw(new IllegalArgumentException($p.' not allowed')); } $this->poweredBy= $p; } /** * Retrieve the power * * @return int power */ public function getPoweredBy() { return $this->poweredBy; } /** * Retrieve string representation of this object * * @return string */ public function toString() { return $this->getClassName().'('.$this->poweredBy.')'; } /** * Returns whether another object is equal to this object. * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $this->poweredBy == $cmp->poweredBy; } /** * Retrieve header suited for HTTP/Mail * * Example: *
         *   X-Binford: 6100 (more power)
         * 
    * * @return peer.Header */ public function getHeader() { return new Header('X-Binford', $this->poweredBy.' (more power)'); } } ?> getTimeZone(); $date= Date::create($year->getYear(), 1, 1, 0, 0, 0); break; } default: { switch ($method) { default: case CAL_DST_EU: $tz= new TimeZone('Europe/Berlin'); break; case CAL_DST_US: $tz= new TimeZone('America/New_York'); break; } $date= ($year == -1 ? Date::now() : Date::create($year, 1, 1, 0, 0, 0) ); break; } } $transition= TimeZoneTransition::nextTransition($tz, $date); while (!$transition->isDst()) { $transition->next(); } return $transition->getDate(); } /** * Calculates end of DST (daylight savings time) * This is the last Sunday of October * * @param int year default -1 Year, defaults to current year * @return util.Date */ public static function dstEnd($year= -1) { $date= ($year == -1 ? Date::now() : Date::create($year, 1, 1, 0, 0, 0) ); $transition= TimeZoneTransition::nextTransition(new TimeZone('Europe/Berlin'), $date); while ($transition->isDst()) { $transition->next(); } return $transition->getDate(); } /** * Retrieve whether a given date object is in daylight savings time. * * @param util.Date date * @param int method default CAL_DST_EU Method to calculate (CAL_DST_EU|CAL_DST_US) * @return bool */ public static function inDst(Date $date) { return (bool)$date->toString('I'); } /** * Calculates the amount of workdays between to dates. Workdays are * defined as Monday through Friday. * * This method takes an optional argument, an array of the following * form: * * * $holidays[gmmktime(...)]= TRUE; * * * @param util.Date start * @param util.Date end * @param array holidays default array() holidays to be included in calculation * @return int number of workdays */ public static function workdays($start, $end, $holidays= array()) { $s= $start->getTime(); $e= $end->getTime(); // For holidays, we have to compare to midnight // else, don't calculate this if (!empty($holidays)) $s-= $s % CAL_SEC_DAY; // Is there a more intelligent way of doing this? $diff= floor(($e - $s) / CAL_SEC_DAY); for ($i= $s; $i <= $e; $i+= CAL_SEC_DAY) { $diff-= ((date('w', $i)+ 6) % 7 > 4 || isset($holidays[$i])); } return $diff+ 1; } /** * Return midnight of a given date * * @param util.Date date * @return util.Date */ public static function midnight($date) { return Date::create( $date->getYear(), $date->getMonth(), $date->getDay(), 0, 0, 0, $date->getTimeZone() ); } /** * Return beginning of month for a given date. E.g., given a date * 2003-06-08, the function will return 2003-06-01 00:00:00. * * @param util.Date date * @return util.Date */ public static function monthBegin($date) { return Date::create( $date->getYear(), $date->getMonth(), 1, 0, 0, 0, $date->getTimeZone() ); } /** * Return end of month for a given date. E.g., given a date * 2003-06-08, the function will return 2003-06-30 23:59:59. * * @param util.Date date * @return util.Date */ public static function monthEnd($date) { return Date::create( $date->getYear(), $date->getMonth() + 1, 0, 23, 59, 59, $date->getTimeZone() ); } /** * Helper method for Calendar::week * * @param int stamp * @param int year * @return int */ protected static function caldiff($stamp, $year) { $d4= mktime(0, 0, 0, 1, 4, $year); return floor(1.05 + ($stamp- $d4) / CAL_SEC_WEEK+ ((date('w', $d4)+ 6) % 7) / 7); } /** * Returns calendar week for a day * * @param util.Date date * @return int calendar week * @see http://www.salesianer.de/util/kalwoch.html */ public static function week($date) { $d= $date->getTime(); $y= $date->getYear() + 1; do { $w= Calendar::caldiff($d, $y); $y--; } while ($w < 1); return (int)$w; } /** * Get first of advent for given year * * @param int year default -1 year, defaults to this year * @return util.Date for date of the first of advent * @see http://www.salesianer.de/util/kalfaq.html */ public static function advent($year= -1) { if (-1 == $year) $year= date('Y'); $s= mktime(0, 0, 0, 11, 26, $year); while (0 != date('w', $s)) { $s+= CAL_SEC_DAY; } return new Date($s); } /** * Get easter date for given year * * @param int year default -1 Year, defaults to this year * @return util.Date date for Easter date * @see http://www.koenigsmuenster.de/rsk/epakte.htm * @see http://www.salesianer.de/util/kalfaq.html * @see php://easter-date#user_contrib */ public static function easter($year= -1) { if (-1 == $year) $year= date('Y'); $g = $year % 19; $c = (int)($year / 100); $h = (int)($c - ($c / 4) - ((8* $c + 13) / 25) + 19 * $g + 15) % 30; $i = (int)$h - (int)($h / 28) * (1 - (int)($h / 28)* (int)(29 / ($h+ 1)) * ((int)(21 - $g) / 11)); $j = ($year + (int)($year / 4) + $i + 2 - $c + (int)($c / 4)) % 7; $l = $i - $j; $m = 3 + (int)(($l + 40) / 44); $d = $l + 28 - 31 * ((int)($m / 4)); return Date::create($year, $m, $d, 0, 0, 0); } /** * Returns whether a year is a leap year * * @param int year * @return bool TRUE if the given year is a leap year */ public static function isLeapYear($year) { return $year % 400 == 0 || ($year > 1582 && $year % 100 == 0 ? FALSE : $year % 4 == 0); } } ?> * uses('util.ChainedException'); * * throw new ChainedException(...); * * The above will acutally throw a lang.ChainedException! * * @purpose BC * @see xp://lang.ChainedException * @deprecated Use lang.ChainedException instead */ class utilChainedException extends XPException { public $cause = NULL; /** * Constructor * * @param string message * @param lang.Throwable cause */ public function __construct($message, $cause) { parent::__construct($message); $this->cause= $cause; } /** * Set cause * * @param lang.Throwable cause */ public function setCause($cause) { $this->cause= $cause; } /** * Get cause * * @return lang.Throwable */ public function getCause() { return $this->cause; } /** * Return string representation of this exception * * @return string */ public function toString() { $s= $this->compoundMessage()."\n"; $t= sizeof($this->trace); for ($i= 0; $i < $t; $i++) { $s.= $this->trace[$i]->toString(); } if (!$this->cause) return $s; $loop= $this->cause; while ($loop) { // String of cause $s.= 'Caused by '.$loop->compoundMessage()."\n"; // Find common stack trace elements for ($ct= $cc= sizeof($loop->trace)- 1, $t= sizeof($this->trace)- 1; $ct > 0, $t > 0; $cc--, $t--) { if (!$loop->trace[$cc]->equals($this->trace[$t])) break; } // Output uncommon elements only and one line how many common elements exist! for ($i= 0; $i < $cc; $i++) { $s.= xp::stringOf($loop->trace[$i]); } if ($cc != $ct) $s.= ' ... '.($ct - $cc + 1)." more\n"; $loop= $loop instanceof ChainedException ? $loop->cause : NULL; } return $s; } } ?> * uses('util.cmd.Console'); * * Console::writeLine('Hello ', 'a', 'b', 1); // Hello ab1 * Console::writeLinef('Hello %s', 'World'); // Hello World * * Console::$out->write('.'); * * * Example: Writing to standard error * * uses('util.cmd.Console'); * * Console::$err->writeLine('*** An error occured: ', $e->toString()); * * * @see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemConsoleClassTopic.asp * @purpose I/O functions */ class Console extends Object { public static $out= NULL, $err= NULL; static function __static() { self::$out= new StringWriter(new ConsoleOutputStream(STDOUT)); self::$err= new StringWriter(new ConsoleOutputStream(STDERR)); } /** * Flush output buffer * */ public static function flush() { self::$out->flush(); } /** * Write a string to standard output * * @param mixed* args */ public static function write() { $a= func_get_args(); call_user_func_array(array(self::$out, 'write'), $a); } /** * Write a string to standard output and append a newline * * @param mixed* args */ public static function writeLine() { $a= func_get_args(); call_user_func_array(array(self::$out, 'writeLine'), $a); } /** * Write a formatted string to standard output * * @param string format * @param mixed* args * @see php://printf */ public static function writef() { $a= func_get_args(); call_user_func_array(array(self::$out, 'writef'), $a); } /** * Write a formatted string to standard output and append a newline * * @param string format * @param mixed* args */ public static function writeLinef() { $a= func_get_args(); call_user_func_array(array(self::$out, 'writeLinef'), $a); } /** * Read a line from standard input. The line ending (\r and/or \n) * is trimmed off the end. * * @param string prompt = NULL * @return string */ public function readLine($prompt= NULL) { $prompt && self::$out->write($prompt.' '); $r= ''; while ($bytes= fgets(STDIN, 0x20)) { $r.= $bytes; if (FALSE !== strpos("\r\n", substr($r, -1))) break; } return rtrim($r, "\r\n"); } /** * Read a single character from standard input. * * @param string prompt = NULL * @return string */ public function read($prompt= NULL) { $prompt && self::$out->write($prompt.' '); return fgetc(STDIN); } } ?> * uses('util.cmd.Command', 'peer.http.HttpConnection'); * * class Head extends Command { * protected * $conn = NULL, * $verbose = FALSE; * * #[@arg(position= 0)] * public function setUrl($url) { * $this->conn= new HttpConnection($url); * } * * #[@arg] * public function setVerbose() { * $this->verbose= TRUE; * } * * public function run() { * $this->verbose && $this->out->writeLine('Opening connection to ', $this->conn); * $this->out->writeLine($this->conn->head()->toString()); * } * } * * * This can be run with the "xpcli" utility as follows: *
     *   $ xpcli Head http://de3.php.net/ -v
     * 
    * * @see http://news.xp-framework.net/article/205/2007/07/22/ * @purpose CLI */ package util.cmd { } setParams(NULL === $list ? $_SERVER['argv'] : $list); } /** * Set the parameter string * * @param array params */ public function setParams($params) { $this->list= $params; $this->list[-1]= @$_SERVER['_']; $this->count= sizeof($params); $this->string= implode(' ', $params); } /** * Private helper function that iterates through the parameter array * * @param string long long parameter (w/o --) * @param string short default NULL Short parameter (w/o -), defaults to the first char of the long param * @return mixed position on which the parameter is placed or FALSE if nonexistant */ protected function _find($long, $short= NULL) { if (is_null($short)) $short= $long{0}; foreach (array_keys($this->list) as $i) { // Short notation (e.g. -f value) if ($this->list[$i] == '-'.$short) return $i+ 1; // Long notation (e.g. --help, without a value) if ($this->list[$i] == '--'.$long) return $i; // Long notation (e.g. --file=*.txt) if (substr($this->list[$i], 0, strlen($long)+ 3) == '--'.$long.'=') return $i; } return FALSE; } /** * Checks whether a parameter is set * * @see xp://util.Properties#value * @param string long long parameter (w/o --) * @param string short default NULL Short parameter (w/o -), defaults to the first char of the long param * @return boolean */ public function exists($long, $short= NULL) { if (is_int($long)) return isset($this->list[$long]); return ($this->_find($long, $short) !== FALSE); } /** * Retrieve the value of a given parameter * * Examples: * * $p= &new ParamString(); * if ($p->exists('help', '?')) { * printf("Usage: %s %s --force-check [--pattern={pattern}]\n", $p->value(-1), $p->value(0)); * exit(-2); * } * * $force= $p->exists('force-check', 'f'); * $pattern= $p->value('pattern', 'p', '.*'); * * // ... * * * @param string long long parameter (w/o --) * @param string short default NULL Short parameter (w/o -), defaults to the first char of the long param * @param string default default NULL A default value if parameter does not exist * @return string * @throws lang.IllegalArgumentException if parameter does not exist and no default value was supplied. */ public function value($long, $short= NULL, $default= NULL) { if (is_int($long)) { if (NULL === $default && !isset($this->list[$long])) { throw new IllegalArgumentException ('Parameter #'.$long.' does not exist'); } return isset($this->list[$long]) ? $this->list[$long] : $default; } $pos= $this->_find($long, $short); if (FALSE === $pos && NULL === $default) { throw new IllegalArgumentException ('Parameter --'.$long.' does not exist'); } // Default usage (eg.: '--with-foo=bar') $length= strlen($long)+ 2; if ($pos !== FALSE && isset($this->list[$pos]) && strncmp('--'.$long, $this->list[$pos], $length) == 0) { // Usage with value (eg.: '--with-foo=bar') if (strlen($this->list[$pos]) > $length && '=' === $this->list[$pos]{$length}) { return substr($this->list[$pos], $length + 1); // Return string after `--{option}=` } // Usage as switch (eg.: '--enable-foo') return NULL; } // Usage in short (eg.: '-v' or '-f /foo/bar') // If the found element is a new parameter, the searched one is used as // flag, so just return TRUE, otherwise return the value. if ($pos !== FALSE && (!isset($this->list[$pos]) || '-' === $this->list[$pos]{0})) { return $default; } return ($pos !== FALSE ? $this->list[$pos] : $default); } } ?> * $ xpcli [options] fully.qualified.class.Name [classoptions] * * * Options includes one of the following: *
       * -c:
       *   Set the path with which the PropertyManager is configured with. The
       *   PropertyManager is used for dependency injection. If a file called
       *   log.ini exists in this path, the Logger will be configured with. If
       *   a database.ini is present there, the ConnectionManager will be
       *   configured with it.
       * 
       * -cp:
       *   Add the path value to the class path.
       * 
    * * @test xp://net.xp_framework.unittest.util.cmd.RunnerTest * @see xp://util.cmd.Command * @purpose Runner */ class Runner extends Object { private static $out = NULL, $err = NULL; static function __static() { self::$out= new StringWriter(new ConsoleOutputStream(STDOUT)); self::$err= new StringWriter(new ConsoleOutputStream(STDERR)); } /** * Converts api-doc "markup" to plain text w/ ASCII "art" * * @param string markup * @return string text */ protected static function textOf($markup) { $line= str_repeat('=', 72); return strip_tags(preg_replace(array( '#
    #', '#
    #', '#
  • #', ), array( $line, $line, '* ', ), trim($markup))); } /** * Show usage * * @param lang.XPClass class */ public static function showUsage(XPClass $class) { // Description if (NULL !== ($comment= $class->getComment())) { self::$err->writeLine(self::textOf($comment)); self::$err->writeLine(str_repeat('=', 72)); } $extra= $details= $positional= array(); foreach ($class->getMethods() as $method) { if (!$method->hasAnnotation('arg')) continue; $arg= $method->getAnnotation('arg'); $name= strtolower(preg_replace('/^set/', '', $method->getName()));; $comment= self::textOf($method->getComment()); if (0 == $method->numParameters()) { $optional= TRUE; } else { list($first, )= $method->getParameters(); $optional= $first->isOptional(); } if (isset($arg['position'])) { $details['#'.($arg['position'] + 1)]= $comment; $positional[$arg['position']]= $name; } else if (isset($arg['name'])) { $details['--'.$arg['name'].' | -'.(isset($arg['short']) ? $arg['short'] : $arg['name']{0})]= $comment; $extra[$arg['name']]= $optional; } else { $details['--'.$name.' | -'.(isset($arg['short']) ? $arg['short'] : $name{0})]= $comment; $extra[$name]= $optional; } } // Usage asort($positional); self::$err->write('Usage: $ xpcli ', $class->getName(), ' '); foreach ($positional as $name) { self::$err->write('<', $name, '> '); } foreach ($extra as $name => $optional) { self::$err->write(($optional ? '[' : ''), '--', $name, ($optional ? '] ' : ' ')); } self::$err->writeLine(); // Argument details self::$err->writeLine('Arguments:'); foreach ($details as $which => $comment) { self::$err->writeLine('* ', $which, "\n ", $comment, "\n"); } } /** * Main method * * @param string[] args * @return int */ public static function main(array $args) { return create(new self())->run(new ParamString($args)); } /** * Reassigns standard output stream * * @param io.streams.OutputStream out * @return io.streams.OutputStream the given output stream */ public function setOut(OutputStream $out) { self::$out= new StringWriter($out); return $out; } /** * Reassigns standard error stream * * @param io.streams.OutputStream error * @return io.streams.OutputStream the given output stream */ public function setErr(OutputStream $err) { self::$err= new StringWriter($err); return $err; } /** * Main method * * @param util.cmd.ParamString params * @return int */ public function run(ParamString $params) { $pm= PropertyManager::getInstance(); // No arguments given - show our own usage if ($params->count < 1) { self::$err->writeLine(self::textOf(XPClass::forName(xp::nameOf(__CLASS__))->getComment())); return 1; } // Separate runner options from class options for ($offset= 0, $i= 0; $i < $params->count; $i++) { if ('-c' === $params->list[$i]) { $pm->configure($params->list[$i+ 1]); $offset+= 2; $i++; } else if ('-cp' === $params->list[$i]) { foreach (explode(PATH_SEPARATOR, $params->list[$i+ 1]) as $element) { ClassLoader::registerPath($element, FALSE); } $offset+= 2; $i++; } else { break; } } // Sanity check if (!$params->exists($offset)) { self::$err->writeLine('*** Missing classname'); return 1; } unset($params->list[-1]); $classname= $params->value($offset); $classparams= new ParamString(array_slice($params->list, $offset+ 1)); // Class file or class name if (strstr($classname, xp::CLASS_FILE_EXT)) { $file= new File($classname); if (!$file->exists()) { self::$err->writeLine('*** Cannot load class from non-existant file ', $classname); return 1; } $uri= $file->getURI(); $path= dirname($uri); $paths= array_flip(array_map('realpath', xp::$registry['classpath'])); $class= NULL; while (FALSE !== ($pos= strrpos($path, DIRECTORY_SEPARATOR))) { if (isset($paths[$path])) { $class= XPClass::forName(strtr(substr($uri, strlen($path)+ 1, -10), DIRECTORY_SEPARATOR, '.')); break; } $path= substr($path, 0, $pos); } if (!$class) { self::$err->writeLine('*** Cannot load class from ', $file); return 1; } } else { try { $class= XPClass::forName($classname); } catch (ClassNotFoundException $e) { self::$err->writeLine('*** ', $e->getMessage()); return 1; } } // Check whether class is runnable if (!$class->isSubclassOf('lang.Runnable')) { self::$err->writeLine('*** ', $class->getName(), ' is not runnable'); return 1; } // Usage if ($classparams->exists('help', '?')) { self::showUsage($class); return 0; } // Load, instantiate and initialize $l= Logger::getInstance(); $pm->hasProperties('log') && $l->configure($pm->getProperties('log')); $cm= ConnectionManager::getInstance(); $pm->hasProperties('database') && $cm->configure($pm->getProperties('database')); $instance= $class->newInstance(); $instance->out= self::$out; $instance->err= self::$err; foreach ($class->getMethods() as $method) { if ($method->hasAnnotation('inject')) { // Perform injection $inject= $method->getAnnotation('inject'); switch ($inject['type']) { case 'rdbms.DBConnection': { $args= array($cm->getByHost($inject['name'], 0)); break; } case 'util.Properties': { $args= array($pm->getProperties($inject['name'])); break; } case 'util.log.LogCategory': { $args= array($l->getCategory($inject['name'])); break; } default: { self::$err->writeLine('*** Unknown injection type "'.$inject['type'].'"'); return 2; } } try { $method->invoke($instance, $args); } catch (Throwable $e) { self::$err->writeLine('*** Error injecting '.$inject['name'].': '.$e->getMessage()); return 2; } } else if ($method->hasAnnotation('args')) { // Pass all arguments if (!$method->hasAnnotation('args', 'select')) { $pass= array_slice($classparams->list, 0, $classparams->count); } else { $pass= array(); foreach (preg_split('/, ?/', $method->getAnnotation('args', 'select')) as $def) { if (is_numeric($def) || '-' == $def{0}) { $pass[]= $classparams->value((int)$def); } else { sscanf($def, '[%d..%d]', $begin, $end); isset($begin) || $begin= 0; isset($end) || $end= $classparams->count- 1; while ($begin <= $end) { $pass[]= $classparams->value($begin++); } } } } try { $method->invoke($instance, array($pass)); } catch (Throwable $e) { self::$err->writeLine('*** Error for arguments '.$begin.'..'.$end.': '.$e->getMessage()); return 2; } } else if ($method->hasAnnotation('arg')) { // Pass arguments $arg= $method->getAnnotation('arg'); if (isset($arg['position'])) { $name= '#'.($arg['position']+ 1); $select= intval($arg['position']); $short= NULL; } else if (isset($arg['name'])) { $name= $select= $arg['name']; $short= isset($arg['short']) ? $arg['short'] : NULL; } else { $name= $select= strtolower(preg_replace('/^set/', '', $method->getName())); $short= isset($arg['short']) ? $arg['short'] : NULL; } if (0 == $method->numParameters()) { if (!$classparams->exists($select, $short)) continue; $args= array(); } else if (!$classparams->exists($select, $short)) { list($first, )= $method->getParameters(); if (!$first->isOptional()) { self::$err->writeLine('*** Argument '.$name.' does not exist!'); return 2; } $args= array(); } else { $args= array($classparams->value($select, $short)); } try { $method->invoke($instance, $args); } catch (TargetInvocationException $e) { self::$err->writeLine('*** Error for argument '.$name.': '.$e->getCause()->compoundMessage()); return 2; } } } try { $instance->run(); } catch (Throwable $t) { self::$err->writeLine('*** ', $t->compoundMessage()); return 70; // EX_SOFTWARE according to sysexits.h } return 0; } } ?> * $sp= &new SingleProcess(); * if (!$sp->lock()) { * exit(-1); * } * * // [...operation which should only take part once at a time...] * * $sp->unlock(); * * * @purpose Lock process so it can only be run once */ class SingleProcess extends Object { public $lockfile = NULL; /** * Constructor * * @param string lockfileName default NULL the lockfile's name, * defaulting to <>.lck */ public function __construct($lockFileName= NULL) { if (NULL === $lockFileName) $lockFileName= $_SERVER['argv'][0].'.lck'; $this->lockfile= new File($lockFileName); } /** * Lock this application * * @return bool success */ public function lock() { try { $this->lockfile->open(FILE_MODE_WRITE); $this->lockfile->lockExclusive(); } catch (IOException $e) { $this->lockfile->close(); return FALSE; } return TRUE; } /** * Unlock the application * * @return bool Success */ public function unlock() { if ($this->lockfile->unlock()) { $this->lockfile->close(); $this->lockfile->unlink(); return TRUE; } return FALSE; } } ?> = 8; $len-= 8) { $hash= (($hash << 5) + $hash) + ord($str{$offset++}); $hash= (($hash << 5) + $hash) + ord($str{$offset++}); $hash= (($hash << 5) + $hash) + ord($str{$offset++}); $hash= (($hash << 5) + $hash) + ord($str{$offset++}); $hash= (($hash << 5) + $hash) + ord($str{$offset++}); $hash= (($hash << 5) + $hash) + ord($str{$offset++}); $hash= (($hash << 5) + $hash) + ord($str{$offset++}); $hash= (($hash << 5) + $hash) + ord($str{$offset++}); } switch ($len) { case 7: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); case 6: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); case 5: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); case 4: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); case 3: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); case 2: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); case 1: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); } return $hash; } } ?> * $hashCode= HashProvider::hashOf($string); * * * Uses MD5 as default hashing implementation. To change the hashing * implementation to be used, use the following: * * HashProvider::getInstance()->setImplementation(new MyHashImplementation()); * * * @see xp://util.collections.DJBX33AHashImplementation * @see xp://util.collections.MD5HashImplementation * @see xp://util.collections.Map * @purpose Hashing */ class HashProvider extends Object { protected static $instance = NULL; public $impl = NULL; static function __static() { self::$instance= new self(); self::$instance->setImplementation(new MD5HashImplementation()); } /** * Constructor * */ protected function __construct() { } /** * Retrieve sole instance of this object * * @return util.collections.HashProvider */ public static function getInstance() { return self::$instance; } /** * Returns hash for a given string * * @param string str * @return int */ public static function hashOf($str) { return self::getInstance()->impl->hashOf($str); } /** * Set hashing implementation * * @param util.collections.HashImplementation impl * @throws lang.IllegalArgumentException when impl is not a HashImplementation */ public function setImplementation(HashImplementation $impl) { $this->impl= $impl; } /** * Get hashing implementation * * @return util.collections.HashImplementation */ public function getImplementation() { return $this->impl; } } ?> v= $v; return $self; } public function current() { return current($this->v); } public function key() { return $this->i; } public function next() { next($this->v); $this->i++; } public function rewind() { reset($this->v); $this->i= 0; } public function valid() { return $this->i < sizeof($this->v); } }'); } /** * Returns an iterator for use in foreach() * * @see php://language.oop5.iterations * @return php.Iterator */ public function getIterator() { return self::$iterate->on($this->_elements); } /** * = list[] overloading * * @param int offset * @return lang.Generic */ public function offsetGet($offset) { throw new IllegalArgumentException('Unsupported operation'); } /** * list[]= overloading * * @param int offset * @param mixed value * @throws lang.IllegalArgumentException if key is neither numeric (set) nor NULL (add) */ public function offsetSet($offset, $value) { if (NULL === $offset) { $this->add($value); } else { throw new IllegalArgumentException('Unsupported operation'); } } /** * isset() overloading * * @param int offset * @return bool */ public function offsetExists($offset) { return $this->contains($offset); } /** * unset() overloading * * @param int offset */ public function offsetUnset($offset) { $this->remove($offset); } /** * Adds an object * * @param lang.Generic object * @return bool TRUE if this set did not already contain the specified element. */ public function add(Generic $object) { if ($this->__generic && !$object instanceof $this->__generic[0]) { throw new IllegalArgumentException('Object '.xp::stringOf($object).' must be of '.$this->__generic[0]); } $h= $object->hashCode(); if (isset($this->_elements[$h])) return FALSE; $this->_hash+= HashProvider::hashOf($h); $this->_elements[$h]= $object; return TRUE; } /** * Removes an object from this set * * @param lang.Generic object * @return bool TRUE if this set contained the specified element. */ public function remove(Generic $object) { if ($this->__generic && !$object instanceof $this->__generic[0]) { throw new IllegalArgumentException('Object '.xp::stringOf($object).' must be of '.$this->__generic[0]); } $h= $object->hashCode(); if (!isset($this->_elements[$h])) return FALSE; $this->_hash-= HashProvider::hashOf($h); unset($this->_elements[$h]); return TRUE; } /** * Removes an object from this set * * @param lang.Generic object * @return bool TRUE if the set contains the specified element. */ public function contains(Generic $object) { if ($this->__generic && !$object instanceof $this->__generic[0]) { throw new IllegalArgumentException('Object '.xp::stringOf($object).' must be of '.$this->__generic[0]); } return isset($this->_elements[$object->hashCode()]); } /** * Returns this set's size * * @return int */ public function size() { return sizeof($this->_elements); } /** * Removes all of the elements from this set * */ public function clear() { $this->_elements= array(); $this->_hash= 0; } /** * Returns whether this set is empty * * @return bool */ public function isEmpty() { return 0 == sizeof($this->_elements); } /** * Adds an array of objects * * @param lang.Generic[] objects * @return bool TRUE if this set changed as a result of the call. */ public function addAll($objects) { $result= FALSE; $hash= $this->_hash; $elements= $this->_elements; for ($i= 0, $s= sizeof($objects); $i < $s; $i++) { if ($this->__generic && !$objects[$i] instanceof $this->__generic[0]) { throw new IllegalArgumentException('Object #'.$i.' '.xp::stringOf($objects[$i]).' must be of '.$this->__generic[0]); } $h= $objects[$i]->hashCode(); if (isset($this->_elements[$h])) continue; $result= TRUE; $hash+= HashProvider::hashOf($h); $elements[$h]= $objects[$i]; } $this->_hash= $hash; $this->_elements= $elements; return $result; } /** * Returns an array containing all of the elements in this set. * * @return lang.Generic[] objects */ public function toArray() { return array_values($this->_elements); } /** * Returns a hashcode for this set * * @return string */ public function hashCode() { return $this->_hash; } /** * Returns true if this set equals another set. * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $this->hashCode() === $cmp->hashCode(); } /** * Returns a string representation of this set * * @return string */ public function toString() { $s= $this->getClassName().'['.sizeof($this->_elements).'] {'; if (0 == sizeof($this->_elements)) return $s.' }'; $s.= "\n"; foreach (array_keys($this->_elements) as $key) { $s.= ' '.$this->_elements[$key]->toString().",\n"; } return substr($s, 0, -2)."\n}"; } } ?> get($offset); } /** * list[]= overloading * * @param lang.Generic offset * @param lang.Generic value */ public function offsetSet($offset, $value) { $this->put($offset, $value); } /** * isset() overloading * * @param lang.Generic offset * @return bool */ public function offsetExists($offset) { return $this->containsKey($offset); } /** * unset() overloading * * @param lang.Generic offset */ public function offsetUnset($offset) { $this->remove($offset); } /** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for this key, the old * value is replaced by the specified value. * Returns previous value associated with specified key, or NULL if * there was no mapping for the specified key. * * @param lang.Generic key * @param lang.Generic value * @return lang.Generic the previous value associated with the key */ public function put($key, Generic $value) { $k= Primitive::boxed($key); if ($this->__generic) { if (!$k instanceof $this->__generic[0]) { throw new IllegalArgumentException('Key '.xp::stringOf($k).' must be of '.$this->__generic[0]); } else if (!$value instanceof $this->__generic[1]) { throw new IllegalArgumentException('Value '.xp::stringOf($value).' must be of '.$this->__generic[1]); } } $h= $k->hashCode(); if (!isset($this->_buckets[$h])) { $previous= NULL; } else { $previous= $this->_buckets[$h][1]; } $this->_buckets[$h]= array($k, $value); $this->_hash+= HashProvider::hashOf($h.$value->hashCode()); return $previous; } /** * Returns the value to which this map maps the specified key. * Returns NULL if the map contains no mapping for this key. * * @param lang.Generic key * @return lang.Generic the value associated with the key */ public function get($key) { $k= Primitive::boxed($key); if ($this->__generic) { if (!$k instanceof $this->__generic[0]) { throw new IllegalArgumentException('Key '.xp::stringOf($k).' must be of '.$this->__generic[0]); } } $h= $k->hashCode(); if (!isset($this->_buckets[$h])) return NULL; return $this->_buckets[$h][1]; } /** * Removes the mapping for this key from this map if it is present. * Returns the value to which the map previously associated the key, * or null if the map contained no mapping for this key. * * @param lang.Generic key * @return lang.Generic the previous value associated with the key */ public function remove($key) { $k= Primitive::boxed($key); if ($this->__generic) { if (!$k instanceof $this->__generic[0]) { throw new IllegalArgumentException('Key '.xp::stringOf($k).' must be of '.$this->__generic[0]); } } $h= $k->hashCode(); if (!isset($this->_buckets[$h])) { $previous= NULL; } else { $previous= $this->_buckets[$h][1]; $this->_hash-= HashProvider::hashOf($h.$previous->hashCode()); unset($this->_buckets[$h]); } return $previous; } /** * Removes all mappings from this map. * */ public function clear() { $this->_buckets= array(); $this->_hash= 0; } /** * Returns the number of key-value mappings in this map * */ public function size() { return sizeof($this->_buckets); } /** * Returns true if this map contains no key-value mappings. * */ public function isEmpty() { return empty($this->_buckets); } /** * Returns true if this map contains a mapping for the specified key. * * @param lang.Generic key * @return bool */ public function containsKey($key) { $k= Primitive::boxed($key); if ($this->__generic) { if (!$k instanceof $this->__generic[0]) { throw new IllegalArgumentException('Key '.xp::stringOf($k).' must be of '.$this->__generic[0]); } } return isset($this->_buckets[$k->hashCode()]); } /** * Returns true if this map maps one or more keys to the specified value. * * @param lang.Generic value * @return bool */ public function containsValue(Generic $value) { if ($this->__generic) { if (!$value instanceof $this->__generic[1]) { throw new IllegalArgumentException('Value '.xp::stringOf($value).' must be of '.$this->__generic[1]); } } foreach (array_keys($this->_buckets) as $key) { if ($this->_buckets[$key][1]->equals($value)) return TRUE; } return FALSE; } /** * Returns a hashcode for this map * * @return string */ public function hashCode() { return $this->_hash; } /** * Returns true if this map equals another map. * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return ( ($cmp instanceof Map) && ($this->hashCode() === $cmp->hashCode()) ); } /** * Returns an array of keys * * @return lang.Generic[] */ public function keys() { $keys= array(); foreach (array_keys($this->_buckets) as $key) { $keys[]= $this->_buckets[$key][0]; } return $keys; } /** * Returns a string representation of this map * * @return string */ public function toString() { $s= $this->getClassName().'['.sizeof($this->_buckets).'] {'; if (0 == sizeof($this->_buckets)) return $s.' }'; $s.= "\n"; foreach (array_keys($this->_buckets) as $key) { $s.= ' '.$this->_buckets[$key][0]->toString().' => '.$this->_buckets[$key][1]->toString().",\n"; } return substr($s, 0, -2)."\n}"; } } ?> setSize($size); } /** * Add an element to the buffer and return the id of the element * which has been deleted in exchange. Returns NULL for the case * that no element has been deleted (which is the case when the * buffer's size has not yet been exceeded). * * * $deleted= $buf->add($key); * * * @param lang.Generic element * @return lang.Generic victim */ public function add(Generic $element) { if ($this->__generic && !$element instanceof $this->__generic[0]) { throw new IllegalArgumentException('Element '.xp::stringOf($element).' must be of '.$this->__generic[0]); } $h= $element->hashCode(); $this->_access[$h]= microtime(TRUE); $this->_elements[$h]= $element; // Check if this buffer's size has been exceeded if (sizeof($this->_access) <= $this->size) return NULL; // Find the position of the smallest value and delete it $p= array_search(min($this->_access), $this->_access, TRUE); $victim= $this->_elements[$p]; unset($this->_access[$p]); unset($this->_elements[$p]); return $victim; } /** * Update an element * * @param lang.Generic element */ public function update(Generic $element) { if ($this->__generic && !$element instanceof $this->__generic[0]) { throw new IllegalArgumentException('Element '.xp::stringOf($element).' must be of '.$this->__generic[0]); } $this->_access[$element->hashCode()]= microtime(TRUE); } /** * Get number of elements currently contained in this buffer * * @return int */ public function numElements() { return sizeof($this->_access); } /** * Set size * * @param int size * @throws lang.IllegalArgumentException is size is not greater than zero */ public function setSize($size) { if ($size <= 0) throw new IllegalArgumentException( 'Size must be greater than zero, '.$size.' given' ); $this->size= $size; } /** * Get size * * @return int */ public function getSize() { return $this->size; } } ?> * $ht= new HashTable(); * $hg= create('new HashTable()'); * * with ($key= new Integer(1); $value= new String('one')); { * $ht->put($key, $value); // OK * $hg->put($key, $value); // *** IllegalArgumentException, key not a string! * } * * * Overloading * =========== * The util.collections classes overload array access where applicable: * * * $h= new HashTable(); * $h['hello']= new Greeting('Hello'); * * $v= create('new Vector()'); * $v[]= new Greeting('Hallo'); * $v[]= new Greeting('Servus'); * $v[]= new Greeting('Grezi'); * * Console::writeLine('Hello in German: ', $v[0]); * * * @see xp://lang.types.ArrayList * @see http://developer.xp-framework.net/xml/rfc/view?0106 * @see http://news.xp-framework.net/article/180/2007/04/29/ * @purpose Colllections */ package util.collections { } * uses('util.collections.Queue', 'text.String'); * * // Fill queue * with ($q= new Queue()); { * $q->put(new String('One')); * $q->put(new String('Two')); * $q->put(new String('Three')); * $q->put(new String('Four')); * } * * // Empty queue * while (!$q->isEmpty()) { * var_dump($q->get()); * } * * * @purpose FIFO * @see xp://util.collections.Stack * @see http://www.faqs.org/docs/javap/c12/ex-12-1-answer.html */ class Queue extends Object { protected $_elements = array(), $_hash = 0; public $__generic = array(); /** * Puts an item into the queue. Returns the element that was added. * * @param lang.Generic object * @return lang.Generic object */ public function put(Generic $object) { if ($this->__generic && !$object instanceof $this->__generic[0]) { throw new IllegalArgumentException('Object '.xp::stringOf($object).' must be of '.$this->__generic[0]); } $this->_elements[]= $object; $this->_hash+= HashProvider::hashOf($object->hashCode()); return $object; } /** * Gets an item from the front of the queue. * * @return lang.Generic * @throws util.NoSuchElementException */ public function get() { if (empty($this->_elements)) { throw new NoSuchElementException('Queue is empty'); } $e= $this->_elements[0]; $this->_hash-= HashProvider::hashOf($e->hashCode()); $this->_elements= array_slice($this->_elements, 1); return $e; } /** * Peeks at the front of the queue (retrieves the first element * without removing it). * * Returns NULL in case the queue is empty. * * @return lang.Generic object */ public function peek() { if (empty($this->_elements)) return NULL; else return $this->_elements[0]; } /** * Returns true if the queue is empty. This is effectively the same * as testing size() for 0. * * @return bool */ public function isEmpty() { return empty($this->_elements); } /** * Returns the size of the queue. * * @return int */ public function size() { return sizeof($this->_elements); } /** * Sees if an object is in the queue and returns its position. * Returns -1 if the object is not found. * * @param lang.Generic object * @return int position */ public function search(Generic $object) { if ($this->__generic && !$object instanceof $this->__generic[0]) { throw new IllegalArgumentException('Object '.xp::stringOf($object).' must be of '.$this->__generic[0]); } return ($keys= array_keys($this->_elements, $object)) ? $keys[0] : -1; } /** * Remove an object from the queue. Returns TRUE in case the element * was deleted, FALSE otherwise. * * @return lang.Generic * @return bool */ public function remove(Generic $object) { if ($this->__generic && !$object instanceof $this->__generic[0]) { throw new IllegalArgumentException('Object '.xp::stringOf($object).' must be of '.$this->__generic[0]); } if (-1 == ($pos= $this->search($object))) return FALSE; $this->_hash-= HashProvider::hashOf($this->_elements[$pos]->hashCode()); unset($this->_elements[$pos]); $this->_elements= array_values($this->_elements); // Re-index return TRUE; } /** * Retrieves an element by its index. * * @param int index * @return lang.Generic * @throws lang.IndexOutOfBoundsException */ public function elementAt($index) { if (!isset($this->_elements[$index])) { throw(new IndexOutOfBoundsException('Index '.$index.' out of bounds')); } return $this->_elements[$index]; } /** * Returns a hashcode for this queue * * @return string */ public function hashCode() { return $this->_hash; } /** * Returns true if this queue equals another queue. * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return ( is('util.collections.Queue', $cmp) && ($this->hashCode() === $cmp->hashCode()) ); } } ?> * uses('util.collections.Stack', 'text.String'); * * // Fill stack * with ($s= new Stack()); { * $s->push(new String('One')); * $s->push(new String('Two')); * $s->push(new String('Three')); * $s->push(new String('Four')); * } * * // Empty stack * while (!$s->isEmpty()) { * var_dump($s->pop()); * } * * * @purpose LIFO * @see xp://util.collections.Queue * @see http://www.faqs.org/docs/javap/c12/ex-12-1-answer.html * @see http://java.sun.com/j2se/1.4.2/docs/api/java/util/Stack.html */ class Stack extends Object { protected $_elements = array(), $_hash = 0; public $__generic = array(); /** * Pushes an item onto the top of the stack. Returns the element that * was added. * * @param lang.Generic object * @return lang.Generic object */ public function push(Generic $object) { if ($this->__generic && !$object instanceof $this->__generic[0]) { throw new IllegalArgumentException('Object '.xp::stringOf($object).' must be of '.$this->__generic[0]); } array_unshift($this->_elements, $object); $this->_hash+= HashProvider::hashOf($object->hashCode()); return $object; } /** * Gets an item from the top of the stack * * @return lang.Generic * @throws util.NoSuchElementException */ public function pop() { if (empty($this->_elements)) { throw(new NoSuchElementException('Stack is empty')); } $element= array_shift($this->_elements); $this->_hash+= HashProvider::hashOf($element->hashCode()); return $element; } /** * Peeks at the front of the stack (retrieves the first element * without removing it). * * Returns NULL in case the stack is empty. * * @return lang.Generic object */ public function peek() { if (empty($this->_elements)) return NULL; else return $this->_elements[0]; } /** * Returns true if the stack is empty. This is effectively the same * as testing size() for 0. * * @return bool */ public function isEmpty() { return empty($this->_elements); } /** * Returns the size of the stack. * * @return int */ public function size() { return sizeof($this->_elements); } /** * Sees if an object is in the stack and returns its position. * Returns -1 if the object is not found. * * @param lang.Generic object * @return int position */ public function search(Generic $object) { if ($this->__generic && !$object instanceof $this->__generic[0]) { throw new IllegalArgumentException('Object '.xp::stringOf($object).' must be of '.$this->__generic[0]); } return ($keys= array_keys($this->_elements, $object)) ? $keys[0] : -1; } /** * Retrieves an element by its index. * * @param int index * @return lang.Generic * @throws lang.IndexOutOfBoundsException */ public function elementAt($index) { if (!isset($this->_elements[$index])) { throw(new IndexOutOfBoundsException('Index '.$index.' out of bounds')); } return $this->_elements[$index]; } /** * Returns a hashcode for this queue * * @return string */ public function hashCode() { return $this->_hash; } /** * Returns true if this queue equals another queue. * * @param lang.Generic cmp * @return bool */ public function equals($cmp) { return $cmp instanceof self && $this->hashCode() === $cmp->hashCode(); } } ?> v= $v; return $self; } public function current() { return $this->v[$this->i]; } public function key() { return $this->i; } public function next() { $this->i++; } public function rewind() { $this->i= 0; } public function valid() { return $this->i < sizeof($this->v); } }'); } /** * Constructor * * @param lang.Generic[] elements default array() */ public function __construct(array $elements= array()) { foreach ($elements as $element) { if ($this->__generic) { if (!$element instanceof $this->__generic[0]) { throw new IllegalArgumentException('Element '.xp::stringOf($element).' must be of '.$this->__generic[0]); } } $this->elements[]= $element; } $this->size= sizeof($this->elements); } /** * Returns an iterator for use in foreach() * * @see php://language.oop5.iterations * @return php.Iterator */ public function getIterator() { return self::$iterate->on($this->elements); } /** * = list[] overloading * * @param int offset * @return lang.Generic */ public function offsetGet($offset) { return $this->get($offset); } /** * list[]= overloading * * @param int offset * @param mixed value * @throws lang.IllegalArgumentException if key is neither numeric (set) nor NULL (add) */ public function offsetSet($offset, $value) { if (is_int($offset)) { $this->set($offset, $value); } else if (NULL === $offset) { $this->add($value); } else { throw new IllegalArgumentException('Incorrect type '.gettype($offset).' for index'); } } /** * isset() overloading * * @param int offset * @return bool */ public function offsetExists($offset) { return ($offset >= 0 && $offset < $this->size); } /** * unset() overloading * * @param int offset */ public function offsetUnset($offset) { $this->remove($offset); } /** * Returns the number of elements in this list. * * @return int */ public function size() { return $this->size; } /** * Tests if this list has no elements. * * @return bool */ public function isEmpty() { return 0 == $this->size; } /** * Adds an element to this list * * @param lang.Generic element * @return lang.Generic the added element * @throws lang.IllegalArgumentException */ public function add(Generic $element) { if ($this->__generic && !$element instanceof $this->__generic[0]) { throw new IllegalArgumentException('Element '.xp::stringOf($element).' must be of '.$this->__generic[0]); } $this->elements[]= $element; $this->size++; return $element; } /** * Adds an element to this list * * @param * elements either an array or an Traversable * @return bool TRUE if the vector was changed as a result of this operation, FALSE if not * @throws lang.IllegalArgumentException */ public function addAll($elements) { if (!is_array($elements) && !$elements instanceof Traversable) { throw new IllegalArgumentException(sprintf( 'Expected either an array or an Traversable', xp::typeOf($elements) )); } $e= array(); $type= $this->__generic ? $this->__generic[0] : xp::reflect('lang.Generic'); foreach ($elements as $element) { if (!$element instanceof $type) { throw new IllegalArgumentException('Element '.xp::stringOf($element).' must be of '.$type); } $e[]= $element; } $size= sizeof($e); $this->elements= array_merge($this->elements, $e); $this->size+= $size; return $size > 0; } /** * Replaces the element at the specified position in this list with * the specified element. * * @param int index * @param lang.Generic element * @return lang.Generic the element previously at the specified position. * @throws lang.IndexOutOfBoundsException */ public function set($index, Generic $element) { if ($index < 0 || $index >= $this->size) { throw new IndexOutOfBoundsException('Offset '.$index.' out of bounds'); } if ($this->__generic && !$element instanceof $this->__generic[0]) { throw new IllegalArgumentException('Element '.xp::stringOf($element).' must be of '.$this->__generic[0]); } $orig= $this->elements[$index]; $this->elements[$index]= $element; return $orig; } /** * Returns the element at the specified position in this list. * * @param int index * @return lang.Generic * @throws lang.IndexOutOfBoundsException if key does not exist */ public function get($index) { if ($index < 0 || $index >= $this->size) { throw new IndexOutOfBoundsException('Offset '.$index.' out of bounds'); } return $this->elements[$index]; } /** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one * from their indices). * * @param int index * @return lang.Generic the element that was removed from the list */ public function remove($index) { if ($index < 0 || $index >= $this->size) { throw new IndexOutOfBoundsException('Offset '.$index.' out of bounds'); } $orig= $this->elements[$index]; unset($this->elements[$index]); $this->elements= array_values($this->elements); $this->size--; return $orig; } /** * Removes all of the elements from this list. The list will be empty * after this call returns. * */ public function clear() { $this->elements= array(); $this->size= 0; } /** * Returns an array of this list's elements * * @return lang.Generic[] */ public function elements() { return $this->elements; } /** * Checks if a value exists in this array * * @param lang.Generic element * @return bool */ public function contains(Generic $element) { if ($this->__generic && !$element instanceof $this->__generic[0]) { throw new IllegalArgumentException('Element '.xp::stringOf($element).' must be of '.$this->__generic[0]); } for ($i= 0; $i < $this->size; $i++) { if ($this->elements[$i]->equals($element)) return TRUE; } return FALSE; } /** * Searches for the first occurence of the given argument * * @param lang.Generic element * @return int offset where the element was found or FALSE */ public function indexOf(Generic $element) { if ($this->__generic && !$element instanceof $this->__generic[0]) { throw new IllegalArgumentException('Element '.xp::stringOf($element).' must be of '.$this->__generic[0]); } for ($i= 0; $i < $this->size; $i++) { if ($this->elements[$i]->equals($element)) return $i; } return FALSE; } /** * Searches for the last occurence of the given argument * * @param lang.Generic element * @return int offset where the element was found or FALSE */ public function lastIndexOf(Generic $element) { if ($this->__generic && !$element instanceof $this->__generic[0]) { throw new IllegalArgumentException('Element '.xp::stringOf($element).' must be of '.$this->__generic[0]); } for ($i= $this->size- 1; $i > -1; $i--) { if ($this->elements[$i]->equals($element)) return $i; } return FALSE; } /** * Creates a string representation of this object * * @return string */ public function toString() { $r= $this->getClassName().'['.$this->size."]@{\n"; for ($i= 0; $i < $this->size; $i++) { $r.= ' '.$i.': '.str_replace("\n", "\n ", xp::stringOf($this->elements[$i]))."\n"; } return $r.'}'; } /** * Checks if a specified object is equal to this object. * * @param lang.Generic collection * @return bool */ public function equals($cmp) { if (!($cmp instanceof IList) || $this->size != $cmp->size) return FALSE; // Compare element by element for ($i= 0; $i < $this->size; $i++) { if ($this->elements[$i]->equals($cmp->elements[$i])) continue; return FALSE; } return TRUE; } } ?> *
  • integer - interpreted as timestamp
  • *
  • string - parsed into a date
  • *
  • php.DateTime object - will be used as is
  • *
  • NULL - creates a date representing the current instance
  • * * * Timezone assignment works through these rules: * . If the time is given as string and contains a parseable timezone identifier * that one is used. * . If no timezone could be determined, the timezone given by the * second parameter is used * . If no timezone has been given as second parameter, the system's default * timezone is used. * * @param mixed in default NULL either a string or a Unix timestamp or DateTime object, defaulting to now * @param string timezone default NULL string of timezone * @throws lang.IllegalArgumentException in case the date is unparseable */ public function __construct($in= NULL, TimeZone $timezone= NULL) { if ($in instanceof DateTime) { $this->date= $in; } else if (is_numeric($in)) { // Specially mark timestamps for parsing (we assume here that strings // containing only digits are timestamps) $this->date= date_create('@'.$in, timezone_open('UTC')); date_timezone_set($this->date, $timezone ? $timezone->getHandle() : timezone_open(date_default_timezone_get())); } else if (FALSE === ($this->date= $timezone instanceof TimeZone ? date_create($in, $timezone->getHandle()) : date_create($in) )) { throw new IllegalArgumentException( 'Given argument is neither a timestamp nor a well-formed timestr