Current File : /home/jvzmxxx/wiki1/extensions/EventLogging/includes/EventLogging.php
<?php
/**
 * PHP API for logging events
 *
 * @file
 * @ingroup Extensions
 * @ingroup EventLogging
 *
 * @author Ori Livneh <ori@wikimedia.org>
 */

class EventLogging {

	/** @var int flag indicating the user-agent should not be logged. **/
	const OMIT_USER_AGENT = 2;

	/**
	 * Transfer small data asynchronously using an HTTP POST.
	 * This is meant to match the Navigator.sendBeacon() API.
	 *
	 * @see https://w3c.github.io/beacon/#sec-sendBeacon-method
	 */
	public static function sendBeacon( $url, array $data = [] ) {
		DeferredUpdates::addCallableUpdate( function() use ( $url, $data ) {
			$options = $data ? [ 'postData' => $data ] : [];
			return Http::post( $url, $options );
		} );

		return true;
	}

	/**
	 * Emit an event via a sendBeacon POST to the event beacon endpoint.
	 *
	 * @param string $schemaName Schema name.
	 * @param int $revId revision ID of schema.
	 * @param array $event Map of event keys/vals.
	 * @param int $options Bitmask consisting of EventLogging::OMIT_USER_AGENT.
	 * @return bool: Whether the event was logged.
	 */
	static function logEvent( $schemaName, $revId, $event, $options = 0 ) {
		global $wgDBname, $wgEventLoggingBaseUri;

		if ( !$wgEventLoggingBaseUri ) {
			return false;
		}

		$remoteSchema = new RemoteSchema( $schemaName, $revId );
		$schema = $remoteSchema->get();

		try {
			$isValid = is_array( $schema ) && self::schemaValidate( $event, $schema );
		} catch ( JsonSchemaException $e ) {
			$isValid = false;
		}

		$encapsulated = [
			'event'            => $event,
			'schema'           => $schemaName,
			'revision'         => $revId,
			'clientValidated'  => $isValid,
			'wiki'             => $wgDBname,
		];
		if ( isset( $_SERVER[ 'HTTP_HOST' ] ) ) {
			$encapsulated[ 'webHost' ] = $_SERVER[ 'HTTP_HOST' ];
		}
		if ( !( $options & self::OMIT_USER_AGENT ) && !empty( $_SERVER[ 'HTTP_USER_AGENT' ] ) ) {
			$encapsulated[ 'userAgent' ] = $_SERVER[ 'HTTP_USER_AGENT' ];
		}

		$json = static::serializeEvent( $encapsulated );
		$url = $wgEventLoggingBaseUri . '?' . rawurlencode( $json ) . ';';
		return self::sendBeacon( $url );
	}

	/**
	 *
	 * Converts the encapsulated event from an object to a string.
	 *
	 * @param array $encapsulatedEvent Encapsulated event
	 * @return string $json
	**/
	static function serializeEvent( $encapsulatedEvent ) {

		$event = $encapsulatedEvent['event'];

		if ( count( $event ) === 0 ) {
			// Ensure empty events are serialized as '{}' and not '[]'.
			$event = (object)$event;
		}

		$encapsulatedEvent['event'] = $event;

		// To make the resultant JSON easily extracted from a row of
		// space-separated values, we replace literal spaces with unicode
		// escapes. This is permitted by the JSON specs.
		$json = str_replace( ' ', '\u0020', FormatJson::encode( $encapsulatedEvent ) );

		return $json;
	}

	/**
	 * Validates object against JSON Schema.
	 *
	 * @throws JsonSchemaException: If the object fails to validate.
	 * @param array $object Object to be validated.
	 * @param array $schema Schema to validate against (default: JSON Schema).
	 * @return bool: True.
	 */
	public static function schemaValidate( $object, $schema = null ) {
		if ( $schema === null ) {
			// Default to JSON Schema
			$json = file_get_contents( dirname( __DIR__ ) . '/schemas/schemaschema.json' );
			$schema = FormatJson::decode( $json, true );
		}

		// We depart from the JSON Schema specification in disallowing by default
		// additional event fields not mentioned in the schema.
		// See <https://bugzilla.wikimedia.org/show_bug.cgi?id=44454> and
		// <https://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.4>.
		if ( !array_key_exists( 'additionalProperties', $schema ) ) {
			$schema[ 'additionalProperties' ] = false;
		}

		$root = new JsonTreeRef( $object );
		$root->attachSchema( $schema );
		return $root->validate();
	}
}