<?php
/**
 * Encodes email addresses.
 *
 * @package rkv
 */

namespace RKV\Theme;

/**
 * Email_Encoder class.
 *
 * Encodes emails.
 */
class EmailEncoder {

	/**
	 * The regex to use for the match.
	 */
	const REGEX = '{
		(?:mailto:)?
		(?:
			[-!#$%&*+/=?^_`.{|}~\w\x80-\xFF]+
		|
			".*?"
		)
		\@
		(?:
			[-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+
		|
			\[[\d.a-fA-F:]+\]
		)
	}xi';

	/**
	 * Searches for plain email addresses in given $string and encodes them.
	 *
	 * Regular expression is based on based on John Gruber's Markdown.
	 * http://daringfireball.net/projects/markdown/
	 *
	 * @param string $string Text with email addresses to encode.
	 *
	 * @return string Given text with encoded email addresses.
	 */
	public function emails( $string ) {
		// abort if `$string` isn't a string.
		if ( ! is_string( $string ) ) {
			return $string;
		}

		// `$string` doesn't contain a @-sign.
		if ( strpos( $string, '@' ) === false ) {
			return $string;
		}

		return preg_replace_callback( static::REGEX, [ $this, 'callback' ], $string );
	}

	/**
	 * Callback fro preg_replace_callback.
	 *
	 * @param array $matches The matches.
	 * @return string
	 */
	public function callback( $matches ) {
		return $this->str( $matches[0] );
	}

	/**
	 * Encodes each character of the given string as either a decimal
	 * or hexadecimal entity, in the hopes of foiling most email address
	 * harvesting bots.
	 *
	 * Based on Michel Fortin's PHP Markdown:
	 *   http://michelf.com/projects/php-markdown/
	 * Which is based on John Gruber's original Markdown:
	 *   http://daringfireball.net/projects/markdown/
	 * Whose code is based on a filter by Matthew Wickline, posted to
	 * the BBEdit-Talk with some optimizations by Milian Wolff.
	 *
	 * @param string $string Text to encode.
	 * @param bool   $hex Whether to use hex entities as well.
	 *
	 * @return string Encoded given text.
	 */
	public function str( $string, $hex = false ) {
		$chars = str_split( $string );

		foreach ( $chars as $key => $char ) {
			$ord = ord( $char );

			if ( $ord < 128 ) { // ignore non-ascii chars.
				$r = wp_rand( 0, 100 ); // pseudo "random function".

				if ( $r > 75 && '@' !== $char && '.' !== $char ) {
					// plain character (not encoded), except @-signs and dots.
					continue;
				} elseif ( $hex && $r < 25 ) {
					$chars[ $key ] = '%' . bin2hex( $char ); // hex.
				} elseif ( $r < 45 ) {
					$chars[ $key ] = '&#x' . dechex( $ord ) . ';'; // hexadecimal.
				} else {
					$chars[ $key ] = "&#{$ord};"; // decimal (ascii).
				}
			}
		}

		return implode( '', $chars );
	}
}
