Я хотел бы поделиться методом генерации изображений-инвайтов почти без бд.

К этому изобретению меня натолкнула эта статья.



Я сегодня преподал статью о генерации инвайтов, но меня сразу заминусовали и я решил убрать статью в черновики.

Программа состоит из таких файлов:
  • generator.php — функция генератора
  • image.php — отображает картинку
  • index.php — морда
  • test.php — тестер

Принцип работы:
Картинка генерируется псевдослучайным способом, с некой закономерностью.
А тестер проверяет наличие этих закономерностей.

Ранее инвайт генерировался только по кодовому слову и по нему же проверялся, но как сказал devpony,

Так что так лучше.
index.php
<?
header('Content-Type: text/html; charset=utf-8');
?>

<fieldset>
   <legend>Get invite</legend>
	<button onclick="document.getElementById('img').src='image.php?rand='+(Math.floor(Math.random()*999))">Обновить</button>
	<br>
	<br> IMG:
	<img src="image.php?in=" id="img" onclick="window.open(this.src)" width="200" height="200">
	<br> Кликни по картинке чтобы скачать ее.
</fieldset>
<fieldset>
   <legend>Test invite</legend>
	<form method="post" enctype="multipart/form-data" action="test.php">
		<input type="hidden" name="UserID" value="<?php
			$ip = $_SERVER['REMOTE_ADDR'];
			$ua = $_SERVER['HTTP_USER_AGENT'];
			$d = getdate();

			$UserID = sha1($ip.$ua.$d["mday"].'Secret');
			$UserID = base64_encode($UserID);
			$UserID = rtrim($UserID,"=");
			echo $UserID;
?>">
		<input type="file" name="uploadfile" style="width:300px;height:100px;background:#fc0;">
		<br>
		<button type="submit">SUBMIT</button>
	</form>
</fieldset>

UserID — идентификатор, чтобы если одновременно кто будет загружать изображения — чтобы не переплутились.

image.php
<?
include('generator.php');//Подключаем генератор
header('Content-Disposition: attachment; filename="image.png"');//Чтобы изображене скачивалось сразу

header ("Content-type: image/png");//Тип изображения
imagepng(generateImage());//Отдаем изображение
?>


generator.php
<?
	function generateImage($str){
	$secret="SECRET_STRING";
	if(!isset($str)){
		$str = sha1(rand(-99999,99999).$secret);//Обычно - рандом
	}else{
		$str = sha1($str.$secret);
	}
	for($i=0;$i<6*$WH+1;$i++){
		$str .= sha1($str.$secret); 
	}

	$WH = 200;//Ширина и высота

	$im = imagecreatetruecolor($WH, $WH);
	$im1 = imagecreatetruecolor($WH, $WH);
	$im2 = imagecreatetruecolor($WH, $WH);
	$im3 = imagecreatetruecolor($WH, $WH);
	$im4 = imagecreatetruecolor($WH, $WH);//5 изображений

	$arr = array();
	for($i=0;$i<round(strlen($str)/6, 0);$i++){
		$arr[] = hex2rgb(substr($str, $i*6, 6)); //Генерируем цвета
	}

	for ($i=0;$i<$WH/2;$i++){
		$r = $arr[$i][0];
		$g = $arr[$i][1];
		$b = $arr[$i][2];

		$color = imagecolorallocate($im, $r, $g, $b);
		imagefilledrectangle($im1, $i, $i, $WH-$i, $WH-$i, $color);

		$color = imagecolorallocate($im, $b, $r, $g);
		imagefilledrectangle($im2, $i, $i, $WH-$i, $WH-$i, $color);

		$color = imagecolorallocate($im, $g, $r, $b);
		imagefilledrectangle($im3, $i, $i, $WH-$i, $WH-$i, $color);

		$color = imagecolorallocate($im, $r, $b, $g);
		imagefilledrectangle($im4, $i, $i, $WH-$i, $WH-$i, $color);
	}


	imagecopy($im, $im1, 0, 0, 0, 0, $WH/2, $WH/2);
	imagecopy($im, $im2, $WH/2, 0, $WH/2, 0, $WH/2, $WH/2);
	imagecopy($im, $im3, 0, $WH/2, 0, $WH/2, $WH/2, $WH/2);
	imagecopy($im, $im4, $WH/2, $WH/2, $WH/2, $WH/2, $WH, $WH);

	return $im;
}
	function hex2rgb($hex){
	$hex = str_replace("#", "", $hex);
		if(strlen($hex) == 3) {
			$r = hexdec(substr($hex,0,1).substr($hex,0,1));
			$g = hexdec(substr($hex,1,1).substr($hex,1,1));
			$b = hexdec(substr($hex,2,1).substr($hex,2,1));
		} else {
			$r = hexdec(substr($hex,0,2));
			$g = hexdec(substr($hex,2,2));
			$b = hexdec(substr($hex,4,2));
		}
		$rgb = array($r, $g, $b);
		return $rgb;
	}

