<?php
/**
 * PHP compatibility helpers.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChanges\Support;

/**
 * Centralizes helpers that differ across supported PHP versions.
 */
final class PhpCompat {
	/**
	 * Returns whether a string contains a substring.
	 *
	 * @param mixed $haystack String under test.
	 * @param mixed $needle   Expected substring.
	 */
	public static function strContains( $haystack, $needle ): bool {
		return '' === $needle || false !== strpos( (string) $haystack, (string) $needle );
	}

	/**
	 * Returns whether a string starts with a prefix.
	 *
	 * @param mixed $haystack String under test.
	 * @param mixed $needle   Expected prefix.
	 */
	public static function strStartsWith( $haystack, $needle ): bool {
		$haystack = (string) $haystack;
		$needle   = (string) $needle;

		return '' === $needle || 0 === strpos( $haystack, $needle );
	}

	/**
	 * Returns whether a string ends with a suffix.
	 *
	 * @param mixed $haystack String under test.
	 * @param mixed $needle   Expected suffix.
	 */
	public static function strEndsWith( $haystack, $needle ): bool {
		$haystack = (string) $haystack;
		$needle   = (string) $needle;

		if ( '' === $needle ) {
			return true;
		}

		return (string) substr( $haystack, -strlen( $needle ) ) === $needle;
	}

	/**
	 * Returns whether the array is a sequential list.
	 *
	 * @param mixed $value Value under test.
	 */
	public static function isList( $value ): bool {
		if ( ! is_array( $value ) ) {
			return false;
		}

		$index = 0;

		foreach ( $value as $key => $ignored ) {
			if ( $key !== $index ) {
				return false;
			}

			++$index;
		}

		return true;
	}

	/**
	 * Returns a readable debug type name for the current runtime.
	 *
	 * @param mixed $value Value under test.
	 */
	public static function getDebugType( $value ): string {
		if ( function_exists( 'get_debug_type' ) ) {
			return get_debug_type( $value );
		}

		if ( null === $value ) {
			return 'null';
		}

		if ( is_object( $value ) ) {
			return get_class( $value );
		}

		return gettype( $value );
	}

	/**
	 * Returns whether a runtime method exists without relying on current static stubs.
	 *
	 * @param mixed  $object_or_class Object or class name under test.
	 * @param string $method          Method name.
	 */
	public static function methodExists( $object_or_class, string $method ): bool {
		return method_exists( $object_or_class, $method );
	}
}
