package php.runtime.ext.core.classes.stream; import php.runtime.Memory; import php.runtime.common.Constants; import php.runtime.common.DigestUtils; import php.runtime.common.HintType; import php.runtime.env.Environment; import php.runtime.env.TraceInfo; import php.runtime.ext.core.classes.lib.FsUtils; import php.runtime.invoke.Invoker; import php.runtime.lang.BaseObject; import php.runtime.memory.ArrayMemory; import php.runtime.memory.LongMemory; import php.runtime.memory.ObjectMemory; import php.runtime.memory.StringMemory; import php.runtime.reflection.ClassEntity; import java.io.*; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.PathMatcher; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.zip.CRC32; import static php.runtime.annotation.Reflection.*; @Name("php\\io\\File") public class FileObject extends BaseObject { public final static String PATH_SEPARATOR = File.pathSeparator; public final static String DIRECTORY_SEPARATOR = File.separator; public final static boolean PATH_NAME_CASE_INSENSITIVE = Constants.PATH_NAME_CASE_INSENSITIVE; protected File file; public FileObject(Environment env, ClassEntity clazz) { super(env, clazz); } public FileObject(Environment env, File file) { super(env); this.file = file; if (file == null) throw new IllegalArgumentException(); } public FileObject(Environment env, ClassEntity clazz, File file) { super(env, clazz); this.file = file; if (file == null) throw new IllegalArgumentException(); } protected void exception(Environment env, String message, Object... args){ WrapIOException exception = new WrapIOException(env, env.fetchClass("php\\io\\IOException")); exception.__construct(env, new StringMemory(String.format(message, args))); env.__throwException(exception); } public File getFile() { return file; } @Signature({@Arg("path"), @Arg(value = "child", optional = @Optional("NULL"))}) public Memory __construct(Environment env, Memory... args){ String path = args[0].toString(); if (args[1].isNull()) file = new File(path); else { String child = args[1].toString(); file = new File(path, child); } return Memory.NULL; } @Signature public Memory __debugInfo(Environment env, Memory... args) { ArrayMemory r = new ArrayMemory(); r.refOfIndex("*path").assign(file.getPath()); return r.toConstant(); } @Signature public Memory exists(Environment env, Memory... args){ return file.exists() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory canExecute(Environment env, Memory... args){ return file.canExecute() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory canRead(Environment env, Memory... args){ return file.canRead() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory canWrite(Environment env, Memory... args){ return file.canWrite() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory getName(Environment env, Memory... args){ return new StringMemory(file.getName()); } @Signature public Memory getAbsolutePath(Environment env, Memory... args){ return new StringMemory(file.getAbsolutePath()); } @Signature public Memory getCanonicalPath(Environment env, Memory... args){ try { return new StringMemory(file.getCanonicalPath()); } catch (IOException e) { exception(env, e.getMessage()); return Memory.FALSE; } } @Signature public Memory getParent(Environment env, Memory... args){ return new StringMemory(file.getParent()); } @Signature public Memory getPath(Environment env, Memory... args){ return new StringMemory(file.getPath()); } @Signature public Memory getAbsoluteFile(Environment env, Memory... args){ FileObject fo = new FileObject(env, __class__); fo.file = file.getAbsoluteFile(); return new ObjectMemory(fo); } @Signature public Memory getCanonicalFile(Environment env, Memory... args){ FileObject fo = new FileObject(env, __class__); try { fo.file = file.getCanonicalFile(); } catch (IOException e) { exception(env, e.getMessage()); return Memory.NULL; } return new ObjectMemory(fo); } @Signature public Memory getParentFile(Environment env, Memory... args){ if (file.getParentFile() == null) return Memory.NULL; FileObject fo = new FileObject(env, __class__); fo.file = file.getParentFile(); return new ObjectMemory(fo); } @Signature public Memory mkdir(Environment env, Memory... args){ return file.mkdir() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory mkdirs(Environment env, Memory... args){ return file.mkdirs() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory isFile(Environment env, Memory... args){ return file.isFile() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory isDirectory(Environment env, Memory... args){ return file.isDirectory() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory isAbsolute(Environment env, Memory... args){ return file.isAbsolute() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory isHidden(Environment env, Memory... args){ return file.isHidden() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory delete(Environment env, Memory... args){ return file.delete() ? Memory.TRUE : Memory.FALSE; } @Signature public Memory deleteOnExit(Environment env, Memory... args){ file.deleteOnExit(); return Memory.NULL; } @Signature(@Arg(value = "withDirs", optional = @Optional("false"))) public Memory createNewFile(Environment env, Memory... args){ try { if (args[0].toBoolean()) { if (!file.getParentFile().mkdirs()) return Memory.FALSE; } return file.createNewFile() ? Memory.TRUE : Memory.FALSE; } catch (IOException e) { exception(env, e.getMessage()); return Memory.FALSE; } } @Signature public Memory lastModified(Environment env, Memory... args){ return LongMemory.valueOf(file.lastModified()); } @Signature public Memory length(Environment env, Memory... args) { try { return LongMemory.valueOf(file.length()); } catch (Exception e){ return Memory.FALSE; } } @Signature public Memory crc32(Environment env, Memory... args) { CRC32 crcMaker = new CRC32(); byte[] buffer = new byte[1024]; int len; FileInputStream is; try { is = new FileInputStream(file); while ((len = is.read(buffer)) > 0) { crcMaker.update(buffer, 0, len); } is.close(); return LongMemory.valueOf(crcMaker.getValue()); } catch (FileNotFoundException e) { return Memory.NULL; } catch (IOException e) { return Memory.NULL; } } @Signature({ @Arg(value = "algorithm", optional = @Optional("MD5")), @Arg(value = "progress", type = HintType.CALLABLE, optional = @Optional("null")) }) public Memory hash(Environment env, Memory... args) throws NoSuchAlgorithmException { MessageDigest messageDigest = MessageDigest.getInstance(args[0].toString()); Invoker invoker = Invoker.valueOf(env, null, args[1]); byte[] buffer = new byte[FsUtils.BUFFER_SIZE]; int len; FileInputStream is; try { is = new FileInputStream(file); int sum = 0; try { while ((len = is.read(buffer)) > 0) { messageDigest.update(buffer, 0, len); sum += len; if (invoker != null) { if (invoker.callAny(sum, len).toValue() == Memory.FALSE) { break; } } } } finally { is.close(); } return StringMemory.valueOf(DigestUtils.bytesToHex(messageDigest.digest())); } catch (FileNotFoundException e) { return Memory.NULL; } catch (IOException e) { return Memory.NULL; } } @Signature(@Arg("newName")) public Memory renameTo(Environment env, Memory... args){ return file.renameTo(new File(args[0].toString())) ? Memory.TRUE : Memory.FALSE; } @Signature({@Arg("value"), @Arg(value = "ownerOnly", optional = @Optional(value = "1", type = HintType.BOOLEAN))}) public Memory setExecutable(Environment env, Memory... args){ return file.setExecutable(args[0].toBoolean(), args[1].toBoolean()) ? Memory.TRUE : Memory.FALSE; } @Signature({@Arg("value"), @Arg(value = "ownerOnly", optional = @Optional(value = "1", type = HintType.BOOLEAN))}) public Memory setReadable(Environment env, Memory... args){ return file.setReadable(args[0].toBoolean(), args[1].toBoolean()) ? Memory.TRUE : Memory.FALSE; } @Signature({@Arg("value"), @Arg(value = "ownerOnly", optional = @Optional(value = "1", type = HintType.BOOLEAN))}) public Memory setWritable(Environment env, Memory... args){ return file.setWritable(args[0].toBoolean(), args[1].toBoolean()) ? Memory.TRUE : Memory.FALSE; } @Signature public Memory setReadOnly(Environment env, Memory... args){ return file.setReadOnly() ? Memory.TRUE : Memory.FALSE; } @Signature(@Arg("time")) public Memory setLastModified(Environment env, Memory... args){ return file.setLastModified(args[0].toLong()) ? Memory.TRUE : Memory.FALSE; } @Signature(@Arg("file")) public Memory compareTo(Environment env, Memory... args){ File what; if (args[0].isObject()){ if (args[0].instanceOf("php\\io\\File")){ FileObject fileObject = (FileObject)args[0].toValue(ObjectMemory.class).value; what = fileObject.file; } else { exception(env, "Argument 1 must be an instance of %s", "php\\io\\File"); return Memory.FALSE; } } else { what = new File(args[0].toString()); } return LongMemory.valueOf(file.compareTo(what)); } @Signature(@Arg(value = "filter", optional = @Optional("NULL"))) public Memory find(final Environment env, Memory... args){ if (args[0].isNull()){ return ArrayMemory.ofStrings(file.list()).toConstant(); } else { final Invoker invoker = Invoker.valueOf(env, null, args[0]); if (invoker == null) { exception(env, "Invalid filter value, must be callable"); return Memory.NULL; } final TraceInfo trace = env.trace(); invoker.setTrace(trace); String[] result = file.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { FileObject o = new FileObject(env, __class__, dir); Memory[] args = new Memory[]{new ObjectMemory(o), new StringMemory(name)}; return invoker.callNoThrow(args).toBoolean(); } }); return ArrayMemory.ofStrings(result); } } @Signature(@Arg(value = "filter", optional = @Optional("NULL"))) public Memory findFiles(final Environment env, Memory... args){ File[] result; if (args[0].isNull()){ result = file.listFiles(); } else { final Invoker invoker = Invoker.valueOf(env, null, args[0]); if (invoker == null) { exception(env, "Invalid filter value, must be callable"); return Memory.NULL; } final TraceInfo trace = env.trace(); invoker.setTrace(trace); result = file.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { FileObject o = new FileObject(env, __class__, dir); Memory[] args = new Memory[]{new ObjectMemory(o), new StringMemory(name)}; return invoker.callNoThrow(args).toBoolean(); } }); } ArrayMemory arr = new ArrayMemory(); if (result != null){ for (File e : result){ arr.add(new ObjectMemory(new FileObject(env, __class__, e))); } } return arr.toConstant(); } @Signature public Memory __toString(Environment env, Memory... args) { return new StringMemory(file.getPath()); } @Signature({ @Arg("prefix"), @Arg("suffix"), @Arg(value = "directory", optional = @Optional("NULL")) }) public static Memory createTemp(Environment env, Memory... args) throws IOException { File file; if (args[2].isNull()) file = File.createTempFile(args[0].toString(), args[1].toString()); else file = File.createTempFile(args[0].toString(), args[1].toString(), valueOf(args[2])); return new ObjectMemory(new FileObject(env, file)); } @Signature(@Arg("pattern")) public Memory matches(Environment env, Memory... args) { FileSystem aDefault = FileSystems.getDefault(); PathMatcher pathMatcher = aDefault.getPathMatcher(args[0].toString()); return pathMatcher.matches(aDefault.getPath(file.getPath())) ? Memory.TRUE : Memory.FALSE; } @Signature public static Memory listRoots(Environment env, Memory... args) { ArrayMemory r = new ArrayMemory(); File[] roots = File.listRoots(); if (roots == null) return r.toConstant(); for(File e : roots) { r.add(new FileObject(env, e)); } return r.toConstant(); } @Signature public static File of(String path) { return new File(path); } public static File valueOf(Memory arg) { if (arg.instanceOf(FileObject.class)) return arg.toObject(FileObject.class).getFile(); else return new File(arg.toString()); } }