/** * 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.IClassLoader classloader * @param lang.XPClass[] interfaces names of the interfaces to implement * @return lang.XPClass * @throws lang.IllegalArgumentException */ public static function getProxyClass(IClassLoader $classloader, array $interfaces) { static $num = 0; static $cache = array(); $t = sizeof($interfaces); if (0 === $t) { throw new IllegalArgumentException('Interfaces may not be empty'); } // 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 = self::PREFIX . $num++; $bytes = 'class ' . $name . ' extends ' . xp::reflect('lang.reflect.Proxy') . ' implements '; $added = array(); for ($j = 0; $j < $t; $j++) { $bytes .= xp::reflect($interfaces[$j]->getName()) . ', '; } $bytes = substr($bytes, 0, -2) . " {\n"; for ($j = 0; $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) { $signature .= ', '; $restriction = $param->getTypeRestriction(); if ($restriction instanceof XPClass) { $signature .= '\\' . strtr($restriction->getName(), array('php.' => '', '.' => '\\')); } else { if (Primitive::$ARRAY->equals($restriction)) { $signature .= 'array'; } } $signature .= ' $' . $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; }
/** Creates fixture */ protected function newFixture() : IClassLoader { return DynamicClassLoader::instanceFor('test'); }
/** * Define an interface with a given name * * @param string class fully qualified class name * @param var[] parents The parent interfaces either by qualified names or XPClass instances * @param string bytes default "{}" inner sourcecode of class (containing {}) * @return lang.XPClass * @throws lang.FormatException in case the class cannot be defined */ public static function defineInterface($class, $parents, $bytes = '{}') { $name = xp::reflect($class); if (!isset(xp::$cl[$class])) { // Load parent class and implemented interfaces $if = array(); foreach ((array) $parents as $interface) { $if[] = self::classOf($interface)->literal(); } // Define class with($dyn = self::registerLoader(DynamicClassLoader::instanceFor(__METHOD__))); $dyn->setClassBytes($class, sprintf('interface %s%s %s', $name, $parents ? ' extends ' . implode(', ', $if) : '', $bytes)); return $dyn->loadClass($class); } return new XPClass($name); }
function newinstance($spec, $args, $bytes) { static $u = 0; // Check for an anonymous generic if (strstr($spec, '<')) { $class = Type::forName($spec); $type = $class->literal(); $p = strrpos(substr($type, 0, strpos($type, 'ии')), 'и'); } else { FALSE === strrpos($spec, '.') && ($spec = xp::nameOf($spec)); $type = xp::reflect($spec); if (!class_exists($type, FALSE) && !interface_exists($type, FALSE)) { xp::error(xp::stringOf(new Error('Class "' . $spec . '" does not exist'))); // Bails } $p = strrpos($type, 'и'); } // Create unique name $n = 'и' . ++$u; if (FALSE !== $p) { $ns = '$package= "' . strtr(substr($type, 0, $p), 'и', '.') . '"; '; $spec = strtr(substr($type, 0, $p), 'и', '.') . '.' . substr($type, $p + 1) . $n; $decl = $type . $n; } else { if (FALSE === ($p = strrpos($type, '\\'))) { $ns = ''; $decl = $type . $n; $spec = substr($spec, 0, strrpos($spec, '.')) . '.' . $type . $n; } else { $ns = 'namespace ' . substr($type, 0, $p) . '; '; $decl = substr($type, $p + 1) . $n; $spec = strtr($type, '\\', '.') . $n; $type = '\\' . $type; } } // Checks whether an interface or a class was given $cl = DynamicClassLoader::instanceFor(__FUNCTION__); if (interface_exists($type)) { $cl->setClassBytes($spec, $ns . 'class ' . $decl . ' extends Object implements ' . $type . ' ' . $bytes); } else { $cl->setClassBytes($spec, $ns . 'class ' . $decl . ' extends ' . $type . ' ' . $bytes); } $decl = $cl->loadClass0($spec); // Build paramstr for evaluation for ($paramstr = '', $i = 0, $m = sizeof($args); $i < $m; $i++) { $paramstr .= ', $args[' . $i . ']'; } return eval('return new ' . $decl . '(' . substr($paramstr, 2) . ');'); }
/** * Creates an XPClass instance from the specified code using a * DynamicClassLoader. * * @param string bytes * @return lang.XPClass */ private function createClass($bytes) { $dyn = DynamicClassLoader::instanceFor(__METHOD__); try { $dyn->setClassBytes($this->getProxyName(), $bytes); $class = $dyn->loadClass($this->getProxyName()); } catch (FormatException $e) { throw new IllegalArgumentException($e->getMessage()); } return $class; }
/** * 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::$cl[$class])) { if (!empty($parents)) { $if = array_map(array('xp', 'reflect'), (array) $parents); foreach ($if as $i => $super) { if (interface_exists($super, FALSE)) { continue; } raise('lang.ClassLinkageException', $parents[$i], self::getLoaders()); } } with($dyn = self::registerLoader(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); }