<commit_before><?php namespace WP_CLI_Dotenv_Command;

/**
 * Class Dotenv_File
 * @package WP_CLI_Dotenv_Command
 */
class Dotenv_File
{
    /**
     * Absolute path to file
     * @var string
     */
    protected $filepath;
    /**
     * File name
     * @var string
     */
    protected $filename;
    /**
     * File lines
     * @var array
     */
    protected $lines = [];

    /**
     * Single line format
     */
    const LINE_FORMAT = '%s=%s';

    /**
     * Pattern to match a var definition
     */
    const PATTERN_KEY_CAPTURE_FORMAT = '/^%s(\s+)?=/';

    const QUOTE_SINGLE = '\'';

    const QUOTE_DOUBLE = '"';

    /**
     * Dotenv_File constructor.
     *
     * @param $filepath
     */
    public function __construct($filepath)
    {
        $this->filepath = $filepath;
        $this->filename = basename($filepath);
    }

    /**
     * @param $filepath
     *
     * @return static
     */
    public static function at($filepath)
    {
        $dotenv = new static($filepath);

        if (! $dotenv->is_readable()) {
            throw new \RuntimeException("File not readable at $filepath");
        }

        return $dotenv;
    }

    /**
     * @param $filepath
     *
     * @return Dotenv_File
     */
    public static function writable($filepath)
    {
        $dotenv = static::at($filepath);

        if (! $dotenv->is_writable()) {
            throw new \RuntimeException("File not writable at $filepath");
        }

        return $dotenv;
    }


    /**
     * @param $filepath
     *
     * @return static
     */
    public static function create($filepath)
    {
        $dotenv = new static($filepath);

        if ( ! is_dir(dirname($filepath))) {
            mkdir(dirname($filepath), 0755, true);
        }
        if ( ! $dotenv->exists()) {
            touch($filepath);
        }

        return $dotenv;
    }

    /**
     * @return string
     */
    public function get_filepath()
    {
        return $this->filepath;
    }

    /**
     * @return string
     */
    public function get_filename()
    {
        return $this->filename;
    }

    /**
     * @return bool
     */
    public function exists()
    {
        return file_exists($this->filepath);
    }

    /**
     * Whether the file exists and is readable
     *
     * @return bool
     */
    public function is_readable()
    {
        return is_readable($this->filepath);
    }

    /**
     * @return bool
     */
    public function is_writable()
    {
        return is_writable($this->filepath);
    }

    /**
     * @param $value
     */
    public function add_line($value)
    {
        array_push($this->lines, (string)$value);
    }

    /**
     * @param $key
     *
     * @return string
     */
    public function get_pattern_for_key($key)
    {
        $preg_key = preg_quote(trim($key), '/');

        return sprintf(static::PATTERN_KEY_CAPTURE_FORMAT, $preg_key);
    }

    /**
     * @return $this
     */
    public function load()
    {
        $this->lines = file($this->filepath, FILE_IGNORE_NEW_LINES);

        return $this;
    }

    /**
     * @return int
     */
    public function save()
    {
        $contents = join(PHP_EOL, $this->lines) . PHP_EOL;

        return file_put_contents($this->filepath, $contents);
    }

    /**
     * @return int
     */
    public function size()
    {
        return count($this->lines);
    }

    /**
     * Get the value for a key
     *
     * Ex using our format:
     * KEY='VALUE'
     *
     * @param $key
     *
     * @return bool|null|string     string value,
     *                              false if line does not have `=`, or null if
     *                              null if no match was found
     */
    public function get($key)
    {
        foreach ($this->lines as &$line) {
            if ($this->is_key_match($key, $line)) {
                return $this->parse_line_value($line);
            }
        }

        // if we got here, the key wasn't matched
        return null;
    }

    /**
     * @param $key
     * @param $value
     *
     * @return bool
     */
    public function set($key, $value)
    {
        $set      = $replaced = false;
        $new_line = format_line($key, $value);

        foreach ($this->lines as &$line) {
            if ($this->is_key_match($key, $line)) {
                $line = $new_line;
                $set  = $replaced = true;
            }
        }

        // if it wasn't replaced, append it
        if ( ! $replaced) {
            $this->add_line($new_line);
            $set = true;
        }

        return $set;
    }

