<commit_before><?php

namespace Awesomite\StackTrace;

use Awesomite\StackTrace\Arguments\Values\DeserializedValue;
use Awesomite\StackTrace\Arguments\Values\Value;
use Awesomite\StackTrace\Steps\Step;
use Awesomite\StackTrace\Steps\StepInterface;
use Awesomite\StackTrace\VarDumpers\LightVarDumper;
use Awesomite\StackTrace\VarDumpers\VarDumperInterface;
use Composer\Semver\Semver;

/**
 * @internal
 */
class StackTrace implements StackTraceInterface
{
    const VERSION = '0.5.0';
    const CONSTRAINTS_VERSION = '>=0.1.0 <0.5.0 || ^0.5.0';

    private $arrayStackTrace;

    private $withoutArgs = false;

    private $filesContents = array();

    private $unserialized = false;

    /**
     * @var VarDumperInterface
     */
    private $varDumper;

    public function __construct(array $arrayStackTrace)
    {
        $this->arrayStackTrace = $arrayStackTrace;
    }

    public function getIterator()
    {
        $result = array();
        foreach ($this->arrayStackTrace as $arrayStep) {
            $result[] = new Step($this->convertStep($arrayStep));
        }

        return new \ArrayIterator($result);
    }

    public function count()
    {
        return count($this->arrayStackTrace);
    }

    public function serialize()
    {
        if ($this->unserialized) {
            $steps = $this->arrayStackTrace;
        } else {
            $steps = array();
            foreach ($this->arrayStackTrace as $step) {
                $steps[] = $this->convertStep($step, true);
                $fileName = isset($step['file']) ? $step['file'] : false;
                if ($fileName && !isset($this->filesContents[$fileName]) && is_file($fileName)) {
                    $this->filesContents[$fileName] = file_get_contents($fileName);
                }
            }
        }

        return serialize(array(
            'steps' => $steps,
            'filesContents' => $this->filesContents,
            '__version' => static::VERSION,
        ));
    }

    public function unserialize($serialized)
    {
        $data = unserialize($serialized);
        if (!Semver::satisfies($data['__version'], static::CONSTRAINTS_VERSION)) {
            // @codeCoverageIgnoreStart
            $message = 'Cannot use incompatible version to unserialize stack trace (serialized by: %s, current: %s).';
            throw new \LogicException(sprintf($message, $data['__version'], static::VERSION));
            // @codeCoverageIgnoreEnd
        }
        $this->arrayStackTrace = $data['steps'];
        $this->unserialized = true;
        $this->filesContents = isset($data['filesContents']) ? $data['filesContents'] : array();
    }

    public function __toString()
    {
        $result = array();
        $stepNumber = 0;
        $this->withoutArgs = true;
        foreach ($this as $step) {
            /** @var StepInterface $step */
            $line = '#' . $stepNumber;
            if ($step->hasCalledFunction()) {
                $line .= ' ' . $step->getCalledFunction()->getName() . '()';
            }
            if ($step->hasPlaceInCode()) {
                $placeInCode = $step->getPlaceInCode();
                $line .= ' at ' . $placeInCode->getFileName() . ':' . $placeInCode->getLineNumber();
            }

            $result[] = $line;
            $stepNumber++;
        }
        $this->withoutArgs = false;

        return implode("\n", $result);
    }

    public function getId()
    {
        $lines = array();
        $this->withoutArgs = true;
        foreach ($this as $key => $step) {
            /** @var StepInterface $step */
            $line = $key . '#';

            if ($step->hasCalledFunction()) {
                $line .= $step->getCalledFunction()->getName();
            }

            if ($step->hasPlaceInCode()) {
                $placeInCode = $step->getPlaceInCode();
                $line .= '@' . $placeInCode->getFileName() . ':' . $placeInCode->getLineNumber();
            }

            $lines[] = $line;
        }
        $this->withoutArgs = false;

        return md5(implode('__', $lines));
    }

    public function setVarDumper(VarDumperInterface $varDumper)
    {
        $this->varDumper = $varDumper;
    }

    private function getVarDumper()
    {
        return $this->varDumper ?: new LightVarDumper();
    }

