package php.runtime.launcher; import org.develnext.jphp.core.compiler.jvm.JvmCompiler; import org.develnext.jphp.core.opcode.ModuleOpcodePrinter; import php.runtime.Information; import php.runtime.Memory; import php.runtime.Startup; import php.runtime.common.Callback; import php.runtime.common.LangMode; import php.runtime.common.StringUtils; import php.runtime.env.*; import php.runtime.exceptions.support.ErrorType; import php.runtime.ext.core.classes.WrapClassLoader; import php.runtime.ext.support.Extension; import php.runtime.loader.dump.ModuleDumper; import php.runtime.memory.ArrayMemory; import php.runtime.memory.LongMemory; import php.runtime.memory.StringMemory; import php.runtime.reflection.ClassEntity; import php.runtime.reflection.ModuleEntity; import php.runtime.reflection.support.ReflectionUtils; import java.io.*; import java.net.URL; import java.net.URLDecoder; import java.util.*; public class Launcher { protected final String[] args; protected CompileScope compileScope; protected Environment environment; protected Properties config; protected String pathToConf; protected OutputStream out; protected boolean isDebug; private static Launcher current; private ClassLoader classLoader = Launcher.class.getClassLoader(); private long startTime = System.currentTimeMillis(); public Launcher(String pathToConf, String[] args) { current = this; this.args = args; this.out = System.out != null ? System.out : new ByteArrayOutputStream(); this.compileScope = new CompileScope(); this.pathToConf = pathToConf == null ? "JPHP-INF/launcher.conf" : pathToConf; } public Launcher(String[] args) { this(null, args); } public Launcher(ClassLoader classLoader) { this(); this.classLoader = classLoader; } public Launcher() { this(new String[0]); } public Memory getConfigValue(String key) { return getConfigValue(key, Memory.NULL); } public Memory getConfigValue(String key, Memory def) { String result = config.getProperty(key); if (result == null) return def; return new StringMemory(result); } public Memory getConfigValue(String key, String def) { return getConfigValue(key, new StringMemory(def)); } public Collection<InputStream> getResources(String name) { List<InputStream> result = new ArrayList<InputStream>(); try { Enumeration<URL> urls = classLoader.getResources(name); while (urls.hasMoreElements()) { URL url = urls.nextElement(); result.add(url.openStream()); } return result; } catch (IOException e) { return Collections.emptyList(); } } public InputStream getResource(String name){ InputStream stream = classLoader.getResourceAsStream(name); if (stream == null) { try { return new FileInputStream(name); } catch (FileNotFoundException e) { return null; } } return stream; } protected Context getContext(String file){ InputStream bootstrap = getResource(file); if (bootstrap != null) { return new Context(bootstrap, file, environment.getDefaultCharset()); } else return null; } public void initModule(ModuleEntity moduleEntity){ compileScope.loadModule(moduleEntity); compileScope.addUserModule(moduleEntity); environment.registerModule(moduleEntity); } public ModuleEntity loadFromCompiled(String file) throws IOException { InputStream inputStream = getResource(file); if (inputStream == null) return null; Context context = new Context(inputStream, file, environment.getDefaultCharset()); ModuleDumper moduleDumper = new ModuleDumper(context, environment, true); return moduleDumper.load(inputStream); } public ModuleEntity loadFromFile(String file) throws IOException { InputStream inputStream = getResource(file); if (inputStream == null) return null; Context context = new Context(inputStream, file, environment.getDefaultCharset()); JvmCompiler compiler = new JvmCompiler(environment, context); return compiler.compile(false); } public ModuleEntity loadFrom(String file) throws IOException { if (file.endsWith(".phb")) return loadFromCompiled(file); else return loadFromFile(file); } protected void readConfig(){ this.config = new Properties(); this.compileScope.configuration = new HashMap<>(); String externalConfig = System.getProperty("jphp.config"); if (externalConfig != null) { try { FileInputStream inStream = new FileInputStream(externalConfig); config.load(inStream); inStream.close(); for (String name : config.stringPropertyNames()){ compileScope.configuration.put(name, new StringMemory(config.getProperty(name))); } } catch (IOException e) { throw new LaunchException("Unable to load the config -Djphp.config=" + externalConfig); } } InputStream resource; resource = getResource(pathToConf); if (resource != null) { try { config.load(resource); for (String name : config.stringPropertyNames()){ compileScope.configuration.put(name, new StringMemory(config.getProperty(name))); } isDebug = Startup.isDebug(); compileScope.setDebugMode(isDebug); compileScope.setLangMode( LangMode.valueOf(getConfigValue("env.langMode", LangMode.MODERN.name()).toString().toUpperCase()) ); } catch (IOException e) { throw new LaunchException(e.getMessage()); } } } protected void loadExtensions() { ServiceLoader<Extension> loader = ServiceLoader.load(Extension.class, compileScope.getClassLoader()); for (Extension extension : loader) { compileScope.registerExtension(extension); } } protected void initExtensions() { String tmp = getConfigValue("env.extensions", "spl").toString(); String[] _extensions = StringUtils.split(tmp, ","); Set<String> extensions = new HashSet<String>(); for(String ext : _extensions) { extensions.add(ext.trim()); } loadExtensions(); compileScope.getClassLoader().onAddLibrary(new Callback<Void, URL>() { @Override public Void call(URL param) { loadExtensions(); return null; } }); if (getConfigValue("env.autoregister_extensions", Memory.TRUE).toBoolean()) { for(InputStream list : getResources("JPHP-INF/extensions.list")) { Scanner scanner = new Scanner(list); while (scanner.hasNext()) { String line = scanner.nextLine().trim(); if (!line.isEmpty()) { System.out.println("WARNING!!! Auto-registration via JPHP-INF/extensions.list is deprected, " + line + ", use META-INF/services/php.runtime.ext.support.Extension file"); extensions.add(line); } } } } for(String ext : extensions){ String className = Information.EXTENSIONS.get(ext.trim().toLowerCase()); if (className == null) className = ext.trim(); long t = System.currentTimeMillis(); compileScope.registerExtension(className); } this.environment = getConfigValue("env.concurrent", "1").toBoolean() ? new ConcurrentEnvironment(compileScope, out) : new Environment(compileScope, out); environment.setErrorFlags(ErrorType.E_ALL.value ^ ErrorType.E_NOTICE.value); environment.getDefaultBuffer().setImplicitFlush(true); } public void printTrace(String name) { long t = System.currentTimeMillis() - startTime; System.out.printf("[%s] = " + t + " millis\n", name); startTime = System.currentTimeMillis(); } public void run() throws Throwable { run(true); } public void run(boolean mustBootstrap) throws Throwable { run(mustBootstrap, false); } public void beforeIncludeBootstrap() { } public void afterIncludeBootstrap() { } public void run(boolean mustBootstrap, boolean disableExtensions) throws Throwable { readConfig(); if (!disableExtensions) { initExtensions(); } if (isDebug()){ if (compileScope.getTickHandler() == null) { throw new LaunchException("Cannot find a debugger, please add the jphp-debugger dependency"); } } if (Startup.isShowInitDelay()) { long t = System.currentTimeMillis() - startTime; Startup.trace("Startup time = " + t + "ms"); } String file = config.getProperty("bootstrap.file", "JPHP-INF/.bootstrap.php"); String classLoader = config.getProperty("env.classLoader", ReflectionUtils.getClassName(WrapClassLoader.WrapLauncherClassLoader.class)); if (classLoader != null && !(classLoader.isEmpty())) { ClassEntity classLoaderEntity = environment.fetchClass(classLoader); if (classLoaderEntity == null) { throw new LaunchException("Class loader class is not found: " + classLoader); } WrapClassLoader loader = classLoaderEntity.newObject(environment, TraceInfo.UNKNOWN, true); environment.invokeMethod(loader, "register", Memory.TRUE); } if (file != null && !file.isEmpty()){ try { ModuleEntity bootstrap = loadFrom(file); if (bootstrap == null) { throw new IOException(); } beforeIncludeBootstrap(); if (new StringMemory(config.getProperty("bootstrap.showBytecode", "")).toBoolean()) { ModuleOpcodePrinter moduleOpcodePrinter = new ModuleOpcodePrinter(bootstrap); System.out.println(moduleOpcodePrinter.toString()); } initModule(bootstrap); ArrayMemory argv = ArrayMemory.ofStrings(this.args); String path = URLDecoder.decode( Launcher.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath(), "UTF-8" ); argv.unshift(StringMemory.valueOf(path)); environment.getGlobals().put("argv", argv); environment.getGlobals().put("argc", LongMemory.valueOf(argv.size())); environment.pushCall(new CallStackItem(new TraceInfo(bootstrap.getName(), -1, -1))); try { bootstrap.includeNoThrow(environment); } finally { afterIncludeBootstrap(); environment.popCall(); compileScope.triggerProgramShutdown(environment); if (StringMemory.valueOf(config.getProperty("env.doFinal", "1")).toBoolean()) { environment.doFinal(); } } } catch (IOException e) { throw new LaunchException("Cannot find '" + file + "' resource for `bootstrap.file` option"); } } else if (mustBootstrap) throw new LaunchException("Please set value of the `bootstrap.file` option in the launcher.conf file"); } public boolean isDebug() { return isDebug; } public OutputStream getOut(){ return out; } public CompileScope getCompileScope() { return compileScope; } public Environment getEnvironment() { return environment; } public static Launcher current() { return current; } public static void main(String[] args) throws Throwable { Launcher launcher = new Launcher(args); Launcher.current = launcher; launcher.run(); } }