PageRenderTime 247ms CodeModel.GetById 98ms app.highlight 42ms RepoModel.GetById 52ms app.codeStats 0ms

/src/lib/Zend/Captcha/Image.php

https://bitbucket.org/mkrasuski/magento-ce
PHP | 625 lines | 281 code | 52 blank | 292 comment | 47 complexity | 8f1bb1448407f673d986cc387846bf3f MD5 | raw file
  1<?php
  2/**
  3 * Zend Framework
  4 *
  5 * LICENSE
  6 *
  7 * This source file is subject to the new BSD license that is bundled
  8 * with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://framework.zend.com/license/new-bsd
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@zend.com so we can send you a copy immediately.
 14 *
 15 * @category   Zend
 16 * @package    Zend_Captcha
 17 * @subpackage Adapter
 18 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
 19 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 20 * @version    $Id$
 21 */
 22
 23/** @see Zend_Captcha_Word */
 24#require_once 'Zend/Captcha/Word.php';
 25
 26/**
 27 * Image-based captcha element
 28 *
 29 * Generates image displaying random word
 30 *
 31 * @category   Zend
 32 * @package    Zend_Captcha
 33 * @subpackage Adapter
 34 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
 35 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 36 */
 37class Zend_Captcha_Image extends Zend_Captcha_Word
 38{
 39    /**
 40     * Directory for generated images
 41     *
 42     * @var string
 43     */
 44    protected $_imgDir = "./images/captcha/";
 45
 46    /**
 47     * URL for accessing images
 48     *
 49     * @var string
 50     */
 51    protected $_imgUrl = "/images/captcha/";
 52
 53    /**
 54     * Image's alt tag content
 55     *
 56     * @var string
 57     */
 58    protected $_imgAlt = "";
 59
 60    /**
 61     * Image suffix (including dot)
 62     *
 63     * @var string
 64     */
 65    protected $_suffix = ".png";
 66
 67    /**
 68     * Image width
 69     *
 70     * @var int
 71     */
 72    protected $_width = 200;
 73
 74    /**
 75     * Image height
 76     *
 77     * @var int
 78     */
 79    protected $_height = 50;
 80
 81    /**
 82     * Font size
 83     *
 84     * @var int
 85     */
 86    protected $_fsize = 24;
 87
 88    /**
 89     * Image font file
 90     *
 91     * @var string
 92     */
 93    protected $_font;
 94
 95    /**
 96     * Image to use as starting point
 97     * Default is blank image. If provided, should be PNG image.
 98     *
 99     * @var string
100     */
101    protected $_startImage;
102    /**
103     * How frequently to execute garbage collection
104     *
105     * @var int
106     */
107    protected $_gcFreq = 10;
108
109    /**
110     * How long to keep generated images
111     *
112     * @var int
113     */
114    protected $_expiration = 600;
115
116    /**
117     * Number of noise dots on image
118     * Used twice - before and after transform
119     *
120     * @var int
121     */
122    protected $_dotNoiseLevel = 100;
123    /**
124     * Number of noise lines on image
125     * Used twice - before and after transform
126     *
127     * @var int
128     */
129    protected $_lineNoiseLevel = 5;
130    /**
131     * @return string
132     */
133    public function getImgAlt ()
134    {
135        return $this->_imgAlt;
136    }
137    /**
138     * @return string
139     */
140    public function getStartImage ()
141    {
142        return $this->_startImage;
143    }
144    /**
145     * @return int
146     */
147    public function getDotNoiseLevel ()
148    {
149        return $this->_dotNoiseLevel;
150    }
151    /**
152     * @return int
153     */
154    public function getLineNoiseLevel ()
155    {
156        return $this->_lineNoiseLevel;
157    }
158    /**
159     * Get captcha expiration
160     *
161     * @return int
162     */
163    public function getExpiration()
164    {
165        return $this->_expiration;
166    }
167
168    /**
169     * Get garbage collection frequency
170     *
171     * @return int
172     */
173    public function getGcFreq()
174    {
175        return $this->_gcFreq;
176    }
177    /**
178     * Get font to use when generating captcha
179     *
180     * @return string
181     */
182    public function getFont()
183    {
184        return $this->_font;
185    }
186
187    /**
188     * Get font size
189     *
190     * @return int
191     */
192    public function getFontSize()
193    {
194        return $this->_fsize;
195    }
196
197    /**
198     * Get captcha image height
199     *
200     * @return int
201     */
202    public function getHeight()
203    {
204        return $this->_height;
205    }
206
207    /**
208     * Get captcha image directory
209     *
210     * @return string
211     */
212    public function getImgDir()
213    {
214        return $this->_imgDir;
215    }
216    /**
217     * Get captcha image base URL
218     *
219     * @return string
220     */
221    public function getImgUrl()
222    {
223        return $this->_imgUrl;
224    }
225    /**
226     * Get captcha image file suffix
227     *
228     * @return string
229     */
230    public function getSuffix()
231    {
232        return $this->_suffix;
233    }
234    /**
235     * Get captcha image width
236     *
237     * @return int
238     */
239    public function getWidth()
240    {
241        return $this->_width;
242    }
243
244    /**
245     * Set start image
246     *
247     * @param string $startImage
248     * @return Zend_Captcha_Image
249     */
250    public function setStartImage ($startImage)
251    {
252        $this->_startImage = $startImage;
253        return $this;
254    }
255
256    /**
257     * Set dot noise level
258     *
259     * @param int $dotNoiseLevel
260     * @return Zend_Captcha_Image
261     */
262    public function setDotNoiseLevel ($dotNoiseLevel)
263    {
264        $this->_dotNoiseLevel = $dotNoiseLevel;
265        return $this;
266    }
267
268    /**
269     * Set line noise level
270     *
271     * @param int $lineNoiseLevel
272     * @return Zend_Captcha_Image
273     */
274    public function setLineNoiseLevel ($lineNoiseLevel)
275    {
276        $this->_lineNoiseLevel = $lineNoiseLevel;
277        return $this;
278    }
279
280    /**
281     * Set captcha expiration
282     *
283     * @param int $expiration
284     * @return Zend_Captcha_Image
285     */
286    public function setExpiration($expiration)
287    {
288        $this->_expiration = $expiration;
289        return $this;
290    }
291
292    /**
293     * Set garbage collection frequency
294     *
295     * @param int $gcFreq
296     * @return Zend_Captcha_Image
297     */
298    public function setGcFreq($gcFreq)
299    {
300        $this->_gcFreq = $gcFreq;
301        return $this;
302    }
303
304    /**
305     * Set captcha font
306     *
307     * @param  string $font
308     * @return Zend_Captcha_Image
309     */
310    public function setFont($font)
311    {
312        $this->_font = $font;
313        return $this;
314    }
315
316    /**
317     * Set captcha font size
318     *
319     * @param  int $fsize
320     * @return Zend_Captcha_Image
321     */
322    public function setFontSize($fsize)
323    {
324        $this->_fsize = $fsize;
325        return $this;
326    }
327
328    /**
329     * Set captcha image height
330     *
331     * @param  int $height
332     * @return Zend_Captcha_Image
333     */
334    public function setHeight($height)
335    {
336        $this->_height = $height;
337        return $this;
338    }
339
340    /**
341     * Set captcha image storage directory
342     *
343     * @param  string $imgDir
344     * @return Zend_Captcha_Image
345     */
346    public function setImgDir($imgDir)
347    {
348        $this->_imgDir = rtrim($imgDir, "/\\") . '/';
349        return $this;
350    }
351
352    /**
353     * Set captcha image base URL
354     *
355     * @param  string $imgUrl
356     * @return Zend_Captcha_Image
357     */
358    public function setImgUrl($imgUrl)
359    {
360        $this->_imgUrl = rtrim($imgUrl, "/\\") . '/';
361        return $this;
362    }
363
364    /**
365     * Set image alternative text
366     *
367     * @param string $imgAlt
368     * @return Zend_Captcha_Image
369     */
370    public function setImgAlt ($imgAlt)
371    {
372        $this->_imgAlt = $imgAlt;
373        return $this;
374    }
375
376    /**
377     * Set captch image filename suffix
378     *
379     * @param  string $suffix
380     * @return Zend_Captcha_Image
381     */
382    public function setSuffix($suffix)
383    {
384        $this->_suffix = $suffix;
385        return $this;
386    }
387
388    /**
389     * Set captcha image width
390     *
391     * @param  int $width
392     * @return Zend_Captcha_Image
393     */
394    public function setWidth($width)
395    {
396        $this->_width = $width;
397        return $this;
398    }
399
400    /**
401     * Generate random frequency
402     *
403     * @return float
404     */
405    protected function _randomFreq()
406    {
407        return mt_rand(700000, 1000000) / 15000000;
408    }
409
410    /**
411     * Generate random phase
412     *
413     * @return float
414     */
415    protected function _randomPhase()
416    {
417        // random phase from 0 to pi
418        return mt_rand(0, 3141592) / 1000000;
419    }
420
421    /**
422     * Generate random character size
423     *
424     * @return int
425     */
426    protected function _randomSize()
427    {
428        return mt_rand(300, 700) / 100;
429    }
430
431    /**
432     * Generate captcha
433     *
434     * @return string captcha ID
435     */
436    public function generate()
437    {
438        $id = parent::generate();
439        $tries = 5;
440        // If there's already such file, try creating a new ID
441        while($tries-- && file_exists($this->getImgDir() . $id . $this->getSuffix())) {
442            $id = $this->_generateRandomId();
443            $this->_setId($id);
444        }
445        $this->_generateImage($id, $this->getWord());
446
447        if (mt_rand(1, $this->getGcFreq()) == 1) {
448            $this->_gc();
449        }
450        return $id;
451    }
452
453    /**
454     * Generate image captcha
455     *
456     * Override this function if you want different image generator
457     * Wave transform from http://www.captcha.ru/captchas/multiwave/
458     *
459     * @param string $id   Captcha ID
460     * @param string $word Captcha word
461     * @throws Zend_Captcha_Exception
462     */
463    protected function _generateImage($id, $word)
464    {
465        if (!extension_loaded("gd")) {
466            #require_once 'Zend/Captcha/Exception.php';
467            throw new Zend_Captcha_Exception("Image CAPTCHA requires GD extension");
468        }
469
470        if (!function_exists("imagepng")) {
471            #require_once 'Zend/Captcha/Exception.php';
472            throw new Zend_Captcha_Exception("Image CAPTCHA requires PNG support");
473        }
474
475        if (!function_exists("imageftbbox")) {
476            #require_once 'Zend/Captcha/Exception.php';
477            throw new Zend_Captcha_Exception("Image CAPTCHA requires FT fonts support");
478        }
479
480        $font = $this->getFont();
481
482        if (empty($font)) {
483            #require_once 'Zend/Captcha/Exception.php';
484            throw new Zend_Captcha_Exception("Image CAPTCHA requires font");
485        }
486
487        $w     = $this->getWidth();
488        $h     = $this->getHeight();
489        $fsize = $this->getFontSize();
490
491        $img_file   = $this->getImgDir() . $id . $this->getSuffix();
492        if(empty($this->_startImage)) {
493            $img        = imagecreatetruecolor($w, $h);
494        } else {
495            $img = imagecreatefrompng($this->_startImage);
496            if(!$img) {
497                #require_once 'Zend/Captcha/Exception.php';
498                throw new Zend_Captcha_Exception("Can not load start image");
499            }
500            $w = imagesx($img);
501            $h = imagesy($img);
502        }
503        $text_color = imagecolorallocate($img, 0, 0, 0);
504        $bg_color   = imagecolorallocate($img, 255, 255, 255);
505        imagefilledrectangle($img, 0, 0, $w-1, $h-1, $bg_color);
506        $textbox = imageftbbox($fsize, 0, $font, $word);
507        $x = ($w - ($textbox[2] - $textbox[0])) / 2;
508        $y = ($h - ($textbox[7] - $textbox[1])) / 2;
509        imagefttext($img, $fsize, 0, $x, $y, $text_color, $font, $word);
510
511       // generate noise
512        for ($i=0; $i<$this->_dotNoiseLevel; $i++) {
513           imagefilledellipse($img, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color);
514        }
515        for($i=0; $i<$this->_lineNoiseLevel; $i++) {
516           imageline($img, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color);
517        }
518
519        // transformed image
520        $img2     = imagecreatetruecolor($w, $h);
521        $bg_color = imagecolorallocate($img2, 255, 255, 255);
522        imagefilledrectangle($img2, 0, 0, $w-1, $h-1, $bg_color);
523        // apply wave transforms
524        $freq1 = $this->_randomFreq();
525        $freq2 = $this->_randomFreq();
526        $freq3 = $this->_randomFreq();
527        $freq4 = $this->_randomFreq();
528
529        $ph1 = $this->_randomPhase();
530        $ph2 = $this->_randomPhase();
531        $ph3 = $this->_randomPhase();
532        $ph4 = $this->_randomPhase();
533
534        $szx = $this->_randomSize();
535        $szy = $this->_randomSize();
536
537        for ($x = 0; $x < $w; $x++) {
538            for ($y = 0; $y < $h; $y++) {
539                $sx = $x + (sin($x*$freq1 + $ph1) + sin($y*$freq3 + $ph3)) * $szx;
540                $sy = $y + (sin($x*$freq2 + $ph2) + sin($y*$freq4 + $ph4)) * $szy;
541
542                if ($sx < 0 || $sy < 0 || $sx >= $w - 1 || $sy >= $h - 1) {
543                    continue;
544                } else {
545                    $color    = (imagecolorat($img, $sx, $sy) >> 16)         & 0xFF;
546                    $color_x  = (imagecolorat($img, $sx + 1, $sy) >> 16)     & 0xFF;
547                    $color_y  = (imagecolorat($img, $sx, $sy + 1) >> 16)     & 0xFF;
548                    $color_xy = (imagecolorat($img, $sx + 1, $sy + 1) >> 16) & 0xFF;
549                }
550                if ($color == 255 && $color_x == 255 && $color_y == 255 && $color_xy == 255) {
551                    // ignore background
552                    continue;
553                } elseif ($color == 0 && $color_x == 0 && $color_y == 0 && $color_xy == 0) {
554                    // transfer inside of the image as-is
555                    $newcolor = 0;
556                } else {
557                    // do antialiasing for border items
558                    $frac_x  = $sx-floor($sx);
559                    $frac_y  = $sy-floor($sy);
560                    $frac_x1 = 1-$frac_x;
561                    $frac_y1 = 1-$frac_y;
562
563                    $newcolor = $color    * $frac_x1 * $frac_y1
564                              + $color_x  * $frac_x  * $frac_y1
565                              + $color_y  * $frac_x1 * $frac_y
566                              + $color_xy * $frac_x  * $frac_y;
567                }
568                imagesetpixel($img2, $x, $y, imagecolorallocate($img2, $newcolor, $newcolor, $newcolor));
569            }
570        }
571
572        // generate noise
573        for ($i=0; $i<$this->_dotNoiseLevel; $i++) {
574            imagefilledellipse($img2, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color);
575        }
576        for ($i=0; $i<$this->_lineNoiseLevel; $i++) {
577           imageline($img2, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color);
578        }
579
580        imagepng($img2, $img_file);
581        imagedestroy($img);
582        imagedestroy($img2);
583    }
584
585    /**
586     * Remove old files from image directory
587     */
588    protected function _gc()
589    {
590        $expire = time() - $this->getExpiration();
591        $imgdir = $this->getImgDir();
592        if(!$imgdir || strlen($imgdir) < 2) {
593            // safety guard
594            return;
595        }
596        $suffixLength = strlen($this->_suffix);
597        foreach (new DirectoryIterator($imgdir) as $file) {
598            if (!$file->isDot() && !$file->isDir()) {
599                if (file_exists($file->getPathname()) && $file->getMTime() < $expire) {
600                    // only deletes files ending with $this->_suffix
601                    if (substr($file->getFilename(), -($suffixLength)) == $this->_suffix) {
602                        unlink($file->getPathname());
603                    }
604                }
605            }
606        }
607    }
608
609    /**
610     * Display the captcha
611     *
612     * @param Zend_View_Interface $view
613     * @param mixed $element
614     * @return string
615     */
616    public function render(Zend_View_Interface $view = null, $element = null)
617    {
618        $endTag = ' />';
619        if (($view instanceof Zend_View_Abstract) && !$view->doctype()->isXhtml()) {
620            $endTag = '>';
621        }
622        return '<img width="' . $this->getWidth() . '" height="' . $this->getHeight() . '" alt="' . $this->getImgAlt()
623             . '" src="' . $this->getImgUrl() . $this->getId() . $this->getSuffix() . '"' . $endTag;
624    }
625}