Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ language: java
jdk:
- openjdk11
- oraclejdk9
- oraclejdk8
#- oraclejdk8

services:
- mongodb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,61 @@
import java.util.Set;

public class MemorySerializer implements JsonSerializer<Memory> {
protected boolean forceObject;
protected boolean numericCheck;
protected WeakReference<Environment> env;

protected Map<Memory.Type, Handler> typeHandlers;
protected Map<String, Handler> classHandlers;
private WeakReference<Environment> env;
private boolean forceObject;
private boolean numericCheck;
private boolean unescapedUnicode;
private Map<Memory.Type, Handler> typeHandlers;
private Map<String, Handler> classHandlers;

{
typeHandlers = new HashedMap<Memory.Type, Handler>(1);
classHandlers = new HashedMap<String, Handler>(1);
typeHandlers = new HashedMap<>(1);
classHandlers = new HashedMap<>(1);
}

private static String escapeUnicode(String input) {
int unicodeIdx = indexOfUnicodeChar(input);

// If there is no unicode characters just return as is
if (unicodeIdx == -1) {
return input;
}

StringBuilder sb = new StringBuilder(input.length())
.append(input, 0, unicodeIdx);

for (int i = unicodeIdx; i < input.length(); i++) {
int c = input.charAt(i);

if (c > 0x7F) {
sb.append('\\').append('u');

String hex = Integer.toHexString(c);

// padding with zeros
for (int j = 0; j < 4 - hex.length(); j++) sb.append('0');

sb.append(hex);
} else {
sb.append(c);
}
}

return sb.toString();
}

/**
* Finds first occurrence of unicode character and returns its index or {@code -1} otherwise.
*/
private static int indexOfUnicodeChar(String input) {
for (int i = 0; i < input.length(); i++) {
int c = input.charAt(i);
if (c > 0x7F) {
return i;
}
}

return -1;
}

public Environment getEnv() {
Expand Down Expand Up @@ -66,36 +111,43 @@ protected JsonElement convert(Memory src, Set<Integer> used, boolean useHandlers
switch (src.getRealType()) {
case BOOL:
return new JsonPrimitive(src.toBoolean());
case DOUBLE: return new JsonPrimitive(src.toDouble());
case INT: return new JsonPrimitive(src.toLong());
case DOUBLE:
return new JsonPrimitive(src.toDouble());
case INT:
return new JsonPrimitive(src.toLong());
case STRING: {
if (numericCheck) {
Memory m = StringMemory.toLong(src.toString());
if (m != null)
if (m != null) {
return new JsonPrimitive(m.toLong());
else
} else {
return new JsonPrimitive(src.toString());
} else
return new JsonPrimitive(src.toString());
}
} else {
return new JsonPrimitive(unescapedUnicode ? src.toString() : escapeUnicode(src.toString()));
}
}
case NULL: return JsonNull.INSTANCE;
case NULL:
return JsonNull.INSTANCE;
case ARRAY: {
if (used.add(src.getPointer())){
if (used.add(src.getPointer())) {
JsonArray array = new JsonArray();
JsonObject object = new JsonObject();

boolean isList = !forceObject && src.toValue(ArrayMemory.class).isList();
ForeachIterator iterator = src.toValue(ArrayMemory.class).foreachIterator(false, false);
while (iterator.next()) {
if (isList)
if (isList) {
array.add(convert(iterator.getValue(), used, useHandlers));
else
} else {
object.add(iterator.getKey().toString(), convert(iterator.getValue(), used, useHandlers));
}
}
used.remove(src.getPointer());
return isList ? array : object;
} else
} else {
return JsonNull.INSTANCE;
}
}
case OBJECT: {
if (used.add(src.getPointer())) {
Expand All @@ -108,8 +160,9 @@ protected JsonElement convert(Memory src, Set<Integer> used, boolean useHandlers
do {
handler = classHandlers.get(pr.getLowerName());
pr = pr.getParent();
if (pr == null)
if (pr == null) {
break;
}

} while (handler == null);

Expand All @@ -133,13 +186,15 @@ protected JsonElement convert(Memory src, Set<Integer> used, boolean useHandlers
JsonObject r = new JsonObject();
while (iterator.next()) {
String key = iterator.getKey().toString();
if (!key.startsWith("\0"))
if (!key.startsWith("\0")) {
r.add(iterator.getKey().toString(), convert(iterator.getValue(), used, useHandlers));
}
}
return r;
}
} else
} else {
return JsonNull.INSTANCE;
}
}
default:
return JsonNull.INSTANCE;
Expand All @@ -153,17 +208,23 @@ public JsonElement serialize(Memory src, Type typeOfSrc, JsonSerializationContex
public void setTypeHandler(Memory.Type type, Handler handler) {
if (handler == null) {
typeHandlers.remove(type);
} else
} else {
typeHandlers.put(type, handler);
}
}

public void setClassHandler(String className, Handler handler) {
className = className.toLowerCase();

if (handler == null)
if (handler == null) {
classHandlers.remove(className);
else
} else {
classHandlers.put(className, handler);
}
}

public void unescapedUnicode(boolean unescapedUnicode) {
this.unescapedUnicode = unescapedUnicode;
}

public interface Handler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,25 +74,29 @@ public static Memory json_decode(Environment env, String json) {
@Immutable
public static String json_encode(Memory memory, int options) {
GsonBuilder builder;
if (options != 0){
if (options != 0) {
MemorySerializer serializer = new MemorySerializer();
builder = JsonExtension.createGsonBuilder(serializer);

if ((options & JsonConstants.JSON_PRETTY_PRINT) == JsonConstants.JSON_PRETTY_PRINT){
if ((options & JsonConstants.JSON_PRETTY_PRINT) == JsonConstants.JSON_PRETTY_PRINT) {
builder.setPrettyPrinting();
}

if ((options & JsonConstants.JSON_HEX_TAG) != JsonConstants.JSON_HEX_TAG){
if ((options & JsonConstants.JSON_HEX_TAG) != JsonConstants.JSON_HEX_TAG) {
builder.disableHtmlEscaping();
}

if ((options & JsonConstants.JSON_FORCE_OBJECT) == JsonConstants.JSON_FORCE_OBJECT){
if ((options & JsonConstants.JSON_FORCE_OBJECT) == JsonConstants.JSON_FORCE_OBJECT) {
serializer.setForceObject(true);
}

if ((options & JsonConstants.JSON_NUMERIC_CHECK) == JsonConstants.JSON_NUMERIC_CHECK){
if ((options & JsonConstants.JSON_NUMERIC_CHECK) == JsonConstants.JSON_NUMERIC_CHECK) {
serializer.setNumericCheck(true);
}

if ((options & JsonConstants.JSON_UNESCAPED_UNICODE) == JsonConstants.JSON_UNESCAPED_UNICODE) {
serializer.unescapedUnicode(true);
}
} else {
builder = JsonExtension.DEFAULT_GSON_BUILDER;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,22 @@ public void testScalarJsonEncode() {
@Test
public void testArrayJsonEncode() {
// simple
assertEquals("[1,2,3,4]", JsonFunctions.json_encode(new ArrayMemory(1,2,3,4)));
assertEquals("[1,2,3,4]", JsonFunctions.json_encode(ArrayMemory.ofIntegers(1,2,3,4)));
assertEquals("[1,\"foo\",3.5,true]", JsonFunctions.json_encode(new ArrayMemory(1,"foo",3.5,true)));

// nested
assertEquals("[[1,2],[3,4],5]", JsonFunctions.json_encode(new ArrayMemory(
new ArrayMemory(1,2), new ArrayMemory(3,4), 5
ArrayMemory.ofIntegers(1,2), ArrayMemory.ofIntegers(3,4), 5
)));
}

@Test
public void testObjectJsonEncode() {
assertEquals("{\"0\":100,\"1\":500}", JsonFunctions.json_encode(
new ArrayMemory(100,500), JsonConstants.JSON_FORCE_OBJECT
ArrayMemory.ofIntegers(100, 500), JsonConstants.JSON_FORCE_OBJECT
));

ArrayMemory array = new ArrayMemory(100, 500);
ArrayMemory array = ArrayMemory.ofIntegers(100, 500);
array.put("x", new LongMemory(100500));

assertEquals("{\"0\":100,\"1\":500,\"x\":100500}", JsonFunctions.json_encode(array));
Expand Down Expand Up @@ -136,4 +136,9 @@ public void testJsonSerializableJsonEncode() {
public void testBugs() {
check("json/json_bug217.php");
}

@Test
public void testStandard() {
check("json/002.phpt");
}
}
39 changes: 39 additions & 0 deletions exts/jphp-zend-ext/src/main/tests/resources/json/002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
json_encode() tests
--DESCRIPTION--
Modified the last output. Original PHP count unicode escaped sequence as single character.
--SKIPIF--
<?php if (!extension_loaded("json")) print "skip"; ?>
--FILE--
<?php

var_dump(json_encode(""));
var_dump(json_encode(NULL));
var_dump(json_encode(TRUE));

var_dump(json_encode(array(""=>"")));
var_dump(json_encode(array(array(1))));
var_dump(json_encode(array()));

var_dump(json_encode(array(""=>""), JSON_FORCE_OBJECT));
var_dump(json_encode(array(array(1)), JSON_FORCE_OBJECT));
var_dump(json_encode(array(), JSON_FORCE_OBJECT));

var_dump(json_encode(1));
var_dump(json_encode("руссиш"));

echo "Done\n";
?>
--EXPECT--
string(2) """"
string(4) "null"
string(4) "true"
string(7) "{"":""}"
string(5) "[[1]]"
string(2) "[]"
string(7) "{"":""}"
string(13) "{"0":{"0":1}}"
string(2) "{}"
string(1) "1"
string(44) ""\\u0440\\u0443\\u0441\\u0441\\u0438\\u0448""
Done