<?php
/**
 * GlobalState
 *
 * Copyright (c) 2001-2014, Sebastian Bergmann <sebastian@phpunit.de>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 *   * Neither the name of Sebastian Bergmann nor the names of his
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @author     Sebastian Bergmann <sebastian@phpunit.de>
 * @copyright  2001-2014 Sebastian Bergmann <sebastian@phpunit.de>
 * @license    http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
 * @link       http://www.github.com/sebastianbergmann/global-state
 */

namespace SebastianBergmann\GlobalState;

use ReflectionProperty;

/**
 * Restorer of snapshots of global state.
 *
 * @author     Sebastian Bergmann <sebastian@phpunit.de>
 * @copyright  2001-2014 Sebastian Bergmann <sebastian@phpunit.de>
 * @license    http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
 * @link       http://www.github.com/sebastianbergmann/global-state
 */
class Restorer {
	/**
	 * Deletes function definitions that are not defined in a snapshot.
	 *
	 * @param  Snapshot $snapshot
	 *
	 * @throws RuntimeException when the uopz_delete() function is not available
	 * @see    https://github.com/krakjoe/uopz
	 */
	public function restoreFunctions ( Snapshot $snapshot ) {
		if ( ! function_exists( 'uopz_delete' ) ) {
			throw new RuntimeException( 'The uopz_delete() function is required for this operation' );
		}

		$functions = get_defined_functions();

		foreach ( array_diff( $functions['user'], $snapshot->functions() ) as $function ) {
			uopz_delete( $function );
		}
	}

	/**
	 * Restores all global and super-global variables from a snapshot.
	 *
	 * @param Snapshot $snapshot
	 */
	public function restoreGlobalVariables ( Snapshot $snapshot ) {
		$superGlobalArrays = $snapshot->superGlobalArrays();

		foreach ( $superGlobalArrays as $superGlobalArray ) {
			$this->restoreSuperGlobalArray( $snapshot, $superGlobalArray );
		}

		$globalVariables = $snapshot->globalVariables();

		foreach ( array_keys( $GLOBALS ) as $key ) {
			if ( $key != 'GLOBALS' &&
			     ! in_array( $key, $superGlobalArrays ) &&
			     ! $snapshot->blacklist()->isGlobalVariableBlacklisted( $key )
			) {
				if ( isset( $globalVariables[ $key ] ) ) {
					$GLOBALS[ $key ] = $globalVariables[ $key ];
				} else {
					unset( $GLOBALS[ $key ] );
				}
			}
		}
	}

	/**
	 * Restores all static attributes in user-defined classes from this snapshot.
	 *
	 * @param Snapshot $snapshot
	 */
	public function restoreStaticAttributes ( Snapshot $snapshot ) {
		foreach ( $snapshot->staticAttributes() as $className => $staticAttributes ) {
			foreach ( $staticAttributes as $name => $value ) {
				$reflector = new ReflectionProperty( $className, $name );
				$reflector->setAccessible( true );
				$reflector->setValue( $value );
			}
		}
	}

	/**
	 * Restores a super-global variable array from this snapshot.
	 *
	 * @param Snapshot $snapshot
	 * @param          $superGlobalArray
	 */
	private function restoreSuperGlobalArray ( Snapshot $snapshot, $superGlobalArray ) {
		$superGlobalVariables = $snapshot->superGlobalVariables();

		if ( isset( $GLOBALS[ $superGlobalArray ] ) &&
		     is_array( $GLOBALS[ $superGlobalArray ] ) &&
		     isset( $superGlobalVariables[ $superGlobalArray ] )
		) {
			$keys = array_keys(
				array_merge(
					$GLOBALS[ $superGlobalArray ],
					$superGlobalVariables[ $superGlobalArray ]
				)
			);

			foreach ( $keys as $key ) {
				if ( isset( $superGlobalVariables[ $superGlobalArray ][ $key ] ) ) {
					$GLOBALS[ $superGlobalArray ][ $key ] = $superGlobalVariables[ $superGlobalArray ][ $key ];
				} else {
					unset( $GLOBALS[ $superGlobalArray ][ $key ] );
				}
			}
		}
	}
}
