
net.shredzone.jshred.io.PhpSerializer Maven / Gradle / Ivy
/**
* jshred - Shred's Toolbox
*
* Copyright (C) 2009 Richard "Shred" Körber
* http://jshred.shredzone.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License / GNU Lesser
* General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
package net.shredzone.jshred.io;
import java.io.FilterWriter;
import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
/**
* PhpSerializer is a {@link Writer} that is able to write out a Java object so PHP's
* unserialize()
function is able to read the result into a PHP variable.
*
* The converter tries to find appropriate data types for conversion. The basic data types
* are as well supported as arrays, collections and maps.
*
* Do not serialize more than one object, since more is not supported by PHP. If you need
* to pass several objects, pack them into an array and then serialize that array.
*
* Note that Java Objects are not serialized in a manner that could be fully restored on
* PHP side. Try to avoid sending pure Java Objects since the result is unpredictable (in
* best case, it's just the {@code toString()} result that is being serialized).
*
* Recursive references to the same object (i.e. an array with an element referring to
* that array) are not supported yet and will lead to a stack overflow.
*
* @author Richard "Shred" Körber
* @since R14
*/
public class PhpSerializer extends FilterWriter {
/**
* Creates a new PhpSerializer.
*
* @param w
* Writer to write the output to.
*/
public PhpSerializer(Writer w) {
super(w);
}
/**
* Writes an integer.
*
* @param i
* Integer value to write
*/
public void serialize(int i) throws IOException {
write("i:");
write(String.valueOf(i));
write(";");
}
/**
* Writes a long.
*
* @param i
* Long value to write
*/
public void serialize(long i) throws IOException {
write("i:");
write(String.valueOf(i));
write(";");
}
/**
* Writes a boolean.
*
* @param b
* Boolean value to write
*/
public void serialize(boolean b) throws IOException {
write("b:");
write(b ? "1" : "0");
write(";");
}
/**
* Writes a double.
*
* @param d
* Double value to write
*/
public void serialize(double d) throws IOException {
write("d:");
write(String.valueOf(d));
write(";");
}
/**
* Writes a float.
*
* @param f
* Float value to write
*/
public void serialize(float f) throws IOException {
serialize((double) f);
}
/**
* Writes a char
*
* @param c
* Char value to write
*/
public void serialize(char c) throws IOException {
serialize(String.valueOf(c));
}
/**
* Writes a char array
*
* @param c
* Array of chars to write
*/
public void serialize(char[] c) throws IOException {
serialize(String.valueOf(c));
}
/**
* Writes a section of a char array
*
* @param c
* Array of chars to write
* @param offset
* First index to write
* @param count
* Number of chars to write
*/
public void serialize(char[] c, int offset, int count) throws IOException {
serialize(String.valueOf(c, offset, count));
}
/**
* Serializes a {@link String}
*
* @param s
* {@link String} to write, may be {@code null}
*/
public void serialize(String s) throws IOException {
if (s == null) {
serializeNull();
} else {
write("s:");
write(String.valueOf(s.length()));
write(":\"");
write(s);
write("\";");
}
}
/**
* Serializes a {@link Number}. {@link BigDecimal} and {@link BigInteger} is
* supported, at least on the Java side. PHP might behave erratic when passing in very
* large numbers.
*
* @param n
* {@link Number} to write, may be {@code null}
*/
public void serialize(Number n) throws IOException {
if (n == null) {
serializeNull();
} else if (n instanceof BigDecimal) {
write("d:");
write(((BigDecimal) n).toString());
write(";");
} else if (n instanceof BigInteger) {
write("i:");
write(((BigInteger) n).toString());
write(";");
} else if (n instanceof Float || n instanceof Double) {
serialize(n.doubleValue());
} else {
serialize(n.longValue());
}
}
/**
* Serializes an array. Each element of that array is serialized.
*
* Note that circular references cannot currently be serialized (e.g. the array itself
* must not be an element of that array) and will lead to a stack overflow.
*
* @param a
* Array to write, may be {@code null}
*/
public void serialize(Object[] a) throws IOException {
if (a == null) {
serializeNull();
} else {
serialize(Arrays.asList(a));
}
}
/**
* Serializes a {@link Collection}. The collection's iterator is used to serialize
* each entry of the {@link Collection}. On PHP side, an index array is created, with
* the index counting from 0.
*
* The {@link Collection} may contain {@code null} values and also nested arrays,
* collections or maps.
*
* Note that the {@link Collection} must not contain circular references.
*
* @param c
* {@link Collection} to write, may be {@code null}
*/
public void serialize(Collection> c) throws IOException {
if (c == null) {
serializeNull();
} else {
write("a:");
write(String.valueOf(c.size()));
write(":{");
long cnt = 0;
for (Object o : c) {
serialize(cnt++);
serialize(o);
}
write("}");
}
}
/**
* Serializes a {@link Map}. The map's key iterator is used to serialize each entry of
* the Map. On PHP side, an associative array is created, with the key being the map's
* key, and the value being the map's appropriate value.
*
* The {@link Map} may contain no more than one {@code null} key. Due to PHP
* limitations, the key is always resolved using {@code toString()} unless it is a
* {@link Number} object. This means that it is not possible to use collections, maps
* or arrays as map keys.
*
* The map's values may contain {@code null} and also nested arrays, collections or
* maps.
*
* Note that the map must not contain circular references.
*
* @param m
* {@link Map} to write, may be {@code null}
*/
public void serialize(Map, ?> m) throws IOException {
if (m == null) {
serializeNull();
} else {
write("a:");
write(String.valueOf(m.size()));
write(":{");
for (Object key : m.keySet()) {
Object value = m.get(key);
// Key needs a special treatment, since it must be serialized
// either as integer or as string.
if (key == null) {
write("s:0:\"\";");
} else if (key instanceof Boolean) {
write("i:");
write(((Boolean) key).booleanValue() ? "1" : "0");
write(";");
} else if (key instanceof Number) {
if (key instanceof Float || key instanceof Double
|| key instanceof BigDecimal) {
serialize(key.toString());
} else {
write("i:");
write(key.toString());
write(";");
}
} else {
serialize(key.toString());
}
// Just serialize the value though
serialize(value);
}
write("}");
}
}
/**
* Serializes an {@link Object}. If the object is found to be an instance of
* {@link String}, {@link Character}, {@link Boolean}, {@link Number},
* {@link Collection} or {@link Map}, or if the object is an array, the appropriate
* {@code serialize()} method is used. In all other cases, the object's
* {@code toString()} result is serialized as a string.
*
* Note that in future versions, Serializable objects may be serialized into PHP
* objects. For now, try to avoid to pass Serializable objects unless they are one of
* the basic types.
*
* @param o
* Object to write, may be {@code null}
*/
public void serialize(Object o) throws IOException {
if (o == null) {
serializeNull();
} else if (o instanceof Number) {
serialize((Number) o);
} else if (o instanceof Collection>) {
serialize((Collection>) o);
} else if (o instanceof Map, ?>) {
serialize((Map, ?>) o);
} else if (o instanceof Object[]) {
serialize((Object[]) o);
} else if (o instanceof Boolean) {
serialize(((Boolean) o).booleanValue());
} else if (o instanceof Character) {
serialize(((Character) o).charValue());
} else {
serialize(o.toString());
}
}
/**
* Writes a mere null pointer.
*/
public void serializeNull() throws IOException {
write("N;");
}
}