package php.runtime.memory.output.serialization; import php.runtime.Memory; import php.runtime.common.Messages; import php.runtime.env.Environment; import php.runtime.env.TraceInfo; import php.runtime.exceptions.support.ErrorType; import php.runtime.lang.IObject; import php.runtime.lang.spl.Serializable; import php.runtime.memory.*; import php.runtime.reflection.ClassEntity; public class Deserializer { protected final Environment env; protected final TraceInfo trace; protected int pos; public Deserializer(Environment env, TraceInfo trace) { this.env = env; this.trace = trace; } protected Memory error(int offset, int length){ this.pos = -1; env.error(trace, ErrorType.E_NOTICE, "unserialize(): Error at offset %s of %s bytes", offset, length); return Memory.NULL; } private int readSize(String input, int pos){ boolean done = false; int j, length = input.length(); for(j = pos; j < length; j++){ char ch = input.charAt(j); if (ch == ':') { done = true; break; } } this.pos = j; if (!done) return -1; String what = input.substring(pos, j); try { return Integer.parseInt(what); } catch (NumberFormatException e){ return -1; } } public Memory read(String input){ return read(input, 0); } public Memory readString(String input, int offset, char endChar){ int i = offset, length = input.length(); int size = readSize(input, i); if (size == -1) return error(i, length); i = this.pos; i++; if (i < length && input.charAt(i) == '"'){ i++; if (i + size <= length){ String str = input.substring(i, i + size); i += size; if (i < length && input.charAt(i) == '"'){ i++; if (i < length && input.charAt(i) == endChar){ this.pos = i + 1; return new StringMemory(str); } else return error(i, length); } else { return error(i + 1, length); } } else { return error(i, length); } } else { return error(i, length); } } public Memory readArray(String input, int offset){ int i = offset, length = input.length(); int size = readSize(input, i); if (size == -1){ error(i, length); return Memory.NULL; } i = this.pos; if (i >= length || input.charAt(i) != ':'){ error(i, length); return Memory.NULL; } i++; if (i >= length || input.charAt(i) != '{'){ error(i, length); return Memory.NULL; } i++; ArrayMemory result = new ArrayMemory(); for(int k = 0; k < size; k++){ Memory key = read(input, i); if (this.pos == -1) return Memory.NULL; i = this.pos; Memory value = read(input, i); if (this.pos == -1) return Memory.NULL; i = this.pos; result.put(ArrayMemory.toKey(key), value); } if (i >= length || input.charAt(i) != '}'){ error(i, length); return Memory.NULL; } this.pos = i + 1; return result; } public Memory read(String input, int offset){ int length = input.length(); if (offset >= length){ error(offset, length); return Memory.NULL; } for(int i = offset; i < length; i++){ char ch = input.charAt(i); Memory.Type type = null; switch (ch){ case 'N': i++; if (i < length && input.charAt(i) == ';'){ this.pos = i + 1; return Memory.NULL; } else return error(i, length); case 'i': type = Memory.Type.INT; case 'd': if (type == null) type = Memory.Type.DOUBLE; case 'b': if (type == null) type = Memory.Type.BOOL; case 's': if (type == null) type = Memory.Type.STRING; case 'a': if (type == null) type = Memory.Type.ARRAY; case 'C': case 'O': if (type == null) type = Memory.Type.OBJECT; boolean isSerializable = ch == 'C'; i++; ch = input.charAt(i); if (ch != ':'){ error(i, length); return Memory.NULL; } i++; switch (type){ case INT: case DOUBLE: case BOOL: boolean done = false; int j; for(j = i; j < length; j++){ ch = input.charAt(j); if (ch == ';') { done = true; break; } } if (!done){ error(i, length); return Memory.NULL; } String what = input.substring(i, j); this.pos = i = j + 1; switch (type){ case BOOL: if ("1".equals(what)) return Memory.TRUE; else if ("0".equals(what)) return Memory.FALSE; else { error(i + 1, length); return Memory.NULL; } case INT: try { return LongMemory.valueOf(Long.parseLong(what)); } catch (NumberFormatException e){ error(i + 1, length); return Memory.NULL; } case DOUBLE: try { return DoubleMemory.valueOf(Double.valueOf(what)); } catch (NumberFormatException e){ error(i + 1, length); return Memory.NULL; } } break; case STRING: return readString(input, i, ';'); case ARRAY: return readArray(input, i); case OBJECT: Memory memory = readString(input, i, ':'); if (this.pos == -1){ return Memory.NULL; } if (!memory.isString()){ error(i, length); return Memory.NULL; } i = this.pos; ClassEntity classEntity = env.fetchClass(memory.toString(), true); if (classEntity == null){ env.error(trace, ErrorType.E_ERROR, Messages.ERR_CLASS_NOT_FOUND, memory.toString()); return Memory.NULL; } try { IObject iObject = classEntity.newObjectWithoutConstruct(env); if (iObject == null) { env.exception( trace, new Messages.Item("Unserialization of '%s' is not allowed").fetch(classEntity.getName()) ); } if (isSerializable) { if (!(iObject instanceof Serializable)){ env.warning(trace, "Class %s has no unserializer", classEntity.getName()); return Memory.NULL; } else { int size = readSize(input, i); if (size == -1) return error(i, length); i = this.pos + 1; what = input.substring(i + 1, i + size + 1); if (i >= length || input.charAt(i) != '{') return error(i, length); i += size; i++; if (i >= length || input.charAt(i) != '}') return error(i, length); env.pushCall(trace, iObject, "unserialize", new StringMemory(what)); try { ((Serializable) iObject).unserialize(env, new StringMemory(what)); } finally { env.popCall(); } } } else { ArrayMemory props = iObject.getProperties(); Memory serProps = readArray(input, i); if (serProps.isArray()) { props.putAll(serProps.toValue(ArrayMemory.class)); if (classEntity.methodMagicWakeup != null){ env.pushCall(trace, iObject, classEntity.methodMagicWakeup.getName()); try { classEntity.methodMagicWakeup.invokeDynamic(iObject, env); } finally { env.popCall(); } } } else return Memory.NULL; } return new ObjectMemory(iObject); } catch (RuntimeException e){ throw e; } catch (Throwable throwable) { throw new RuntimeException(throwable); } } break; default: error(i, length); return Memory.NULL; } } return Memory.NULL; } }