Как мы видим, цвета «одинаковые», но перепутаны каналы.

test.php
<?php
error_reporting(0);
include('generator.php');

$UserID = rtrim(base64_encode(sha1($_POST['UserID'])), '=');
$name1 = './files/image1_'.$UserID.'.png';

if (copy($_FILES['uploadfile']['tmp_name'], $name1)){
	$im = imagecreatefrompng($name1);

	function getColor($im, $x, $y){
		$rgb = imagecolorat($im, $x, $y);
		$r = ($rgb >> 16) & 0xFF;
		$g = ($rgb >> 8) & 0xFF;
		$b = $rgb & 0xFF;
		return array($r, $g, $b);
	}

	$max_count = 0;
	$counted = 0;

	for($i=0;$i<100;$i++){
			$max_count++;
		if(
			getColor($im, 99, $i)[0] == getColor($im, 100, $i)[1] AND 
			getColor($im, 99, $i)[1] == getColor($im, 100, $i)[2] AND 
			getColor($im, 99, $i)[2] == getColor($im, 100, $i)[0] AND

			(getColor($im,  99, $i)[0] != getColor($im, 100, $i)[0] OR
			getColor($im,  99, $i)[1] != getColor($im,  100, $i)[1] OR
			getColor($im,  99, $i)[2] != getColor($im,  100, $i)[2])){
			$counted++;
		}
	}
	for($i=0;$i<100;$i++){
		$max_count++;
		if(
			getColor($im,  $i, 99)[0] == getColor($im, $i, 100)[1] AND 
			getColor($im,  $i, 99)[1] == getColor($im,  $i, 100)[0] AND 
			getColor($im,  $i, 99)[2] == getColor($im,  $i, 100)[2] AND

			(getColor($im,  $i, 99)[0] != getColor($im, $i, 100)[0] OR
			getColor($im,  $i, 99)[1] != getColor($im,  $i, 100)[1] OR
			getColor($im,  $i, 99)[2] != getColor($im,  $i, 100)[2])){
			$counted++;
		}
	}
	for($i=101;$i<200;$i++){
		$max_count++;
		if(
			getColor($im,  $i, 99)[0] == getColor($im, $i, 100)[1] AND 
			getColor($im,  $i, 99)[1] == getColor($im,  $i, 100)[0] AND 
			getColor($im,  $i, 99)[2] == getColor($im,  $i, 100)[2] AND

			(getColor($im,  $i, 99)[0] != getColor($im, $i, 100)[0] OR
			getColor($im,  $i, 99)[1] != getColor($im,  $i, 100)[1] OR
			getColor($im,  $i, 99)[2] != getColor($im,  $i, 100)[2])){
			$counted++;
		}
	}
	for($i=101;$i<200;$i++){
		$max_count++;
		if(
			getColor($im,  99, $i)[0] == getColor($im, 100, $i)[2] AND 
			getColor($im,  99, $i)[1] == getColor($im,  100, $i)[0] AND 
			getColor($im,  99, $i)[2] == getColor($im,  100, $i)[1] AND

			(getColor($im,  99, $i)[0] != getColor($im, 100, $i)[0] OR 
			getColor($im,  99, $i)[1] != getColor($im, 100, $i)[1] OR
			getColor($im,  99, $i)[2] != getColor($im, 100, $i)[2])){
			$counted++;
		}
	}

	if($counted>$max_count*0.9){//Если больше 90%
		echo " COMPLETE";
	}else{
		echo " FAILED";
	}
}else{
	echo "ERROR ".$_FILES['uploadfile']['error'];
}
?>

Здесь подробнее.
Эти 4 цикла — тестирование осей(место «стыка четырех частей») +проверка, чтобы они были неодинаковые.



