package php.runtime.ext.core.classes.lib; import php.runtime.Memory; import php.runtime.common.DigestUtils; import php.runtime.common.HintType; import php.runtime.common.StringUtils; import php.runtime.env.Environment; import php.runtime.ext.core.MathFunctions; import php.runtime.lang.BaseObject; import php.runtime.lang.ForeachIterator; import php.runtime.memory.*; import php.runtime.reflection.ClassEntity; import php.runtime.reflection.ParameterEntity; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Scanner; import java.util.UUID; import static php.runtime.annotation.Reflection.*; import static php.runtime.annotation.Runtime.FastMethod; @Name("php\\lib\\Str") public class StrUtils extends BaseObject { public StrUtils(Environment env, ClassEntity clazz) { super(env, clazz); } @Signature private Memory __construct(Environment env, Memory... args) { return Memory.NULL; } @FastMethod @Signature({ @Arg("string"), @Arg("search"), @Arg(value = "fromIndex", optional = @Optional(value = "0", type = HintType.INT)) }) public static Memory pos(Environment env, Memory... args) { int fromIndex = args[2].toInteger(); return LongMemory.valueOf(args[0].toString().indexOf(args[1].toString(), fromIndex)); } @FastMethod @Signature({ @Arg("string"), @Arg("search"), @Arg(value = "fromIndex", optional = @Optional(value = "0", type = HintType.INT)) }) public static Memory posIgnoreCase(Environment env, Memory... args) { int fromIndex = args[2].toInteger(); return LongMemory.valueOf(StringUtils.indexOfIgnoreCase(args[0].toString(), args[1].toBinaryString(), fromIndex)); } @FastMethod @Signature({ @Arg("string"), @Arg("search"), @Arg(value = "fromIndex", optional = @Optional("NULL")) }) public static Memory lastPos(Environment env, Memory... args) { return LongMemory.valueOf(args[2].isNull() ? args[0].toString().lastIndexOf(args[1].toString()) : args[0].toString().lastIndexOf(args[1].toString(), args[2].toInteger()) ); } @FastMethod @Signature({ @Arg("string"), @Arg("search"), @Arg(value = "fromIndex", optional = @Optional("NULL")) }) public static Memory lastPosIgnoreCase(Environment env, Memory... args) { String string = args[0].toString(); int from = args[2].isNull() ? string.length() : args[2].toInteger(); return LongMemory.valueOf(StringUtils.lastIndexOfIgnoreCase(string, args[1].toString(), from)); } @FastMethod @Signature({@Arg("string"), @Arg("beginIndex"), @Arg(value = "endIndex", optional = @Optional("NULL"))}) public static Memory sub(Environment env, Memory... args) { String string = args[0].toString(); int len = string.length(); int begin = args[1].toInteger(); int finish; if (args.length < 3 || args[2].isNull()) finish = len; else { finish = args[2].toInteger(); if (finish > len) return Memory.FALSE; } if (begin > finish || begin < 0 || begin > len - 1) return Memory.FALSE; return StringMemory.valueOf(string.substring(begin, finish)); } @FastMethod @Signature({@Arg("string1"), @Arg("string2")}) public static Memory compare(Environment env, Memory... args) { return LongMemory.valueOf(args[0].toString().compareTo(args[1].toString())); } @FastMethod @Signature({@Arg("string1"), @Arg("string2")}) public static Memory compareIgnoreCase(Environment env, Memory... args) { return LongMemory.valueOf(args[0].toString().compareToIgnoreCase(args[1].toString())); } @FastMethod @Signature({@Arg("string1"), @Arg("string2")}) public static Memory equalsIgnoreCase(Environment env, Memory... args) { return TrueMemory.valueOf(args[0].toString().equalsIgnoreCase(args[1].toString())); } @FastMethod @Signature({ @Arg("string"), @Arg("prefix"), @Arg(value = "offset", optional = @Optional(value = "0", type = HintType.INT)) }) public static Memory startsWith(Environment env, Memory... args) { return args[0].toString().startsWith(args[1].toString(), args[2].toInteger()) ? Memory.TRUE : Memory.FALSE; } @FastMethod @Signature({ @Arg("string"), @Arg("suffix") }) public static Memory endsWith(Environment env, Memory... args) { return args[0].toString().endsWith(args[1].toString()) ? Memory.TRUE : Memory.FALSE; } @FastMethod @Signature({@Arg("string")}) public static Memory lower(Environment env, Memory... args) { return StringMemory.valueOf(args[0].toString().toLowerCase()); } @FastMethod @Signature({@Arg("string")}) public static Memory upper(Environment env, Memory... args) { return StringMemory.valueOf(args[0].toString().toUpperCase()); } @FastMethod @Signature({@Arg("string")}) public static Memory length(Environment env, Memory... args) { return LongMemory.valueOf(args[0].toString().length()); } @FastMethod @Signature({@Arg("string"), @Arg("target"), @Arg("replacement")}) public static Memory replace(Environment env, Memory... args) { String target = args[1].toString(); String replacement = args[2].toString(); if (target.length() == 1 && replacement.length() == 1) { return StringMemory.valueOf(args[0].toString().replace(target.charAt(0), replacement.charAt(0))); } else { return StringMemory.valueOf(args[0].toString().replace(target, replacement)); } } @FastMethod @Signature({@Arg("string"), @Arg("amount")}) public static Memory repeat(Environment env, Memory... args) { String s = args[0].toString(); int amount = args[1].toInteger(); if (amount <= 0) return Memory.FALSE; if (s.length() == 1) { return new StringMemory(StringUtils.repeat(s.charAt(0), amount)); } else { int cnt = args[1].toInteger(); StringBuilder sb = new StringBuilder(cnt * s.length()); for(int i = 0; i < cnt; i++) { sb.append(s); } return new StringMemory(sb.toString()); } } protected static String trimStringByString(String text, String trimBy, boolean toLeft, boolean toRight) { int len = text.length(); int left = 0; if (toLeft) { while (left < len && trimBy.indexOf(text.charAt(left)) > -1) { left++; } } int right = len - 1; if (toRight) { while (right > 0 && trimBy.indexOf(text.charAt(right)) > -1) { right--; } } if (toLeft && toRight) { if (left == 0 && right == len - 1) return text; /* if (left < right - 1) return "";*/ if (right == 0 && left > 0) return text.substring(left); return text.substring(left, right + 1); } else if (toLeft) { if (left == len) return ""; return text.substring(left); } else if (toRight) { if (right == 0) return ""; return text.substring(0, right + 1); } else throw new IllegalArgumentException(); } @FastMethod @Signature({ @Arg("string"), @Arg(value = "charlist", optional = @Optional(" \t\n\r\0\11")) }) public static Memory trim(Environment env, Memory... args) { String trimBy = args[1].toString(); return StringMemory.valueOf(trimStringByString(args[0].toString(), trimBy, true, true)); } @FastMethod @Signature({ @Arg("string"), @Arg(value = "charlist", optional = @Optional(" \t\n\r\0\11")) }) public static Memory trimRight(Environment env, Memory... args) { String trimBy = args[1].toString(); return StringMemory.valueOf(trimStringByString(args[0].toString(), trimBy, false, true)); } @FastMethod @Signature({ @Arg("string"), @Arg(value = "charlist", optional = @Optional(" \t\n\r\0\11")) }) public static Memory trimLeft(Environment env, Memory... args) { String trimBy = args[1].toString(); return StringMemory.valueOf(trimStringByString(args[0].toString(), trimBy, true, false)); } @FastMethod @Signature(@Arg("string")) public static Memory reverse(Environment env, Memory... args) { return new StringMemory(StringUtils.reverse(args[0].toString())); } @FastMethod @Signature(@Arg("string")) public static Memory shuffle(Environment env, Memory... args) { char[] chars = args[0].toString().toCharArray(); int length = chars.length; for (int i = 0; i < length; i++) { int rand = MathFunctions.RANDOM.nextInt(length); char temp = chars[rand]; chars[rand] = chars[i]; chars[i] = temp; } return new StringMemory(new String(chars)); } @FastMethod @Signature({ @Arg("string"), @Arg("separator"), @Arg(value = "limit", optional = @Optional(value = "0", type = HintType.INT)) }) public static Memory split(Environment env, Memory... args) { String string = args[0].toString(); if (string.isEmpty()) { return new ArrayMemory().toConstant(); } String separator = args[1].toString(); int limit = args[2].toInteger(); String[] strings = StringUtils.split(string, separator, limit); return ArrayMemory.ofStrings(strings).toConstant(); } @FastMethod @Signature({ @Arg("collection"), @Arg("separator"), @Arg(value = "limit", optional = @Optional(value = "0", type = HintType.INT)) }) public static Memory join(Environment env, Memory... args) { String separator = args[1].toString(); StringBuilder builder = new StringBuilder(); int limit = args[2].toInteger(); int i = 0; if (args[0].isArray()) { ArrayMemory array = args[0].toValue(ArrayMemory.class); int size = array.size(); if (limit > 0 && limit < size) size = limit; for(Memory el : array){ builder.append(el); if (i != size - 1) builder.append(separator); i++; if (i == size) break; } return new StringMemory(builder.toString()); } else { ParameterEntity.validateTypeHinting(env, 1, args, HintType.TRAVERSABLE, false); ForeachIterator iterator = args[0].getNewIterator(env); while (iterator.next()) { builder.append(iterator.getValue()); builder.append(separator); i++; if (limit > 0 && i == limit) break; } int length = builder.length(); if (length > 0) { builder.delete(length - separator.length(), length); } else return Memory.CONST_EMPTY_STRING; return new StringMemory(builder.toString()); } } @FastMethod @Signature({ @Arg("string"), @Arg("charset") }) public static Memory encode(Environment env, Memory... args) { Charset charset; try { charset = Charset.forName(args[1].toString()); } catch (Exception e) { return Memory.FALSE; } ByteBuffer buffer = charset.encode(args[0].toString()); return new BinaryMemory(Arrays.copyOf(buffer.array(), buffer.limit())); } @FastMethod @Signature({ @Arg("string"), @Arg("charset") }) public static Memory decode(Environment env, Memory... args) { Charset charset; try { charset = Charset.forName(args[1].toString()); } catch (Exception e) { return Memory.FALSE; } CharBuffer charBuffer = charset.decode(ByteBuffer.wrap(args[0].getBinaryBytes(env.getDefaultCharset()))); return StringMemory.valueOf(charBuffer.toString()); } @FastMethod @Signature({ @Arg("string"), @Arg(value = "bigNumbers", optional = @Optional("true")) }) public static Memory isNumber(Environment env, Memory... args) { return StringMemory.toLong(args[0].toString(), args[1].toBoolean()) != null ? Memory.TRUE : Memory.FALSE; } @Signature({ @Arg(value = "length", optional = @Optional("16")), @Arg(value = "set", optional = @Optional("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789")) }) public static Memory random(Environment env, Memory... args) { String set = args[1].toString(); int resultLen = args[0].toInteger(); int len = set.length(); if (len < 1) { return Memory.FALSE; } StringBuilder sb = new StringBuilder(resultLen); for (int i = 0; i < resultLen; i++) { sb.append(set.charAt(MathFunctions.RANDOM.nextInt(len))); } return StringMemory.valueOf(sb.toString()); } protected interface CharChecker { boolean check(char ch); } protected static Memory _checkChars(Memory string, CharChecker checker) { String s = string.toString(); int length = s.length(); if (length == 0) { return Memory.FALSE; } for (int i = 0; i < length; i++) { char ch = s.charAt(i); if (!checker.check(ch)) { return Memory.FALSE; } } return Memory.TRUE; } @FastMethod @Signature(@Arg("string")) public static Memory isLower(Environment env, Memory... args) { String s = args[0].toString(); int length = s.length(); if (length == 0) { return Memory.FALSE; } boolean letterExists = false; for (int i = 0; i < length; i++) { char ch = s.charAt(i); if (!Character.isLetter(ch)) continue; if (!Character.isLowerCase(ch)) { return Memory.FALSE; } letterExists = true; } return letterExists ? Memory.TRUE : Memory.FALSE; } @FastMethod @Signature(@Arg("string")) public static Memory isUpper(Environment env, Memory... args) { String s = args[0].toString(); int length = s.length(); if (length == 0) { return Memory.FALSE; } boolean letterExists = false; for (int i = 0; i < length; i++) { char ch = s.charAt(i); if (!Character.isLetter(ch)) continue; if (!Character.isUpperCase(ch)) { return Memory.FALSE; } letterExists = true; } return letterExists ? Memory.TRUE : Memory.FALSE; } @FastMethod @Signature({@Arg("string")}) public static Memory upperFirst(Environment env, Memory... args) { String s = args[0].toString(); if (s.length() > 0) { s = Character.toUpperCase(s.charAt(0)) + s.substring(1); } return StringMemory.valueOf(s); } @FastMethod @Signature({@Arg("string")}) public static Memory lowerFirst(Environment env, Memory... args) { String s = args[0].toString(); if (s.length() > 0) { s = Character.toLowerCase(s.charAt(0)) + s.substring(1); } return StringMemory.valueOf(s); } @FastMethod @Signature({ @Arg("string"), @Arg("subString"), @Arg(value = "offset", optional = @Optional("0")) }) public static Memory count(Environment env, Memory... args) { String s = args[0].toString(); String sub = args[1].toString(); int offset = args[2].toInteger(); int length = sub.length(); int count = 0; int sLength = s.length(); if (length == 1) { char subChar = sub.charAt(0); for (int i = offset; i < sLength; i++) { if (s.charAt(i) == subChar) { count++; } } } else if (length > 1) { for (int i = offset; i < sLength; i++) { if (s.startsWith(sub, i)) { count++; i += length; } } } return LongMemory.valueOf(count); } @FastMethod @Signature({@Arg("string"), @Arg("search")}) public static Memory contains(Environment env, Memory... args) { String s = args[0].toString(); return s.contains(args[1].toString()) ? Memory.TRUE : Memory.FALSE; } @Signature({@Arg("string")}) public static Memory format(Environment env, Memory... args) { String s = args[0].toString(); Object[] _args = new Object[args.length - 1]; for (int i = 1; i < args.length; i++) { Memory arg = args[i]; Object _arg = arg; switch (arg.getRealType()) { case INT: _arg = arg.toLong(); break; case DOUBLE: _arg = arg.toDouble(); break; case BOOL: _arg = arg.toBoolean(); break; } _args[i - 1] = _arg; } return StringMemory.valueOf(String.format(s, _args)); } @Signature({ @Arg(value = "value", optional = @Optional("null")) }) public static Memory uuid(Environment env, Memory... args) { Memory value = args[0]; String s; if (value.isNotNull()) { s = UUID.fromString(value.toString()).toString(); } else { s = UUID.randomUUID().toString(); } return StringMemory.valueOf(s); } @Signature({ @Arg("value"), @Arg(value = "algorithm", optional = @Optional("SHA-1")) }) public static Memory hash(Environment env, Memory... args) throws NoSuchAlgorithmException { MessageDigest messageDigest = MessageDigest.getInstance(args[1].toString()); messageDigest.update(args[0].getBinaryBytes(env.getDefaultCharset())); return StringMemory.valueOf(DigestUtils.bytesToHex(messageDigest.digest())); } @Signature({ @Arg(value = "string"), @Arg(value = "removeEmpty", optional = @Optional("false")) }) public static Memory lines(Environment env, Memory... args) { boolean removeEmpty = args[1].toBoolean(); Scanner scanner = new Scanner(args[0].toString()); ArrayMemory result = new ArrayMemory(); while (scanner.hasNextLine()) { String value = scanner.nextLine(); if (removeEmpty) { value = value.trim(); if (value.isEmpty()) { continue; } } result.add(value); } return result.toConstant(); } }