    /**
     * @param $key
     *
     * @return int Lines removed
     */
    public function remove($key)
    {
        $removed = 0;

        $this->lines = $this->filter(function ($line) use ($key, &$removed) {
            if ($this->is_key_match($key, $line)) {
                $removed++;

                return false;
            }

            return true;
        });

        return $removed;
    }

    /**
     * @param callable $callback
     *
     * @return array
     */
    public function map(callable $callback)
    {
        return array_map($callback, $this->lines);
    }

    /**
     * @param callable $callback
     *
     * @return $this
     */
    public function transform(callable $callback)
    {
        $this->lines = $this->map($callback);

        return $this;
    }

    /**
     * @param callable $callback
     *
     * @return array
     */
    public function filter($callback = null)
    {
        return array_filter($this->lines, $callback);
    }

    /**
     * @param $line
     *
     * @return bool|string
     */
    protected function parse_line_value($line)
    {
        // key = value
        // key=value
        // key = "value"
        // key='value' << our format

        if ( ! strpos($line, '=')) {
            return false;
        }

        // break the line at the = into 2 pieces (key, value)
        $pieces = explode('=', $line, 2);
        // clean off any extra whitespace or wrapping quotes
        $value = trim(array_pop($pieces), '\'""');

        return $value;
    }

    /**
     * @param $key
     * @param $line
     *
     * @return int
     */
    protected function is_key_match($key, $line)
    {
        return preg_match($this->get_pattern_for_key($key), $line);
    }