Ну, вот и все.
Правда, есть некоторые минусы.
А именно, не предусмотрено защиту от повторного использования.
Можно использовать, например, перепцетивний хэш или придумать какое-то ограничение по времени.
Я над этим еще думаю.
Демо: http://app.blastorq.pp.ua/ImgInvite/(на украинском)
GitHub: https://github.com/da411d/ImgInviteGenerator

Если нашли неточность или ошибку — пишите мне, не сильно минусуйте
Как вам?

Проголосовало 173 человека. Воздержалось 133 человека.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Комментарии (18)


  1. NorthDakota
    14.01.2016 23:54
    +4

    psr'ы почитайте…


    1. andrewnester
      15.01.2016 10:17
      +2

      и правила русского языка автору тоже не помешают


      1. da411d
        15.01.2016 14:19

        Я из западной Украины. Стараюсь как могу. Юзаю Google Translate


  1. Londoner
    15.01.2016 00:27
    +3

    Наверное, это странный вопрос, но зачем нужно делать инвайты в виде изображений, а не ссылок?


    1. gltrinix
      15.01.2016 00:35
      +1

      …сказал пользователь Хабрахабра.
      [/sarcasm_mode]


      1. Londoner
        15.01.2016 00:44
        +10

        Да-да, зачем Хабрахабр это делает, я тоже не понимаю…


      1. Meklon
        15.01.2016 10:31
        +2

        Кстати, а зачем? Я такого не получал, когда инвайт получил в 2012.


    1. Alexufo
      15.01.2016 00:41

      Это тоже самое, что софтовому продукту рисуют картонную коробку при онлайн продаже. Чтобы понимал, что оно почти «держится в руках».


      1. Londoner
        15.01.2016 00:45
        +6

        … и потом не знаешь чего с этой коробкой делать — место занимает, а выкинуть жалко.


        1. Alexufo
          15.01.2016 00:47

          да нету этой коробки когда онлайн покупаешь, нету. а ее рисуют — как то нужно создать образ товара при продаже. Пусть образ будет «в коробке». Так и с инвайтом, просто такая вот надуманная ценность от ощущения секрета. Ссылку каждый может сделать, а если завернуть ее в коробочку :-)


  1. Zibx
    15.01.2016 02:45
    +3

    Ещё метод — генерируем любое изображение (вообще любое). Берём младшие биты цветов и радуемся вместе с наукой стеганографии. Запихиваем в эти биты (хоть кодовое слово, хоть хэш). В бд что-то всё равно хранить придётся, системы инвайтов без этого не работают, но тут можно будет легко записать и извлечь эту информацию и при этом всё будет завёрнуто в красивую шелестящую упаковку.


    1. da411d
      15.01.2016 14:28
      -4

      Хорошая мысль.
      Я также над этим думал


  1. bromzh
    15.01.2016 06:16
    +3

    Зачем всё это? Зачем куча кода (весьма посредственного, к тому же) без пояснений? И первая статья была точно такой же, зачем постить снова и снова?

    PS. Печально такие статьи видеть на хабре. Увы, всё меньше и меньше полезного тут появляется.
    Автор, то что ты интересуешься программированием, пытаешься придумать что-то своё и реализуешь это — это хорошо. Но не надо все свои идеи и их реализации вываливать на хабр. Сперва научись писать программы нормально: изучи алгоритмы, изучи инструменты языка и его style guide.


  1. sergiks
    15.01.2016 09:29
    +1

    Если уж изображения, то независимые от оцифровки. Т.е. чтобы их можно было отсканировать со смятой бумажки на плохом сканере в интернет-кафе в Найроби, загрузить в ВК/FB, где их пересэмплируют и уменьшат, открыть у себя на телефоне и показать повёрнутыми в 3D на неопределённый угол – и через дешёвую веб-камеру они однозначно правильно распознались и сработали только один раз.

    Плохой пример: одна жирная буква латинского алфавита. Большинство OCR справятся и «узнают» её после скана-из-Найроби-через-fb-и-телефон. Ну и дальше чуть усложнить, повысив ёмкость системы знаков до разумных пределов.


    1. gwer
      15.01.2016 09:59
      +2

      Этим изображением был QR-код.


      1. sergiks
        15.01.2016 10:36

        … и воспроизвести рисунком от руки )


        1. gwer
          15.01.2016 10:39

          При необходимости вполне реально. На листке в клеточку.


        1. Aclz
          15.01.2016 10:43

          QR или PDF417 нет, а вот, например, Dotcode можно:

          image