    private function convertStep(array $step, $toSerialize = false)
    {
        $result = array();<commit_msg>Do not create many times the same object<commit_after><?php

namespace Awesomite\StackTrace;

use Awesomite\StackTrace\Arguments\Values\DeserializedValue;
use Awesomite\StackTrace\Arguments\Values\Value;
use Awesomite\StackTrace\Steps\Step;
use Awesomite\StackTrace\Steps\StepInterface;
use Awesomite\StackTrace\VarDumpers\LightVarDumper;
use Awesomite\StackTrace\VarDumpers\VarDumperInterface;
use Composer\Semver\Semver;

/**
 * @internal
 */
class StackTrace implements StackTraceInterface
{
    const VERSION = '0.5.0';
    const CONSTRAINTS_VERSION = '>=0.1.0 <0.5.0 || ^0.5.0';

    private $arrayStackTrace;

    private $withoutArgs = false;

    private $filesContents = array();

    private $unserialized = false;

    /**
     * @var VarDumperInterface
     */
    private $varDumper;

    public function __construct(array $arrayStackTrace)
    {
        $this->arrayStackTrace = $arrayStackTrace;
    }

    public function getIterator()
    {
        $result = array();
        foreach ($this->arrayStackTrace as $arrayStep) {
            $result[] = new Step($this->convertStep($arrayStep));
        }

        return new \ArrayIterator($result);
    }

    public function count()
    {
        return count($this->arrayStackTrace);
    }

    public function serialize()
    {
        if ($this->unserialized) {
            $steps = $this->arrayStackTrace;
        } else {
            $steps = array();
            foreach ($this->arrayStackTrace as $step) {
                $steps[] = $this->convertStep($step, true);
                $fileName = isset($step['file']) ? $step['file'] : false;
                if ($fileName && !isset($this->filesContents[$fileName]) && is_file($fileName)) {
                    $this->filesContents[$fileName] = file_get_contents($fileName);
                }
            }
        }

        return serialize(array(
            'steps' => $steps,
            'filesContents' => $this->filesContents,
            '__version' => static::VERSION,
        ));
    }

    public function unserialize($serialized)
    {
        $data = unserialize($serialized);
        if (!Semver::satisfies($data['__version'], static::CONSTRAINTS_VERSION)) {
            // @codeCoverageIgnoreStart
            $message = 'Cannot use incompatible version to unserialize stack trace (serialized by: %s, current: %s).';
            throw new \LogicException(sprintf($message, $data['__version'], static::VERSION));
            // @codeCoverageIgnoreEnd
        }
        $this->arrayStackTrace = $data['steps'];
        $this->unserialized = true;
        $this->filesContents = isset($data['filesContents']) ? $data['filesContents'] : array();
    }

    public function __toString()
    {
        $result = array();
        $stepNumber = 0;
        $this->withoutArgs = true;
        foreach ($this as $step) {
            /** @var StepInterface $step */
            $line = '#' . $stepNumber;
            if ($step->hasCalledFunction()) {
                $line .= ' ' . $step->getCalledFunction()->getName() . '()';
            }
            if ($step->hasPlaceInCode()) {
                $placeInCode = $step->getPlaceInCode();
                $line .= ' at ' . $placeInCode->getFileName() . ':' . $placeInCode->getLineNumber();
            }

            $result[] = $line;
            $stepNumber++;
        }
        $this->withoutArgs = false;

        return implode("\n", $result);
    }

    public function getId()
    {
        $lines = array();
        $this->withoutArgs = true;
        foreach ($this as $key => $step) {
            /** @var StepInterface $step */
            $line = $key . '#';

            if ($step->hasCalledFunction()) {
                $line .= $step->getCalledFunction()->getName();
            }

            if ($step->hasPlaceInCode()) {
                $placeInCode = $step->getPlaceInCode();
                $line .= '@' . $placeInCode->getFileName() . ':' . $placeInCode->getLineNumber();
            }

            $lines[] = $line;
        }
        $this->withoutArgs = false;

        return md5(implode('__', $lines));
    }

    public function setVarDumper(VarDumperInterface $varDumper)
    {
        $this->varDumper = $varDumper;
    }

    private function getVarDumper()
    {
        if (is_null($this->varDumper)) {
            $this->varDumper = new LightVarDumper();
        }

        return $this->varDumper;
    }

    private function convertStep(array $step, $toSerialize = false)
    {
        $result = array();
{'fraction_non_alphanumeric': 0.1464782413638403, 'fraction_numerical': 0.0031404217137729925, 'mean_word_length': 2.1091352859135286, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2125138', 'n_tokens_mistral': 2559, 'n_tokens_neox': 2511, 'n_words': 485}