دسته: PHP

  • دیزاین پترن singleton در PHP

    در ادامه بحث هامون در مورد دیزاین پترن ها امروز میخوام بصورت خلاصه در مورد singleton design pattern بنویسم که فکر میکنم به درد خیلی هاتون بخوره و خیلی از پیچیدگی کد نویسی رو براتون کمتر کنه. بصورت خلاصه و مفید اگه بخوام بگم این دیزاین پترن همونطور که از اسمش پیداست از تکرار جلوگیری میکنه و جاهایی استفاده میشه که بخوایم یه کد تکراری رو فقط یکبار بنویسیم. پس سینگلتون جاهایی به کارمون میاد که ما به یه نمونه از شی بصورت گلوبال میخوایم دسترسی داشته باشیم. مثلا شما ممکنه مجبور باشید توی یک سیستم بارها به دیتابیس وصل بشید. یا ممکنه بخواید از api یه سایت دیگه هر چند بار پشت سر هم استفاده کنید یا هر چیز دیگه ای که خودتون صلاح میدونید… با مثال توضیح میدم که ایده ی این الگو چطور هست. در زیر یه پیاده سازی پایه از این الگو رو میبینیم :

    class Database
    {
        public static function Instance()
        {
            static $inst = null;
            if ($instnce === null) {
                $instnce = new Database();
            }
            return $instnce;
        }
    
        public function getUsers()
        {
            return 'users...';
        }
        
        public function getPosts()
        {
            return 'posts...';
        }
    }
    
    $users = Database::Instance()->getUsers();
    $posts = Database::Instance()->getPosts();

    همونطور که میبینید Instance  به صورت متد استاتیک تعریف شده و بصورت استاتیک بهش دسترسی پیدا میکنیم.

  • دیزاین پترن Simple Factory در PHP

    سلام، بعد از یه وقفه ی نسبتا طولانی امروز دوباره تصمیم گرفتم که این وبلاگ متروکه رو آپدیت کنم و یسری پست که احساس میکنم خوبن رو اینجا بفرستم… البته پستای قبلیمو دارم میبینم اکثرا چرت و پرتن و ارزش خوندن ندارن ولی به هر حال وبلاگ خودمه و کلی پول هاست و دامین دادم و دوس دارم که بعنوان یادگاری همینجا باشن و کسی اگه اعتراض داره میتونه مراتب اعتراضش رو تحت یک کامنت برام ارسال کنه تا بررسی و پاسخ داده بشه 🙂

    Design patternها یا فارسیش میشه احتمالا الگوهای طراحی نرم افزار توی همه زبان ها خیلی خیلی مهمن. معمولا وقتی کدی مینویسیم که قرار نیست در آینده گسترش داده بشه خیالمون راحته و از یجای کار شروع میکنیم و یجا تموم میکنیم و کار رو تحویل میدیم، ولی وقتی کارفرما میگه که “در آینده قراره امکانات زیادی از قبیل فلان۱ و فلان۲ و… به سیستم اضافه بشه”، اونجا باید یه فکر پایه ای تر برای سیستم داشته باشیم. طوری که اگه قرار شد یه بخش جدید رو به سیستم اضافه کنیم مجبور نباشیم تو بخشای قبلی دست اندازی کنیم یا حداقل کم مجبور باشیم(!).

    فرض کنید که میخواید یه فروشگاه بنویسید که ادمین روزی چند تا محصول از نوع اسباب بازی به سیستم اضافه میکنه. ابتدایی ترین روشی که به فکر همه ما میرسه اینه که یه کلاس ایجاد کنیم تحت نام Product  و توی اون درج، اصلاح، حذف و … محصول که فعلا اسباب بازی هست رو انجام بدیم. یه چیزی شبیه این :

    <?php
    class Product {
        public function __construct($name) {}
    
        public function save() {}
    }
    $product = new Product('first toy');
    $product->save();

    تا اینجا همه چی خوبه و شما در آرامش قبل از طوفان به سر میبرید! مشکل زمانی پیش میاد که مجبور بشید مثلا بجز اسباب بازی ، کتاب هم در فروشگاهتون بفروشید. اینجا دیگه روش ایجاد و اصلاح و … فرق میکنه و نمیتونید همین متد های قبل رو استفاده کنید. توی دیزاین پترن بالا شما مجبورید یه $type  زمان نمونه گیری از کلاس بگیرید و توی هر متد if و else بذارید و بگید اگه این دسته بود این کارو بکن و اگه… که قطعا روش خوبی نیست و مثلا وقتی چنتا دسته دیگه بعدها اضافه بشه کدمون خیلی داغون میشه.

    نحوه ی پیاده سازی دیزاین پترن Simple Factory در PHP

    سیمپل فکتوری دیزاین پترن میگه که شما این کارو بکنید :

    اول یه کلاس factory بسازید :

    class ProductFactory {
      public function __construct(){ }
    
      public static function create($type){
         if ($type == '')
            throw new Exception('invalid product type');
         else {
            $className = 'product_' . ucfirst($type);
            if (class_exists($className))
               return new $className;
            else
               throw new Exception('Product type not found');
         }
      }
    }
    

    حالا واسه ساخت اسباب بازی از روش خیلی ساده زیر استفاده کنید :  

    class product_Toy
    {
      public function __construct(){
         echo "toy is creating";
      }
    }
    $toy = ProductFactory::create('toy');
    

    حالا اگه یه دسته جدید مثل کتاب به محصولاتون اضافه شه خیلی راحت میتونید کلاسش رو بسازید محصولتون رو اضافه کنید :

    class product_Book
    {
      public function __construct(){
         echo "book is creating";
      }
    }
    $book = ProductFactory::create('book');
    

    در مجموع simple factory method خیلی ایده ی سختی نداره. میگه وقتی داری از یه کلاس نمونه میگیری که خودش میتونه انواع مختلفی داشته باشه یدونه فکتوری واسش درست کن و از فکتوری نمونه بگیر که هر بار مجبور نشی هزار تا جایی که ازش نمونه گرفتی رو تغییر بدی. بعد برو توی فکتوری و چک کن که با توجه به کانفیگت کدوم کلاس رو نمونه بگیری.

    احتمالا الان میگید که این که خیلی پیش پا افتاده بود و شاید شما خیلی کارای خفن تر هم میتونید انجام بدید ولی خب بعضی وقتا همین کارای ساده باعث منظم شدن برنامه میشن و در آینده خودتون و مخصوصا دیگران کمتر اذیت میشن. البته این دیزاین پترن همونطور که از اسمش مشخصه سیمپل بود و  در آینده در مورد پترن های دیگه هم صحبت خواهیم کرد حتما

    امیدوارم کمکتون کرده باشم و خوشحال میشم نظراتتون رو بشنوم 🙂

  • ذخیره کردن شکلک ها در MySQL

    اخیرا که ملت همه ریختن تو شبکه اجتماعی و با شکلک – ایموجی – ها و… سروکار دارن ما هم باید بتونیم کارشون رو راه بندازیم 🙂

    کانکشن پیشفرض MySQL لاراول به صورت پیشفرض از کاراکتر ست utf8 استفاده میکنه که نمیتونه شکلک ها رو ذخیره کنه و معمولا با این خطا مواجه میشیم :

    SQLSTATE[HY000]: General error: 1366 Incorrect string value...

    برای حل این مشکل کانکشن mysql  در config/database.php  رو به صورت زیر اصلاح کنید :

    'mysql' => [
        'driver' => 'mysql',
    	[...]
        'charset' => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
        [....]

    و دیتابیس رو ریست کنید. حواستون باشه که از دیتاتون قبلش بکاپ بگیرید…

  • file_get_content بوسیله curl در php

    همونطور که میدونید تابع flie_get_contents()  سورس فایل مورد نظر رو که میتونه حتی یک صفحه وب باشه رو میریزه توی یه رشته و میتونید هر بلایی سرش بیارید(مثلا با html-dom-parser به المنت هاش دسترسی پیدا کنید). بعضی وقتا ssl و… نمیذارن که بصورت مستقیم به محتویات صفحه دسترسی پیدا کنید. همینطور وقتی میخواید یه عکس رو از یه صفحه ای که ssl روش هست کپی کنید…  من به این مشکل برخوردم و با curl مشکلم رو حل کردم… جمع و جورش رو براتون در قالب یه فانکشن گذاشتم که امیدوارم به دردتون بخوره…

     function file_get_contents_curl( $url ) {
            $ch = curl_init();
            curl_setopt( $ch, CURLOPT_AUTOREFERER, TRUE );
            curl_setopt( $ch, CURLOPT_HEADER, 0 );
            curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
            curl_setopt( $ch, CURLOPT_URL, $url );
            curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, TRUE );
            curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0);
            curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0);
            $data = curl_exec( $ch );
            curl_close( $ch );
            return $data;
        }

    موفق باشید…

  • nodejs در مقابل php

    اخیرا مجبور شدم روی یک پروژه چت آنلاین کار کنم که یک موتور چت داره و یک سری ریکوئست رو میگیره و جواب مناسب رو میده…  با php و فریمورک لاراول (♥) و وب سرور آپاچی شروع کردم  برای هرچه بهینه تر شدن موتور از روش comet استفاده کردم که یک درخواست رو تا دریافت درخواست بعدی به تاخیر میندازه و خلاصه به روشی خلاقانه باعث پایین اومدن حجم درخواست ها به سرور و در عین حال real time شدن کامل برنامه میشه. واضحه که با این حجم read و write که توی همچین سیستمی وجود داره، mysql گزینه مناسبی برای مدیریت داده ها نیست و بخاطر همین از همون اول از mongodb بعنوان یک سیستم مدیریت پایگاه داده noSQL استفاده کردم…

    تا اینجا یه موتور چت (chat engine) با لاراول و مانگو دارم… برای تست ، ۲۰ کاربر با سشن های مختلف روش فعال کردم که هر کدوم دارن چت میکنن و بیشتره تایم رو هم گوش میکنن و منتظر دریافت پیام طرف مقابلن… با ۲۰ تا کاربر با اینکه منابع نسبتا بالایی از سرور استفاده میشد ولی سرعت جواب ها قابل قبول بود… وقتی تعداد کلاینت های سیستم پایین هست بالطبع تعداد درخواست هایی که برای سیستم میاد هم پایین خواهد بود و مشکلی پیش نمیاد… اما وقتی تعداد کلاینت ها رو ۱۰۰ برابر کردم… بووووم… سیستم ترکید☺

    اینجا بود که به ضعیف بودن php و یه وب سرور مثل آپاچی ایمان آوردم… یه چرخی تو اینترنت زدم و مقاله هایی رو خوندم که دقیقا حرف الان من رو تایید میکردن… کم کم با پدیده ای به نام nodejs آشنا شدم… syntax و داکیومنت خوبش باعث شد که سمتش برم و موتور چت رو ببرم روی node…

    گرچه تحقبق روی پروژه انقد طولانی شد که شرکت، پروژه رو شکست خورده تلقی کرد و رفتم روی پروژه ی بعدی… ولی به شما پیشنهاد میکنم اشتباه من رو تکرار نکنید و اگه سیستم مشابهی رو برای کارتون نیاز دارید به node بعنوان یک گزینه خوب نگاه کنید… یه مقایسه قشنگه node و php رو هم توی این لینک ببینید. موفق باشید 🙂

     

  • پراپرتی و متدهای استاتیک در php

    قبل از اینکه بخوام در مورد facade  در لاراول صحبت کنم باید اطلاعات کلی در مورد توابع یا متدهای استاتیک php داشته باشید. متدهای استاتیک متدهایی از یک کلاس هستند که بدون نمونه گیری از کلاس و بصورت استاتیک قابل دسترسی هستند. یعنی بدون داشتن شئ از کلاس ، میتونیم از این نوع متدها استفاده کنیم.

    قبل از مثال در مورد متدهای استاتیک میخوام کمی در مورد متغیرها یا پراپرتی های استاتیک صحبت کنم. یک پراپرتی استاتیک بدون استفاده از $this  و به راحتی با استفاده از ::  قابل دسترسی هست. همچنین میتونید یک متغیر استاتیک رو با استفاده از کلمه کلیدی static  تعریف کنید. به مثال زیر توجه کنید :

    class test
    {
    public static $a;//Static variable
    }
    test::$a = 5;
    echo test::$a;

    میبینید که چقدر راحت میشه به پراپرتی های استاتیک دسترسی داشت. با استفاده از کلمه های کلیدی self  و parent  هم میتونید به پراپرتی های این کلاس و یا کلاس پدر دسترسی داشته باشید.

    class testParent
    {
    public static $var1;
    }
    class testChild extends testParent
    {
    public static $var2;
    public $abc =2;
    function testFunction()
    {
    self::$var2 = 3;
    parent::$var1 = 5;
    }
    }
    echo testChild::$abc; //throw fatal error

    نحوه ی تعریف متدهای استاتیک

    <?php
    class Game
    {
    	public static function start()
    	{
    		echo "Go!";
    	}
    }
    
    Game::start();

    همونطور که در بالا میبینید متد start()  بصورت استاتیک تعریف شده و خیلی شیک و مجلسی میشه ازش نمونه ساخت. این نوع توابع در نگاه اول خیلی جالب به نظر میرسن… و سوالی که پیش میاد اینه که چرا همه متد هارو استاتیک تعریف نمیکنیم؟ 

    متدهای استاتیک با اینکه اولش خیلی باحال به نظر میرسن ولی محدودیتی دارن که تنها در بعضی موارد میتونیم ازشون استفاده کنیم : “بخاطر استاتیک تعریف شدن این متدها ، امکان استفاده از $this  در این نوع متدها وجود ندارد.” ولی در مجموع این نوع متدهای خیلی پرکاربرذ هستند.

  • متغییر های استاتیک در PHP

    متغییر های استاتیک در PHP متغییر های خیلی جالبین 🙂 . متغیر هایی هستند که مقداری که دارند رو از دست نمیدن ، حتی داخل یک تابع و بعد از به اتمام رسیدن پردازش تابع.

    مثال میزنم :

    <?php
       function keep_track() {
          STATIC $count = 0;
          $count++;
          print $count;
          print "<br />";
       }
       
       keep_track();
       keep_track();
       keep_track();
    ?>

    خروجی کد بالا به شکل زیر خواهد بود :

    ۱
    ۲
    ۳

    میبینید که مقدار متغیر استاتیک ثابت مونده بعد از اتمام هر تابع…

  • آموزش و کاربرد استفاده IOC container در Laravel

    امروز بحث رو اندکی تخصصی تر میکنیم و میریم سمت container قدرتمند لاراول. یکی از مشخصه هایی که لاراول رو از سایر فریمورک های PHP جدا میکنه ، همین IOC هست. Dependency injection  یا DI یا تزریق نیازمندیها یک موضوع مهم در همه زبانهای شئ گراست. اینکه شما سایر کلاسها رو در کلاس خودتون استفاده کنید خیلی مهمه.

    اما IOC کانتینر چیست؟

    برای اینکه از یک کلاس در کدمون استفاده کنیم باید از اون نمونه بگیریم. یه چیزی مثل کد زیر :

    $myInstance = new MyClass();
    
    $myInstance->method();

    حتما بارها شده که مقدار Request $request رو بعنوان ورودی به یک تابع در کنترلر میفرستید. سوالی که پیش میاد اینه که بدون نمونه گرفتن از کلاس Request چطور PHP اونو میشناسه؟ اینجاست که کاربرد IOC مشخص میشه. لاراول با کانتینرش شرایطی رو مهیا کرده که شما به راحتی با آبجکت ها کار کنید و ازشون نمونه بگیرید. لاراول وقتی کدی مثل FOO $foo رو میبینه دنبال کلاس FOO میگرده که از اون نمونه بگیره به شرطی که کلاس FOO به کانتینر معرفی شده باشه قبلا. این خیلی خوبه. شما بدون اینکه از کلاس نمونه بگیرید نمونه گرفته میشه. بعضی وقتا پیش میاد که شما میخواید یک ریپازیتوری بنویسید که دیگران هم بتونن ازش استفاده کنن. اینجا نیاز هست که کلاس خودتون رو به لاراول برای نمونه گیری معرفی کنید و شما باید بصورت دستی کلاس رو به IOC بچسبونید یا به اصطلاح bind کنید.

    نحوه ی bind یک کلاس به IOC در لاراول

    ساده ترین روش بایند کردن کلاس به ioc استفاده از فساد App هست که باهاش میتونید هر جایی در برنامه یه کلاس رو به کانتینر بایند کنید. (هر چند بهترین جا برای بایند کردن استفاده از service provider ها هست)

    App::bind('Hamid\Repositories\User\UserRepository', function($app)
    {
        return new EloquentUserRepository( new User );
    });

    در کد بالا EloquentUserRepository رو چسبوندیم به کانتینر و از این به بعد لاراول کلاس EloquentUserRepository رو میشناسه همه جا و resolve اش میکنه.

    حتما در مورد فساد ها در لاراول اطلاعاتی دارید. همه ی کلاسهایی که در فساد ها ازشون استفاده میکنیم قبلا به کانتینر بایند شدن. مثلا در زیر بخشی فایل Illuminate/Support/Facades/Auth.php رو میبینید که مربوط به فساد Auth هست که هر روز ازش استفاده میکنیم:

    class Auth extends Facade
    {
        /**
         * Get the registered name of the component.
         *
         * @return string
         */
        protected static function getFacadeAccessor()
        {
            return 'auth';
        }

    همونطور که میبینید متد استاتیک getFacadeAccessor داره یه رشته رو برمیگردونه که در فایل Illuminate/Auth/AuthServiceProvider.php بصورت زیر به کانتینر بایند شده:

    $this->app->singleton('auth', function ($app) {
         return new AuthManager($app);
    });

  • مفهوم و کاربرد اینترفیس – interface در PHP

    قبلا در مورد کلاسهای abstract در PHP مطلبی نوشتم ، کلاسهای انتزاعی یا abstract کلاسهایی هستند که قابل نمونه گیری نیستند و میتونیم درون کلاسهای انتزاعی متد هایی بنویسیم و در کلاسهایی که از اون مشتق شدن ازش استفاده کنیم. اینترفیس ها دقیقا مثل کلاسهای انتزاعی هستند ، با این تفاوت که در اینترفیس ها هیچ متدی نمیتونه دارای بدنه باشه. اولش خیلی عجیب به نظر میرسه… وقتی متدها بدنه نداشته باشن ، به چه دردی میخودن؟

    به خاطر داشته باشید که شما با تعریف یک متد در کلاس abstract نویسنده کلاسهای دیگه (که از این کلاس مشتق شدن) رو وادار نمیکردید که متدهای کلاس پدر رو بازنویسی کنه. ولی در اینترفیس ها اینطور هست، یعنی هر کلاسی که یک اینرفیس رو implement کنه ، باید تمام متدهای کلاس پدر رو بازنویسی کنه.

    مثلا شما میخواید کلاسی بسازید که یک تگ html رو رندر کنه و برای این کار نیاز هست که به تگ ، id و class داده بشه. شما میاید یه اینترفیس میسازید و درون اون دو تابع setID  و setClass  رو مینویسید. از اون به بعد هر کسی که کلاس خودش رو از اینترفیس شما مشتق کرد مجبوره که دو تا تابع رو بنویسه و این از خطای برنامه جلوگیری میکنه. شاید در پروژه های کوچیک زیاد به درد نخوره ، ولی پروژه هایی که تعداد زیادی توسعه دهنده روشون کار میکنند ، برای پیاده سازی design pattern ها در اکثر اوقات باید از اینترفیس ها استفاده کنن.

    نحوه ی تعریف interface ها در PHP

    برای تعریف اینترفیس ها از کلمه کلیدی interface استفاده میکنیم :

    interface abc
    {
        public function xyz($b);
    }

    در مثال بالا کلاسها رو مجبور میکنیم که متد xyz رو بنویسن. برای ارث بردن یا به اصطلاح پیاده کردن یک اینترفیس از کلمه کلیدی implements استفاده میکنیم:

    class test implements abc
    {
       public function xyz($b)
       {
           // The body of your function...
       }
    }

    نکته قابل توجه اینه که تمام متد ها و پراپرتی هایی که درون یک اینترفیس تعریف میشن باید بصورت عمومی یا public  تعریف بشن ، در غیر اینصورت با اررور مواجه میشیم.

    یک نکته دیگه اینکه یک کلاس میتونه بیش از یک اینترفیس رو implement کنه و همچنین یک اینترفیس میتونه از چند اینترفیس دیگه مشتق بشه.

    نوشتن کدهای Open-Closed با استفاده از اینترفیس ها

    ما در مفاهیم SOLID همیشه تشویق میشیم که کدهامون رو open-closed بنویسیم. ینی کدهامون برای تغییر بسته باشن و جوری کد بزنیم که کلاسها رو هی نیایم دستکاری کنیم همینطور باید کد رو جوری بنویسیم که به راحتی extendable باشه. دلیل این هم شاید برمیگرده به اینکه ریفکتور کردن کدهای اینجوری سخته و باعث بروز مشکلات میشه. من در ادامه یه مثال میارم و روی اون توضیح میدم:

    <?php
    
    interface DriverContract
    {
        public function connect();
        public function run();
        public function get();
    }
    
    
    class MySQL implements DriverContract
    {
        public function connect()
        {
            echo "Connecting to MySQL...\n";
        }
        public function run()
        {
            echo "Running MySQL...\n";
        }
        public function get()
        {
            echo "Getting MySQL...\n";
        }
    }
    
    class MongoDB implements DriverContract
    {
        public function connect()
        {
            echo "Connecting to MongoDB...\n";
        }
        public function run()
        {
            echo "Running MongoDB...\n";
        }
        public function get()
        {
            echo "Getting MongoDB...\n";
        }
    }
    
    
    function consume(DriverContract $driver)
    {
        $driver->connect();
        $driver->run();
        $driver->get();
    }
    
    consume(new MySQL());
    

    یه راه ابتدای برای نوشتن این کد اینه که بگیم اگه درایور فلان بود اینجوری کانکت شو و اگه اونیکی بود اونجوری! ولی مشکلی که پیش میاد هر بار که درایور اضافه میشه من باید بیام ایف هام رو دستکاری کنم. با این مدل کد نوشتن من انتخاب درایور رو به کانسومر واگذار کردم و در واقع پیچیدگی کد رو بردم به لایه های بالاتر و همینطور در صورت نیاز به اضافه کردن درایور دیگر، به راحتی فقط کلاسش رو عوض میکنیم و نمیریم کد قبلی رو دستکاری کنیم. در واقع ما با استفاده از یک اینترفیس اون پشت رو از دید کاربر معمولی قایم کردیم و کاربر میدونه که یه درایور رو بگیره و استفاده کنه. مثلا در فریمورک لاراول ما وقتی model binding انجام میدیم مهم نیس که اون مدل با چی ساخته شده! مهم اینه که فانکشن هایی که هر مدل میخواد رو ما داخلش داریم و اینجوریه که دیتابیس رو عوض میکنیم هیچ مشکلی پیش نمیاد. پس کد برای گسترش باز نگه داشته شده و هر چنتا درایور بخوایم میتونیم اضافه کنیم ولی برای تغییر کلوزد هست و کدهای قبلی دستکاری نمیشن.

    کدهای Testable با کمک interface ها

    اگه با مفاهیم تست نویسی و بخصوص mocking آشنا نیستید شاید این بخش یه خورده گنگ باشه واستون. ولی به هر حال در کد بالا میتونستیم بجای inject کردن اینترفیس مستقیما یک کلاس رو اینجکت کنیم و در ظاهر خیلی اتفاق بدی نمیفتاد. ولی مشکل اصلی زمانی پیش میاد که شما میخواید بدون تغییر در کد یا گرفتن ورودی اضافه کدتون رو تست کنید. اونجا برای تست تابع consume باید حتما ورودی رو از نوع مثلا MySQL میدادید که در این صورت نمیشد یه کلاس فیک برای تست نوشت ولی الان خیلی راحت میتونیم هر نمونه ای که اینترفیس رو پیاده کرده رو به عنوان ورودی به تابع بدیم و اون رو تست کنیم.

    در پایان من فرصت نکردم متن رو ریویو کنم اگه مشکلی بود ببخشید و توی کامنت ها بگید که اصلاح کنم 🙂