    /**
     * Whether or not the file defines the given key
     *
     * @param $key
     *
     * @return bool
     */
    public function has_key($key)
    {
        foreach ($this->lines as $line) {
            if ($this->is_key_match($key, $line)) {
                return true;
            }
        }

        return false;<commit_msg>refactor parse_line_value to use clean_quotes<commit_after><?php namespace WP_CLI_Dotenv_Command;

/**
 * Class Dotenv_File
 * @package WP_CLI_Dotenv_Command
 */
class Dotenv_File
{
    /**
     * Absolute path to file
     * @var string
     */
    protected $filepath;
    /**
     * File name
     * @var string
     */
    protected $filename;
    /**
     * File lines
     * @var array
     */
    protected $lines = [];

    /**
     * Single line format
     */
    const LINE_FORMAT = '%s=%s';

    /**
     * Pattern to match a var definition
     */
    const PATTERN_KEY_CAPTURE_FORMAT = '/^%s(\s+)?=/';

    const QUOTE_SINGLE = '\'';

    const QUOTE_DOUBLE = '"';

    /**
     * Dotenv_File constructor.
     *
     * @param $filepath
     */
    public function __construct($filepath)
    {
        $this->filepath = $filepath;
        $this->filename = basename($filepath);
    }

    /**
     * @param $filepath
     *
     * @return static
     */
    public static function at($filepath)
    {
        $dotenv = new static($filepath);

        if (! $dotenv->is_readable()) {
            throw new \RuntimeException("File not readable at $filepath");
        }

        return $dotenv;
    }

    /**
     * @param $filepath
     *
     * @return Dotenv_File
     */
    public static function writable($filepath)
    {
        $dotenv = static::at($filepath);

        if (! $dotenv->is_writable()) {
            throw new \RuntimeException("File not writable at $filepath");
        }

        return $dotenv;
    }


    /**
     * @param $filepath
     *
     * @return static
     */
    public static function create($filepath)
    {
        $dotenv = new static($filepath);

        if ( ! is_dir(dirname($filepath))) {
            mkdir(dirname($filepath), 0755, true);
        }
        if ( ! $dotenv->exists()) {
            touch($filepath);
        }

        return $dotenv;
    }

    /**
     * @return string
     */
    public function get_filepath()
    {
        return $this->filepath;
    }

    /**
     * @return string
     */
    public function get_filename()
    {
        return $this->filename;
    }

    /**
     * @return bool
     */
    public function exists()
    {
        return file_exists($this->filepath);
    }

    /**
     * Whether the file exists and is readable
     *
     * @return bool
     */
    public function is_readable()
    {
        return is_readable($this->filepath);
    }

    /**
     * @return bool
     */
    public function is_writable()
    {
        return is_writable($this->filepath);
    }

    /**
     * @param $value
     */
    public function add_line($value)
    {
        array_push($this->lines, (string)$value);
    }

    /**
     * @param $key
     *
     * @return string
     */
    public function get_pattern_for_key($key)
    {
        $preg_key = preg_quote(trim($key), '/');

        return sprintf(static::PATTERN_KEY_CAPTURE_FORMAT, $preg_key);
    }

    /**
     * @return $this
     */
    public function load()
    {
        $this->lines = file($this->filepath, FILE_IGNORE_NEW_LINES);

        return $this;
    }

    /**
     * @return int
     */
    public function save()
    {
        $contents = join(PHP_EOL, $this->lines) . PHP_EOL;

        return file_put_contents($this->filepath, $contents);
    }

    /**
     * @return int
     */
    public function size()
    {
        return count($this->lines);
    }

    /**
     * Get the value for a key
     *
     * Ex using our format:
     * KEY='VALUE'
     *
     * @param $key
     *
     * @return bool|null|string     string value,
     *                              false if line does not have `=`, or null if
     *                              null if no match was found
     */
    public function get($key)
    {
        foreach ($this->lines as &$line) {
            if ($this->is_key_match($key, $line)) {
                return $this->parse_line_value($line);
            }
        }

        // if we got here, the key wasn't matched
        return null;
    }

    /**
     * @param $key
     * @param $value
     *
     * @return bool
     */
    public function set($key, $value)
    {
        $set      = $replaced = false;
        $new_line = format_line($key, $value);

        foreach ($this->lines as &$line) {
            if ($this->is_key_match($key, $line)) {
                $line = $new_line;
                $set  = $replaced = true;
            }
        }

        // if it wasn't replaced, append it
        if ( ! $replaced) {
            $this->add_line($new_line);
            $set = true;
        }

        return $set;
    }

    /**
     * @param $key
     *
     * @return int Lines removed
     */
    public function remove($key)
    {
        $removed = 0;

        $this->lines = $this->filter(function ($line) use ($key, &$removed) {
            if ($this->is_key_match($key, $line)) {
                $removed++;

                return false;
            }

            return true;
        });

        return $removed;
    }

    /**
     * @param callable $callback
     *
     * @return array
     */
    public function map(callable $callback)
    {
        return array_map($callback, $this->lines);
    }

    /**
     * @param callable $callback
     *
     * @return $this
     */
    public function transform(callable $callback)
    {
        $this->lines = $this->map($callback);

        return $this;
    }

    /**
     * @param callable $callback
     *
     * @return array
     */
    public function filter($callback = null)
    {
        return array_filter($this->lines, $callback);
    }

    /**
     *
     * key = value
     * key=value
     * key = "value"
     * key='value'
     *
     * @param $line
     *
     * @return bool|string
     */
    protected function parse_line_value($line)
    {
        if ( ! strpos($line, '=')) {
            return false;
        }

        // break the line at the = into 2 pieces (key, value)
        $pieces = explode('=', $line, 2);
        // clean off any extra whitespace or wrapping quotes
        return static::clean_quotes(end($pieces));
    }

    /**
     * @param $key
     * @param $line
     *
     * @return int
     */
    protected function is_key_match($key, $line)
    {
        return preg_match($this->get_pattern_for_key($key), $line);
    }

    /**
     * Whether or not the file defines the given key
     *
     * @param $key
     *
     * @return bool
     */
    public function has_key($key)
    {
        foreach ($this->lines as $line) {
            if ($this->is_key_match($key, $line)) {
                return true;
            }
        }

        return false;
{'fraction_non_alphanumeric': 0.13654348637551325, 'fraction_numerical': 0.0010451661067562524, 'mean_word_length': 1.5204139228598306, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6395360', 'n_tokens_mistral': 4229, 'n_tokens_neox': 4057, 'n_words': 913}