public static Memory callStatic(Environment env, TraceInfo trace,
                                    String className, String methodName, String originClassName, String originMethodName,
                                    Memory[] args, MethodCallCache callCache, int cacheIndex)
            throws Throwable {
        if (callCache != null) {
            MethodEntity entity = callCache.get(env, cacheIndex);
            if (entity != null) {
                return callStatic(env, trace, entity, originClassName, args, false);
            }
        }

        ClassEntity classEntity = env.fetchClass(originClassName, className, true);

        MethodEntity method = classEntity == null ? null : classEntity.findMethod(methodName);
        Memory[] passed = null;
        boolean isMagic = false;

        if (method == null) {
            IObject maybeObject = env.getLateObject();
            if (maybeObject != null && maybeObject.getReflection().isInstanceOf(classEntity))
                return ObjectInvokeHelper.invokeMethod(
                        new ObjectMemory(maybeObject), originMethodName, methodName, env, trace, args
                );

            if (classEntity != null && classEntity.methodMagicCallStatic != null) {
                method = classEntity.methodMagicCallStatic;
                isMagic = true;
                passed = new Memory[]{
                        new StringMemory(originMethodName),
                        ArrayMemory.of(args)
                };
            } else {
                if (classEntity == null) {
                    env.error(trace, Messages.ERR_CLASS_NOT_FOUND.fetch(originClassName));
                    return Memory.NULL;
                }
            }
        }

        if (method == null) {
            env.error(trace, Messages.ERR_CALL_TO_UNDEFINED_METHOD.fetch(originClassName + "::" + originMethodName));
            return Memory.NULL;
        }

        if (!method.isStatic()) {
            IObject maybeObject = env.getLateObject();
            if (maybeObject != null
                    && maybeObject.getReflection().isInstanceOf(classEntity))
                return ObjectInvokeHelper.invokeMethod(maybeObject, method, env, trace, args, true);

            env.error(trace,
                    ErrorType.E_STRICT,
                    Messages.ERR_NON_STATIC_METHOD_CALLED_DYNAMICALLY,
                    originClassName, originMethodName
            );
        }

        if (callCache != null && !isMagic) {
            callCache.put(env, cacheIndex, method);
        }

        checkAccess(env, trace, method);

        if (passed == null) {
            passed = InvokeArgumentHelper.makeArguments(
                    env, args, method.getParameters(), originClassName, originMethodName, originClassName, trace
            );
        }

        Memory result = method.getImmutableResultTyped(env, trace);

        if (result != null) {
            return result;
        }

        CallStack callStack = trace != null && method.isUsesStackTrace() ? env.getCallStack() : null;

        try {
            if (callStack != null) {
                callStack.push(trace, (IObject) null, args, originMethodName, method.getClazz(), classEntity);
            }

            return method.invokeStatic(env, trace, passed);
        } finally {
            if (callStack != null) {
                callStack.pop();
            }
        }
    }
Method is invoked via bytecode

@throws Throwable
jphp-runtime/src/php/runtime/invoke/InvokeHelper.java