<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های WebPajooh</title>
        <link>https://virgool.io/feed/@WebPajooh</link>
        <description>توسعه‌دهندۀ بک‌اند، امیدوار، خیال‌باف، علاقه‌مند به خواندن و نوشتن</description>
        <language>fa</language>
        <pubDate>2026-04-15 04:44:59</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/27847/avatar/vd4qBp.png?height=120&amp;width=120</url>
            <title>WebPajooh</title>
            <link>https://virgool.io/@WebPajooh</link>
        </image>

                    <item>
                <title>آنچه هر برنامه‌نویس تازه‌کاری باید بداند!</title>
                <link>https://virgool.io/@WebPajooh/%D8%A2%D9%86%DA%86%D9%87-%D9%87%D8%B1-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3-%D8%AA%D8%A7%D8%B2%D9%87-%DA%A9%D8%A7%D8%B1%DB%8C-%D8%A8%D8%A7%DB%8C%D8%AF-%D8%A8%D8%AF%D8%A7%D9%86%D8%AF-s0nfaidkw8ge</link>
                <description>این ویرگول، ترجمه‌ای خوب و روان (?) از پستی است که در سایت dev.to دیده بودم. اگر در ادامه با نویسنده مخالف باشم، نظر خودم را هم اضافه می‌کنم و اگر این پست را می‌بینید، یعنی مخالفتمان خیلی جدی نبوده است!یک جایی از مسیر کاریمان را به گذشته نگاه می‌کنیم؛ اینکه چگونه شروع کردیم، چگونه پیشرفت کردیم و چه چیزی برای مای کم‌تجربه خیلی مفید و سودمند بود. اکنون تصمیم گرفته‌ام که برخی نکات را -که ترتیب آنها بی‌اهمیت است- با شما که یک برنامه‌نویس تازه‌کار هستید، در میان بگذارم.وقت خود را در جنگ زبان‌ها تلف نکنید!بله، درست خواندید! این کار را نکنید! هر زبانی، چیزی دارد که برخی با آن مشکل داشته باشند و شما -به عنوان تازه‌کار- جزو آن «برخی» نیستید! یکی را انتخاب کنید و با آن پیش بروید، و تصمیم‌گیری‌های بعدی را به زمانی در آینده موکول کنید که باتجربه شده‌اید!هر جوابی که در Stackoverflow تایید شده، به درد مورد شما نمی‌خورد!می‌دانم که ناخودآگاه به دنبال پاسخ‌های تیک‌خورده می‌گردید، اما همیشه جواب درست و مناسب شما در آن نیست. دیگر جواب‌ها و حتی کامنت‌ها را بخوانید؛ زیرا در کامنت‌ها هم چیزهای خوبی را متوجه خواهید شد.قبل از کپی‌پیست‌کردن، بپرسید که این کد چگونه کار می‌کند!در هر چالش غیر قابل حل، فرصتی برای یادگیری چیزهای جدید وجود دارد، پس با pasteکردن راه‌حل، این فرصت را از دست ندهید! سعی کنید شیوۀ کار کد را بفهمید و خوب درک کنید!آموزش‌دیدن بدون تمرین‎‌کردن، اتلاف وقت است!مطمئنم که دربارۀ دوزخ آموزش (tutorial hell) شنیده‌اید و هیچ بعید نیست که این اتفاق برای شما بیفتد! اگر یک برنامه‌نویس باتجربه نیستید که هدفش از دیدن آموزش، افزایش دانش است، پیش از شروع‌کردن هر دورۀ آموزشی، بررسی کنید که آیا تمرینات عملی در آن هست یا نه؛ زیرا یادگیری واقعی در تمرین‌کردن اتفاق می‌افتد. البته تمرین هم کافی نیست، باید بگذارید که ذهنتان فعال شود و به این پرسش بپردازد که آنچه آموخته‌اید در کجا می‌تواند کاربردی شود.دانشگاه مهم است!شاید دردناک به نظر برسد، ولی تحصیلات دانشگاهی در بازار کار یک مزیت به شمار می‌رود و حتی اگر خیلی‌ها بگویند مهم نیست، در عمل چنین برخورد نمی‌کنند و ممکن است به خاطر «نداشتن بک‌گراند کافی برای نیازمندی‌ها» یک فرصت شغلی را از دست بدهید! همیشه در حال مطالعه و بهبود دانش خود باشید.مترجم: کتاب‌خواندن سرمایه‌گذاری خوبی است و باعث می‌شود که به جای آزمایش و خطا، از تجارب بزرگان حوزۀ مهندسی نرم‌افزار استفاده کنید. لیستی از کتاب‌های خوب را جمع‌آوری کنید، اولویت‌بندی کنید و بعد بخوانید. اگر مهارت قابل قبولی در زبان انگلیسی ندارید، حتما برای آن وقت بگذارید.اینکه کد کار می‌کند، به معنای خوب‌بودنش نیست!یک حقیقت دردناک دیگر! همیشه تلاش کنید که از برنامه‌نویسان باتجربه و کهنه‌کار دربارۀ کدهایی که می‌نویسید بازخورد بگیرید. اگر چنین کنید، سرعت یادگیری شما به مراتب بیشتر می‌شود و جلوتر می‌افتید.مترجم: خواندن کتاب‌هایی مثل Clean Code اثر Robert Martin و Refactoring اثر Martin Fowler می‌تواند بسیار مفید باشد. در هر زبانی، استانداردهایی مربوط به Style کدها وجود دارد که می‌توانید خود را با آنها وفق دهید.شرکت در مصاحبه‌های شغلی، خسته‌کننده هستند!جاده بسیار باریک است، پس خود را برای رد شدن آماده کنید! ممکن است سه مرحله مصاحبه را بگذرانید، و در آخر بگویند که تا نگویید فلان سیستم را چه کسی ابداع کرده، به مرحلۀ بعد نخواهید رفت! گاهی مصاحبه و شغل با هم مطابقت ندارند؛ مثلاً در نهایت با ORM کار خواهید کرد اما سؤالات عجیب SQL مطرح خواهد شد، یا اینکه قرار است REST API ساده‌ای بنویسید، اما از شما دربارۀ اینکه Node.js چگونه ساخته شد بپرسند!قوی و آماده باشید!زبان و فریمورک را بشناسید!اینکه با یک زبان یا کتابخانه، اپلیکیشن‌های CRUDمحور بسازید، کافی نیست! سعی کنید درک بهتری از سیستم به دست بیاورید، زیرا وقتی به جاهای بهتری برسید، احتمالا ابزارهایی خواهید ساخت که توسط دیگران استفاده شوند و دانشی که از این راه به دست می‌آورید، فوق‌العاده خواهد بود!مترجم: خواندن documentation بسیار بیشتر از آنچه فکر می‌کنید مفید است. همچنین ضرری ندارد که گاهی کد فریمورک یا لایبرری‌هایی که استفاده می‌کنید را بخوانید و از طرز کار آنها سر در بیاورید.تنها در حد چالش‌هایی که داشته‌اید خوب هستید!به طور ساده، تفاوت میان Junior و Senior در تجربه است! چیزهای خیلی زیادی برای آموختن هست. گاهی از خود می‌پرسید: چه چیزی بسازم؟ و وقتی با موانع رو به رو شوید، چیزهای جدیدی خواهید آموخت.بنویسید، داکیومنت کنید و وبلاگ داشته باشید!آنچه یاد می‌گیرید و تجربه می‌کنید را با دیگران هم به اشتراک بگذارید. قرار نیست که همیشه دربارۀ یک مفهوم، حق با شما باشد و حرف درست را بزنید. خیلی اوقات از بخش کامنت‌ها چیزهای زیادی یاد گرفته‌ام و بعد با خود فکر کرده‌ام که اگر نویسنده آن پست را منتشر نمی‌کرد، دیگران هم آن کامنت‌های خوب و مفید را نمی‌نوشتند!مترجم: نوشتن کامنت برای کدهایتان را هم تمرین کنید. پروژه‌هایتان را مستندسازی کنید، Readme بنویسید و طرز کار آنها را توضیح دهید.رابطه‌سازی را شروع کنید!با توسعه‌دهندگان ملاقات کنید، در رویدادها شرکت کنید، به انجمن ها بپیوندید و با دیگران تعامل داشته باشید. در آینده به مشکلاتی خواهید خورد و این آنها هستند که در را به رویتان باز می‌کنند! شبکۀ اجتماعی Linkedin ابزار مناسبی برای برندسازی و وصل‌شدن به دیگران است.شما توصیۀ دیگری دارید؟ کامنت بگذارید!</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Wed, 06 Jul 2022 17:49:15 +0430</pubDate>
            </item>
                    <item>
                <title>پیام خطای عجیب لاراول: هست ولی نیست!</title>
                <link>https://virgool.io/@WebPajooh/no-dots-jcxwdfea66me</link>
                <description>پنیر نداریم ولی پنیر داریم!امروز گرم کارکردن بودم که اتفاق عجیبی افتاد؛ خیلی وقت بود که از Blade استفاده نکرده بودم و بعد از returnکردن ویو، پیام خطایی شبیه تصویر بالا دیدم. می‌گفت ویوی x را پیدا نکردم، منظورت ویوی x نیست؟ مطمئن بودم که ویو وجود دارد، وگرنه چنین پیشنهادی نمی‌داد، اما چرا باید نام ویویی که پیدا نشده را پیشنهاد دهد؟! اگر foo.bar وجود نمی‌داشت، بی‌معنا بود که foo.bar را به عنوان شکل درست آن پیشنهاد دهد.برای اینکه بدانیم چه اتفاقی افتاده است، باید اول بپرسیم که لاراول چگونه ویوها را پیدا می‌کند؟ وقتی از هلپر فانکشن view استفاده می‌کنیم:return view(&#039;blog.post&#039;);چگونه فایل مربوط به آن را پیدا می‌کند؟ آن هم وقتی پسوند فایل را ذکر نکرده‌ایم؟ برای درک این مطلب، سراغ کلاس Illuminate\View\FileViewFinder می‌رویم:protected $extensions = [&#039;blade.php&#039;, &#039;php&#039;, &#039;css&#039;, &#039;html&#039;];

protected function getPossibleViewFiles($name)
{
    return array_map(function ($extension) use ($name) {
        return str_replace(&#039;.&#039;, &#039;/&#039;, $name).&#039;.&#039;.$extension;
    }, $this-&gt;extensions);
}متد getPossibleViewFiles نام ویو (در مثال ما blog.post) را دریافت می‌کند و سپس حالت‌های ممکن (یعنی نام فایل به همراه پسوندهایی که در پراپرتی extensions آمده است) را به عنوان یک آرایه برمی‌گرداند. اگر دقت کنیم، از فانکشن str_replace استفاده شده است تا نقطه را تبدیل به slash کند. یعنی blog.post تبدیل به blog/post می‌شود، و به این صورت لاراول می‌تواند یک ویو را از پوشه‌های تو در تو پیدا کند.وقتی چنین باشد، ما نمی‌توانیم از نقطه در نام خود فایل استفاده کنیم! این نکته در داکیومنتیشن هم آمده است:View directory names should not contain the . character.به مثال قبلی برگردیم: نام ویو را foo.bar گذاشته بودم و لاراول تصور می‌کرد که چنین فایلی باید در این مسیر باشد:resources/views/foo/bar.{blade.php/php/css/html}از آنجا که ویویی به نام bar در آن مسیر وجود ندارد، متد findInPaths به اینجا می‌رسد:throw new InvalidArgumentException(&amp;quotView [{$name}] not found.&amp;quot);تا اینجای کار همه‌چیز طبیعی است و مشکل در کارکرد این کلاس نبود. مشکل از آنجا شروع می‌شود که پکیج facade/ignition به برنامه‌نویس پیشنهاداتی می‌دهد؛ مثلاً اگر در نوشتن نام ویو خطای تایپی داشته باشید، سعی می‌کند نام اصلی فایل را پیدا کند و پیشنهاد دهد.این اتفاق، در کلاس Facade\Ignition\SolutionProviders\ViewNotFoundSolutionProvider می‌افتد:public function getSolutions(Throwable $throwable): array
{
    $suggestedView = $this-&gt;findRelatedView($missingView);

    if ($suggestedView) {
        return [
            BaseSolution::create(&amp;quot{$missingView} was not found.&amp;quot)
                -&gt;setSolutionDescription(&amp;quotDid you mean `{$suggestedView}`?&amp;quot),
        ];
    }

    return [
        BaseSolution::create(&amp;quot{$missingView} was not found.&amp;quot)
            -&gt;setSolutionDescription(&#039;Are you sure the view exists and is a `.blade.php` file?&#039;),
    ];
}قسمت‌هایی از کدهای متد را حذف کرده‌ام تا قسمت‌هایی که مربوط به موضوع ما هستند را ببینید. با کمک متد findRelatedView تلاش می‌شود تا ویوی مرتبط با ویوی درخواست‌شده پیدا شود. اگر پیدا شد، عبارت Did you mean... برگردانده می‌شود (یعنی حالتی که کاربر خطای تایپی داشته است و ویویی با نام مشابه وجود دارد) و در غیر این صورت به کاربر پیشنهاد می‌شود که مطمئن شود که چنین ویویی وجود دارد.از آنجا که ما foo.bar را واقعاً ساخته بودیم، متد findRelatedView آن را پیدا می‌کند و سناریوی اول اتفاق می‌افتد: پیشنهاددادن!foo.bar was not found.
Did you mean foo.bar?شاید الآن خنده‌دار و جالب به نظر برسد، ولی من در ابتدا گیج شده بودم و حتی فکر می‌کردم که این دو با هم فرق دارند و چشم من از فرط خستگی قادر به تشخیص تفاوتشان نیست! اما واقعاً یکی بودند: foo.bar را پیدا نکردم، نکند که منظورت foo.bar بوده باشد؟! ?بعد از ظهر، ساعت کاری‌ام تمام شده بود و دوباره کنجکاو شدم که مشکل آن را حل کنم. به سرعت متوجه شدم که این مشکل از کلاس خود لاراول نیست، بلکه از پکیجی است که لاراول برای نمایش خطاها استفاده می‌کند. ریپازیتوری را فورک کردم و تغییرات لازم را برای حل این مشکل انجام دادم: https://github.com/facade/ignition/pull/457 خوشبختانه ساعتی قبل این pull request تایید شد و پیام Freek شبم را ساخت:Thanks!نتیجه‌گیریِ اخلاقیِ پست این بود که همیشه داکیومنتیشن را بخوانید تا اشتباهات عجیبی مانند آنچه صبح مرتکب شدم -استفاده از نقطه در نام ویو- را مرتکب نشوید؛ نقطه ممنوع!</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Thu, 30 Jun 2022 23:51:52 +0430</pubDate>
            </item>
                    <item>
                <title>فهمیدن LSP - یک‌بار برای همیشه!</title>
                <link>https://virgool.io/@WebPajooh/liskov-substitution-principle-in67fgqwv8ir</link>
                <description>مقدمهاگر به دنبال یادگیری اصول SOLID بوده باشید، احتمالا فهم اصل سوم برایتان دشوار بوده است. مقالات بسیاری در فضای مجازی فارسی وجود دارند که غالبا حق مطلب را ادا نکرده و تنها مطالب سایت‌های دیگر را تکرار می‌کنند و همین باعث شده که بعد از خواندنشان دچار سرگردانی و گیجی شویم یا اینکه به اشتباه فکر کنیم که این اصول را فهمیده‌ایم، با اینکه چیزی دست‌گیرمان نشده است!این اصل، نام عجیبی دارد که احتمالا آدم را بترساند، اما حداقل فهمیدن نام آن سخت نیست! منظور از کلمۀ اول این عبارت (Liskov)، باربارا لسکوف است که یک دانشمند حوزۀ کامپیوتر بود و کلمۀ دوم (Substitution) به معنای استفاده‌کردن چیزی به جای چیزی دیگر است. کلمۀ دوم برای ما مهم است، زیرا کلید فهم این اصل در همین‌جاست: جایگزینی!تعریفLet Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T.یا واضح‌تر از آن، اینکه:If S is a subtype of T, then objects of type T may be replaced with objects of type S, without breaking the program.بیایید T را superclass (مثل Animal) و S را یک subclass فرض کنیم (مثل Cat)؛ اگر آبجکتی از T را در جایی استفاده می‌کنیم، باید آبجکتی از S هم بتواند جایگزین آن شود و کار همچنان ادامه بیابد. اگر این جابه‌جایی دردسرساز شد، یعنی LSP نقض شده است.مثال‌هامثال 1: مستطیل و مربعمثال مشهور مستطیل و مربع! به کد زیر دقت کنید:&lt;?php

class Rectangle
{
    private $width;
    private $height;

    public function __construct($width, $height)
    {
        $this-&gt;width = $width;
        $this-&gt;height = $height;
    }

    public function setWidth($width)
    {
        $this-&gt;width = $width;
    }

    public function setHeight($height)
    {
        $this-&gt;height = $height;
    }

    public function getArea()
    {
        return $this-&gt;width * $this-&gt;height;
    }
}

$shape = new Rectangle(2, 3);
$shape-&gt;setWidth(4);
$shape-&gt;setHeight(6)
assert($shape-&gt;getArea() == 24); // 4 x 6 = 24همه‌چیز خوب کار می‌کند و همه خوشحال‌اند، تا اینکه مشتری تماس می‌گیرد و پشتیبانی از مربع را هم می‌خواهد! با خودمان فکر می‌کنیم که هر مربع را می‌شود مستطیل هم در نظر گرفت، پس کلاس مربع می‌تواند از کلاس مستطیل که قبلاً ساخته بودیم، ارث‌بری کند. از آنجا که طول و عرض مربع برابر است، نباید به کاربر اجازه داد تا از مربع، مستطیل بسازد! کد مربوط به کلاس مربع را ببینیم:&lt;?php

class Square extends Rectangle
{
    public function setWidth($width)
    {
        $this-&gt;width = $width;
        $this-&gt;height = $width;
    }

    public function setHeight($height)
    {
        $this-&gt;height = $height;
        $this-&gt;width = $height;
    }
}سپس... جابه‌جایی مستطیل با مربع اتفاق می‌افتد:$shape = new Square (2, 2);
$shape-&gt;setWidth(4);
$shape-&gt;setHeight(6);
assert($shape-&gt;getArea() == 24); // fatal error, 6 x 6 = 36کلاس مربع، رفتار کلاس پدر خود یعنی مستطیل را تغییر داده است و چنین اتفاق غیر منتظره‌ای افتاد. این یعنی آبجکتی از Square نمی‌تواند جایگزین آبجکتی از Rectangle شود و همین نشان می‌دهد که در طراحی اشتباه کرده‌ایم!مثال 2: اسلحه وسیلۀ بازی نیست!این، شکل دیگری از مثال معروف اردک است. به کد زیر دقت کنید:&lt;?php

class Gun
{
    public function shoot()
    {
        // 50 lines of code
    }
}

class ToyGun extends Gun
{
    public function shoot()
    {
        throw new Exception(&#039;We don\&#039;t do that here!&#039;);
    }
}کد دیگری، از سوپرکلاس Gun استفاده می‌کرده است:public function use(Gun $gun)
{
    $gun-&gt;shot();
}کد استفاده‌کننده از Gun، انتظار هیچ Exceptionای را نمی‌کشیده است و تغییر رفتار Gun توسط ToyGun اصل سوم SOLID را نقض کرد. اصلاً چه کسی گفته که تفنگ اسباب‌بازی باید از تفنگ ارث‌بری کند؟ ارتباط در Inheritance، یک is-a است اما:Toy gun is not a gun!چنین مواردی به ما گوشزد می‌کنند که is-a راه‌حل همیشگی نیست و گاهی باید سراغ has-a برویم، یعنی: Composition! اما بگذریم... نکتۀ مثال دوم این است که اگر کلاس پدر یک نوع از Exception را throw کرد، کلاس‌های فرزند نباید چیزی فراتر از آن را throw کنند زیرا کد استفاده‌کننده (Client) آمادگی چنین چیزی را ندارد! آقای Alexander Shvets می‌گوید:Types of exceptions should match or be subtypes of the ones that the base method is already able to throw.یعنی نوع Exceptionها باید دقیقاً همان‌هایی باشد که بوده، یا زیرمجموعۀ آنها باشد و دیگر از Exception جدیدی استفاده نشود. اگر تا الآن خبر نداشتید که در PHP خودمان می‌توانیم Exception بسازیم و از آنها ارث‌بری کنیم، این بخش از داکیومنتیشن را بخوانید!مثال 3: آپلودر جدید!به کد زیر دقت کنید:&lt;?php

class File { }
class Document extends File { }
class PDFDocument extends Document { }

class Uploader
{
    public function upload(Document $file)
    {
        // 100 lines of code
    }
}

$file = new Document;
$uploader = new Uploader;
$uploader-&gt;upload($file);این کد به خوبی کار می‌کند، اما تصور کنید کلاس جدیدی بر اساس کلاس Uploader به نام FTPUploader بسازیم و متد upload را override کنیم. LSP به ما می‌گوید که نوع آرگومانی که به این متد داده می‌شود، باید همان Document یا چیزی عام‌تر از آن باشد.کلاس File از کلاس Document عام‌تر است؛ زیرا هر Document یک File حساب می‌شود اما هر File لزوماً یک Document نیست. همچنین کلاس Document از کلاس PDFDocument عام‌تر است، زیرا هر PDFDocument یک Document هم حساب می‌شود، اما Document می‌تواند علاوه بر pdf، فرمت docx یا txt هم داشته باشد.بعد از فهمیدن پاراگراف بالا، کلاس جدید را ببینید:class FTPUploader extends Uploader
{
    public function upload(... $file)
    {
        // 100 lines of code
    }
}جای یک Type در ... خالی است! با توضیحاتی که گفته شد، متدِ کلاسِ جدید می‌تواند Document و همچنین File را بپذیرد (File از Document عام‌تر است)، اما PDFDocument از نوع پارامتری که کلاس پدر می‌گرفت خاص‌تر است و LSP را نقض می‌کند. اگر PDFDocument را به جای Type قرار دهیم، و کلاس FTPUploader را جایگزین کلاس Uploader کنیم:$file = new Document;
$uploader = new FTPUploader; // Look at here
$uploader-&gt;upload($file);با خطای زیر مواجه خواهیم شد:Fatal error: Declaration of FTPUploader::upload(PDFDocument $file) must be compatible with Uploader::upload(Document $file)اما اگر Type را File در نظر بگیریم، این خطا را نمی‌بینیم؛ زیرا Document هم یک File است و در نتیجه کد به درستی کار خواهد کرد!مثال 4: کالکشن به جای آرایه!این مثال را در ویدئویی از Jeffrey Way دیده بودم و شاید از مثال‌های گذشته قابل‌درک‌تر باشد. در لاراول، کلاسی به نام Collection داریم که چیزی شبیه آرایه اما بسیار قدرت‌مندتر ارائه می‌کند. اگر superclass یک متد با خروجی array دارد، همان متد در subclass هم باید خروجی array داشته باشد؛ زیرا اگر چنین نباشد، در صورت جایگزین‌کردن subclass با superclass، امکان بروز خطا وجود خواهد داشت:$scores = (new SubClass())-&gt;getScore(); // Returns a collection
return array_sum($scores);از آنجا که فانکشن array_sum با آرایه‌ها کار می‌کند و کدی که از کلاس استفاده می‌کرد، توقع یک آرایه را از متد getScore داشت، با گذاشتن subclass به جای superclass، خطای زیر را خواهیم دید:array_sum(): Argument #1 ($array) must be of type array, Illuminate\Support\Collection givenمثال 5: اما و اگر نداریم!کلاس A متدی به نام submitPoints دارد که یک آرگومان int را می‌پذیرد. اگر کلاس B طوری آن را override کند که تنها اعداد مثبت را بپذیرد:public function submitPoints(int $points)
{
    if ($points &lt; 1) {
        throw new Exception(&#039;I want you to give me positive numbers!&#039;);
    }

    // Logic here
}قوی‌کردن پیش‌شرط‌ها خوب نیست و کدی که با کلاس قبلی کار می‌کرده و اعداد منفی هم وارد آن می‌شده، با جایگزین‌شدن کلاس جدید، به مشکل خواهد خورد!مثال 6: کانکشن‌های باز و کلاس دردسرساز!این مثال شبیه مثال قبلی است، اما به پس‌شرط‌ها (post-conditions) مربوط می‌شود. مثال Shvet این است که کلاس A کانکشن‌های دیتابیس را بعد از استفاده می‌بسته، اما کلاس جدید چنین نمی‌کند و کانکشن‌ها را برای استفادۀ مجدد، زنده نگه می‌دارد! کدی که از کلاس استفاده می‌کرده، از قصد برنامه‌نویس خبر ندارد و وقتی جایگزینی B به جای A صورت بگیرد، کانکشن‌های باز زیادی باقی خواهد ماند!البته ما اهمیت خاصی به بستن کانکشن‌های MySQL نمی‌دادیم و دلیلش را هم می‌دانید، اما به نظرم مثال خوب و واضحی است.نتیجه‌گیریبا اصل سوم SOLID یعنی Liskov Substitution Principle طراحی‌های بهتری خواهیم داشت و ارث‌بری را صرفاً برای افزودن متدهای جدید امتحان نخواهیم کرد. در یک زنجیره (برای نمونه، زنجیرۀ حیوانات را تصور کنید)، آبجکت هر کلاس باید بدون مشکل با آبجکتی از کلاس دیگری که subtype است جایگزین شود و کد به کار خود ادامه دهد. امیدوارم این مقاله به شما کمک کرده باشد و فهم بهتری از LSP به دست آورده باشید.منابع:Dive Into Design Patterns, by Alexander Shvets.Clean Architecture, by Robert Martin.https://laracasts.com/series/solid-principles-in-php/episodes/3</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Mon, 27 Jun 2022 17:58:37 +0430</pubDate>
            </item>
                    <item>
                <title>قابلیت کرم‌چاله در تست لاراول</title>
                <link>https://virgool.io/@WebPajooh/laravel-wormhole-toaraihzviwj</link>
                <description>کرم‌چاله را می‌توان به صورت تونلی تصور کرد که پایانه‌های آن در نقاط مجزایی از فضا-زمان قرار دارند.صورت مسئلهدر حال ساختن وب‌سایت برای یک رویداد خیلی مهم هستیم. افراد برای این رویداد خیلی مهم صندلی رزرو می‌کنند و مهلت رزرو بلیت تا آخر ماه آوریل است. کد زیر یک مثال برای کنترل این وضعیت است:کد رزرواسیوناکنون می‌خواهیم برای این کد، تست بنویسیم...نوشتن تست و مشکل زمانبا صرف نظر از Validation، دو حالت قابل تصور است:تاریخ امروز قبل از سی‌ام آوریل است و یک id در پاسخ برگردانده می‌شود.تاریخ امروز بعد از سی‌ام آوریل است و پیام خطا مشاهده می‌شود.پس ما باید زمان فعلی را اصطلاحاً fake کنیم تا بتوانیم برای این دو حالت فرضی تست بنویسیم، وگرنه زمان فعلی، همان زمان اجرای تست در نظر گرفته خواهد شد و باید تا روز اول ماه می صبر کنیم و بعد دوباره تست را اجرا کنیم تا مطمئن شویم که سناریوی دوم درست کار می‌کند! (چه احمقانه)استفاده از متد setTestNowکتابخانۀ Carbon یک static method به نام setTestNow به این منظور نوشته بود که قبلاً استفاده می‌شد. با همین روش دو تست برای حالت‌های اول و دوم می‌نویسیم:public function test_people_can_reserve_before_the_deadline()
{
    $data = [
        &#039;email&#039; =&gt; &#039;alireza@gmail.com&#039;,
        &#039;age&#039; =&gt; 25,
    ];

    Carbon::setTestNow(Carbon::createFromDate(2022, 04, 29));

    $this-&gt;post(&#039;/api/reservations&#039;, $data)
        -&gt;assertJsonStructure([&#039;id&#039;]);
}public function test_people_can_reserve_after_the_deadline()
{
    $data = [
        &#039;email&#039; =&gt; &#039;parsa@gmail.com&#039;,
        &#039;age&#039; =&gt; 23,
    ];

    Carbon::setTestNow(Carbon::createFromDate(2022, 04, 30));

    $this-&gt;post(&#039;/api/reservations&#039;, $data)
        -&gt;assertJson([&#039;error&#039; =&gt; &amp;quotIt&#039;s too late!&amp;quot]);
}کرم‌چالۀ لاراولدر لاراول 8 قابلیتی اضافه شد که سفر در زمان را آسان‌تر کرد. کلاس TestCase از تریت Concerns\InteractsWithTime استفاده می‌کند که متدهایی را برای تغییر زمان پیاده کرده‌اند. اکنون به جای متد setTestNow به صورت زیر عمل می‌کنیم:$this-&gt;travelTo(Carbon::createFromDate(2022, 04, 29));گاهی نیاز داریم که زمان را به طور نسبی تغییر دهیم، مثلاً پنج ساعت قبل را شبیه‌سازی کنیم. در اینجا از متد travel استفاده می‌کنیم که عددی را به عنوان ورودی می‌گیرد و آبجکت Wormhole را برمی‌گرداند که متدهای جالبی روی آن پیاده شده است:$this-&gt;travel(-5)-&gt;hours();اگر بخواهیم به زمان حال برگردیم، از متد travelBack استفاده می‌کنیم:$this-&gt;travelBack();همچنین اگر بخواهیم که زمان را در هنگام اجرای تعدادی از دستورات متوقف کنیم، متد freezeTime را استفاده می‌کنیم که یک closure به عنوان آرگومان می‌پذیرد.انتخاب عنوان‌هایی مثل travel یا Wormhole که بیشتر از انتزاع به واقعیت نزدیک است می‌تواند ایدۀ جالبی باشد و حتی خوانایی کدها را به نحوی بالاتر ببرد. چنین قابلیت‌هایی نوشتن تست را آسان‌تر کرده و به گسترش فرهنگ تست‌نویسی کمک می‌کنند.برای مطالعۀ بیشتر، به مستندات رسمی لاراول مراجعه کنید:https://laravel.com/docs/9.x/mocking#interacting-with-time</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Thu, 14 Apr 2022 18:08:46 +0430</pubDate>
            </item>
                    <item>
                <title>Pruning Models</title>
                <link>https://virgool.io/laravel-community/pruning-models-hdzkl81ne5g5</link>
                <description>تا به حال در پروژه‌ای نیاز به این داشته‌اید که رکوردهای قدیمی دیتابیس را حذف کنید؟ در مواردی نیاز داریم که همه‌چیز باقی بماند، مثل تیکت‌های پشتیبانی که سوابق ضروری سرویس‌گیرنده هستند. اما شرایطی را در نظر بگیرید (اپلیکیشنی مثل دیوار که نیازی به آرشیو آگهی‌هایش ندارد، یا شاید هم دارد؟) که واقعا نیازی به رکوردهای قدیمی نداشته باشیم و بخواهیم آنها را با شروط خاصی حذف کنیم.لاراول برای این هم راه‌حل دارد!آقای Nuno Maduro در یک Pull Request پیشنهاد کرد که چنین قابلیتی به لاراول اضافه شود تا با پیامی زمان‌بندی‌شده، رکوردها را به صورت اتوماتیک حذف کنیم و این پیشنهاد بسیار مورد استقبال قرار گرفت. در مستندات لاراول، این خاصیت را به این شیوه تعریف کرده‌اند:Sometimes you may want to periodically delete models that are no longer needed.شاید گاهی بخواهید که به صورت دوره‌ای، مدل‌هایی که دیگر نیازی به آنها نیست را حذف کنید.فعل Prune در انگلیسی به معنای هرس‌کردن است. انگار با حذف‌کردن رکوردهایی که نیاز نداریم، جدول مربوط به آن مدل‌ها را هرس می‌کنیم.چگونه از آن استفاده کنیم؟ابتدا Trait مربوط به آن یعنی Prunable را به مدلی که می‌خواهیم اضافه می‌کنیم:class Ticket extends Model
{
    use Prunable;اما لاراول از کجا بداند که ما به چه رکوردهایی نیاز نداریم؟! در Prunable که به عنوان Trait اضافه شد، متدی به نام ()prunable وجود دارد که خروجی آن از جنس Builder است و اگر در مدل پیاده‌سازی نشود، بعدها یک Exception بر خواهد گرداند. من چنین شرطی گذاشتم:public function prunable()
{
    return static::where(&#039;created_at&#039;, &#039;&lt;&#039;, now()-&gt;subMonth());
}پس هر رکوردی که تاریخ ایجاد آن از یک ماه پیش قدیمی‌تر باشد، حذف خواهد شد. می‌توانیم شروطی دیگری هم اضافه کنیم:public function prunable()
{
    return static::where(&#039;created_at&#039;, &#039;&lt;&#039;, now()-&gt;subDay())
        -&gt;where(&#039;is_vip&#039;, false);
}حالا می‌توانیم با دستور model:prune به آزمایش آنچه انجام داده‌ایم بپردازیم:λ php artisan model:prune
571 [App\Models\Ticket] records have been pruned.عالی شد! با این دستور، مدل‌هایی که Prunable (قابل هرس) بودند به طور خودکار شناسایی شده و رکوردهای غیر ضروریشان (بر اساس متد prunable که خودمان تعریف کردیم) حذف خواهد شد.البته برای آسان‌ترشدن کار، از متد schedule کلاس App\Console\Kernel برای زمان‌بندی استفاده می‌کنیم تا این اتفاق به صورت روزانه تکرار شود:protected function schedule(Schedule $schedule)
{
    $schedule-&gt;command(&#039;model:prune&#039;)-&gt;daily();
}گزینه‌ی pretend هم روی میز است!گاهی قصد جدی برای حذف رکوردها نداریم و فقط می‌خواهیم بدانیم که اگر دستور model:prune را اجرا کنیم، چند رکورد حذف خواهد شد. در این صورت از یک option به نام pretend استفاده می‌کنیم:λ php artisan model:prune --pretend                                                                               
58 [App\Models\Password] records will be pruned.گزینه‌ی chunkدستور model:prune یک گزینه‌ی دیگر به نام chunk دارد که توضیحش را در مستندات لاراول ندیدم. با این گزینه مشخص می‌کنیم که در هر chunk، چند مدل برای حذف‌شدن برگردانده شود و مقدار پیشفرضش 1000 است:λ php artisan model:prune --chunk=100متد ()pruningیکی از متدهای تریت Prunable، متدی به نام pruning است که مشخص می‌کند قبل از حذف یک مدل چه اتفاقی بیفتد. مثلاً بلیتی که ما تولید می‌کنیم یک فایل pdf روی سرور دارد و باید با حذف رکوردهای قدیمی، آن فایل‌ها هم حذف شوند یا دیوار را در نظر بگیرید که باید تصاویر، گزارشات و دیگر موارد مربوط به آگهی را هم حذف کند. در این صورت از متد pruning در کلاس مربوط به مدل بلیت استفاده می‌کنیم و کدهای دلخواهمان را در آن می‌نویسیم:public function pruning()
{
    $this-&gt;file&#40;&#41;-&gt;delete();
}حتما می‌دانید که this$ به نمونه‌ای از کلاس فعلی اشاره دارد و اگر باور نمی‌کنید، با ()dd چیزی که گفتم را آزمایش کنید!کمی عمیق‌تر!دستور model:prune چگونه کار می‌کند؟ اگر با Commandها آشنایی ندارید، توصیه می‌کنم بخش مربوط به Console از مستندات لاراول را مطالعه کنید.کلاس PruneCommand یک Command است:Illuminate\Database\Console\PruneCommandبا اجرای دستور model:prune متد handle این کلاس اجرا می‌شود كه کد تمیزی دارد:$models = $this-&gt;models();مدل‌هایی که تریت Prunable را استفاده کرده‌اند در متغیر models$ جمع می‌شوند.if ($models-&gt;isEmpty()) {
    $this-&gt;info(&#039;No prunable models found.&#039;);
    return;
}اگر هیچ مدل Prunableای در پروژه نباشد، یک پیام برگردانده می‌شود و همه‌چیز تمام می‌شود!if ($this-&gt;option(&#039;pretend&#039;)) {
    $models-&gt;each(function ($model) {
        $this-&gt;pretendToPrune($model);
    });
    return;
}اگر گزینه‌ی pretend وارد شده باشد، متد pretendToPrune را برای تک‌تک آنها اجرا می‌کند. اما اگر اینطور نبود؟$events-&gt;listen(ModelsPruned::class, function ($event) {
    $this-&gt;info(&amp;quot{$event-&gt;count} [{$event-&gt;model}] records have been pruned.&amp;quot);
});یک Event Listener برای رویداد ModelsPruned ثبت می‌کند. این رویداد در متد ()pruneAll تریت Prunable بعد از حذف‌کردن مدل‌ها و شمارش آنها اتفاق خواهد افتاد. پیامی که در ترمینال خواهیم دید، توسط این Event Listener تولید می‌شود.$models-&gt;each(function ($model) {
    $instance = new $model;

    $chunkSize = property_exists($instance, &#039;prunableChunkSize&#039;)
        ? $instance-&gt;prunableChunkSize
        : $this-&gt;option(&#039;chunk&#039;);

    $total = $this-&gt;isPrunable($model)
        ? $instance-&gt;pruneAll($chunkSize)
        : 0;

    if ($total == 0) {
        $this-&gt;info(&amp;quotNo prunable [$model] records found.&amp;quot);
    }
});به ازای هر مدل، این کارها انجام می‌شود: یک نمونه به نام instance ساخته می‌شود. سپس اگر روی مدل، مشخصه‌ای به نام prunableChunkSize تعریف شده باشد، آن را به عنوان سایز هر قطعه در نظر می‌گیرد (این نکته در مستندات لاراول نیست) وگرنه از گزینه‌ی chunk که در خط فرمان تعیین شده باشد استفاده می‌کند. اگر خواستید مقدار آن را در مدل تعیین کنید، کار سختی در پیش ندارید:class Ticket extends Model
{
    public $prunableChunkSize = 200;بگذریم... بعد از آن تعداد مدل‌های حذف‌شده در total$ قرار داده می‌شود و اگر total$ برابر صفر باشد (یعنی رکوردی برای حذف نباشد)، پیام مناسبی نمایش داده خواهد شد. صدازدن متد ()pruneAll روی instance$ بخش مهم کار را انجام می‌دهد:public function pruneAll(int $chunkSize = 1000)
{
    $total = 0;
    $this-&gt;prunable()
        -&gt;when(in_array(SoftDeletes::class, class_uses_recursive(get_class($this))), function ($query) {
        $query-&gt;withTrashed();
    })-&gt;chunkById($chunkSize, function ($models) use (&amp;$total) {
        $models-&gt;each-&gt;prune();
        $total += $models-&gt;count();
        event(new ModelsPruned(static::class, $total));
    });

    return $total;
}این متد در تریت Prunable قرار دارد و می‌توانید dispatchشدن رویداد ModelsPruned را هم در آن ببینید. ماجرا با متد prunable آغاز می‌شود که خودمان آن را در مدل تعریف کردیم و یک نمونه‌ی Builder را return کرد.امیدوارم که از مقاله استفاده کرده باشید. این مقاله شکوه لاراول را نشان می‌دهد و همچنین اهمیت مطالعه‌ی کدها و اتفاقاتی که پشت پرده رخ می‌دهند. با خواندن کدهای ابزاری که با آن کار می‌کنیم، درک عمیق‌تری از آن خواهیم داشت و این روی خروجی کارمان تاثیرات به‌سزایی خواهد گذاشت.کد تمیز بخوانید و کد تمیز بنویسید. ?</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Sat, 30 Oct 2021 21:24:32 +0330</pubDate>
            </item>
                    <item>
                <title>چرا نرم‌افزارها تغییر می‌کنند؟</title>
                <link>https://virgool.io/@WebPajooh/software-changes-mgng2zxicv2h</link>
                <description>&quot;همه‌چیز تغییر می‌کند و دیگر هیچ‌چیزی مانند سابق نخواهد بود... زندگی همین است! زندگی به پیش می‌رود و ما هم باید چنین باشیم!&quot; (اسپنسر جانسون: چه کسی پنیر مرا جابه‌جا کرد؟)تغییر نرم‌افزار، غیر قابل اجتناب است. نه تنها مشکلی ندارد، می‌تواند عالی هم باشد! زیر نظر گرفتن تغییرات، معلّم خوبی است و به ما کمک می‌کند که حوزه‌ی تخصصی خود را بهتر و عمیق‌تر درک کنیم و بسیاری از اشتباهات را مرتکب نشویم. مادری را مثال می‌زنم که اولین فرزند خود را به دنیا می‌آورد و رشد کودکش را زیر نظر می‌گیرد و از آن تجاربی به دست می‌آورد؛ بعد از تربیت چند کودک، آشنایی بسیار خوبی با مراحل رشد و مقتضیات سنین مختلف خواهد داشت.البته ما نرم‌افزار را بی‌علت تغییر نمی‌دهیم! مايكل فزرس (Michael Feathers) چهار دلیل عمده برای تغییر نرم‌افزارها ذکر می‌کند:اضافه‌کردن یک قابلیتنیاز مشتریان تا ابد ثابت نمی‌ماند و ممکن است نیازهای جدیدی داشته باشند. از سویی دیگر، محصول شما رقیبانی در بازار دارد که نباید از آنها عقب بمانید. فرض کنید برای شرکتی کار می‌کنید که محصولی مشابه Medium را می‌سازد. قابلیت‌های جدید Medium را پیاده‌سازی می‌کند و همچنین نیازهای دیگری که کاربران ایرانی دارند را برآورده می‌کند، مانند امکان درون‌ریزی (import) از سیستم‌های وبلاگ‌دهی که در حال شکست هستند.اصلاح‌کردن یک باگباگ نرم‌افزاری، یک ارور یا نقص فنی در یک برنامه‌ی کامپیوتری است که می‌تواند منجر به یک نتیجه‌ی اشتباه یا غیر قابل پیش‌بینی شود.یکی دیگر از دلایل تغییر نرم‌افزار، اصلاح‌کردن باگ‌ها (fixing bugs) است. اصطلاح باگ به شکل وسیعی قبل از حوزه‌ی نرم‌افزار، در حوزه‌های دیگر مهندسی به کار می‌رفته و حتی ادیسون هم از آن استفاده کرده است! باگ‌ها انواع زیادی دارند؛ بعضی منجر به خطایی بحرانی می‌شوند که برنامه را متوقف می‌کند و بعضی دیگر موجب عملکرد نادرست یا مشکلات امنیتی می‌گردند. پس از اینکه نرم‌افزار منتشر شود، توسط کاربران زیادی در شرایط مختلف استفاده می‌شود و همین، اشکالات زیادی را به ما خواهد شناساند. برنامه‌نویسان برای حل اشکالات شناسایی‌شده، نرم‌افزار را تغییر می‌دهند و این تغییر می‌تواند در هر سطحی اتفاق بیفتد.بهبود طراحیاین نوع متفاوتی از تغییر نرم‌افزار است. می‌خواهیم ساختار نرم‌افزار را به نحوی تغییر دهیم که قابلیت نگهداری (maintainability) افزایش یابد، اما رفتار (behavior) آن دست‌نخورده و سالم باقی بماند! مایکل فزرس می‌گوید:The act of improving design without changing its behavior is called refactoring.یعنی عمل بهبود طراحی نرم‌افزار -بدون اینکه رفتار آن تغییر کند- را ریفکتورینگ می‌گوییم. مارتین فولر (Martin Fowler) هم دو تعریف برای ریفکتورینگ آورده که یکی بنا بر فرم اسمی (noun form: refactoring) و دیگری بنا بر فرم فعلی (verb form: refactor) آن است. بر اساس تعریف اول:Change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.تغییرات به ساختار داخلی نرم‌افزار اعمال می‌شود تا برای دیگران قابل‌فهم‌تر و دستکاری آن -بدون تغییر رفتار قابل مشاهده‌اش- ارزان‌تر باشد.امیدوارم آنچه گفته شد به شما دید مناسبی از ریفکتورینگ داده باشد. وقتی نرم‌افزار را برای تحقق اهداف کوتاه‌مدت تغییر می‌دهیم و به ساختار کلی پروژه توجه نمی‌کنیم، نرم‌افزار به سوی خرابی می‌رود و نگهداری آن مشکل می‌شود؛ در آینده‌ی نه‌چندان دور به آسانی قادر به اعمال تغییرات و افزودن قابلیت‌های جدید نخواهیم بود و کارمان شبیه یافتن سوزن در انبار کاه خواهد شد. اما ریفکتورینگ مداوم، شکل نرم‌افزار را حفظ کرده و از اینکه غیر قابل کنترل شود، جلوگیری می‌کند.فایده‌ی دیگر ریفکتورینگ این است که فهم نرم‌افزار را آسان می‌کند. شما کدهایتان را تنها برای کامپیوتر نمی‌نویسید بلکه افراد دیگری هم مجبور به خواندن آنها هستند تا نرم‌افزار را توسعه دهند، پس باید کدی بنویسید که ساختار ایده‌آلی داشته باشد و منظور خود را به وضوح بیان کند! رابرت مارتین (Robert Martin) در مقدمه‌ی Clean Code تعریف‌های مختلفی از «کد تمیز» آورده و واحد WTFs/minute را برای اندازه‌گیری کیفیت کد در بازبینی (review) معرفی می‌کند که جای تأمل دارد، خودتان باید کتاب را بخوانید!مارتین فولر فایده‌ی دیگری برای ریفکتورینگ ذکر می‌کند و آن هم کشف باگ‌هاست؛ او می‌گوید که من در شناسایی باگ‌ها از طریق خواندن کد، خیلی خوب نیستم، ولی وقتی به ریفکتورینگ می‌پردازم، با درکی که از ساختار کد پیدا کرده‌ام، باگ‌ها را شناسایی می‌کنم!آیا ریفکتورینگ باعث توسعه‌ی سریع‌تر هم می‌شود؟ جواب مثبت است زیرا از راهی که قبلاً صاف و هموار کرده‌اید عبور می‌کنید!بهینه‌سازی مصرف منابعبهینه‌سازی (optimization) مانند ریفکتورینگ است، اما هدف متفاوتی از طریق آن دنبال می‌شود. در خصوص هردویشان می‌گوییم که «ما عملکرد را دقیقاً همانطوری که هست باقی می‌گذاریم و قصدمان چیز دیگری است»، ولی در ریفکتورینگ آن «چیز دیگر» ساختار برنامه است؛ یعنی می‌خواهیم نگهداری برنامه را آسان‌تر کنیم. درباره‌ی بهینه‌سازی چطور؟ در اینجا «چیز دیگر» نحوه‌ی استفاده از منابع (معمولاً زمان و حافظه) است و می‌خواهیم مصرفشان را کاهش دهیم تا بازدهی نرم‌افزار افزایش یابد.بعد از تمام این‌ها، شما را به خواندن مقاله‌ی زیر دعوت می‌کنم:https://virgool.io/@WebPajooh/soghote-narmafzarha-ap2pkaaw4om1منابعی که این مقاله را شکل دادند:Working Effectively with Legacy Code by Michael Feathers.Refactoring by Martin Fowler.https://en.wikipedia.org/wiki/Software_bug</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Fri, 03 Sep 2021 20:13:59 +0430</pubDate>
            </item>
                    <item>
                <title>چگونه نرم‌افزارها سقوط می‌کنند؟</title>
                <link>https://virgool.io/@WebPajooh/soghote-narmafzarha-ap2pkaaw4om1</link>
                <description>یک نرم‌افزار به هزار و یک دلیل سقوط می‌کند. کاری با بازاریابی ناکارآمد یا دلایل ناشناخته‌ی دیگر ندارم و تنها چند تجربه‌ی فنی خودم را می‌نویسم. چرا یک نرم‌افزار با گذر زمان شکننده‌تر می‌شود و هرچه جلوتر بروید، کنترل و تسلط کمتری احساس می‌کنید؟نداشتن تستزمانی تست‌نوشتن را بیهوده می‌دانستم و درک نمی‌کردم که تست‌نوشتن چه فایده‌ای دارد. بعداً با پروژه‌های مختلفی سر و کار داشتم که بزرگ‌تر و بزرگ‌تر می‌شدند و تغییردادنشان ریسک داشت و ممکن بود با تغییر یک متد، قسمت‌های مختلف پروژه از کار بیفتند. تست‌ها باید به صورت استاندارد و با دقت نوشته شوند و بخش‌های مختلف نرم‌افزار را پوشش دهند. برای هر چیزی که ممکن است در آینده با مشکل مواجه شود (یعنی تقریباً همه‌چیز D:) تست بنویسید.در این باره، Michael Feathers نوشته است:The tests that we write to characterize code are very important. They are the documentation of the system’s actual behavior. Like any documentation that you write, you have to think about what will be important to the reader. Put yourself in the reader’s shoes. (Working Effectively with Legacy Code, p189)تست‌هایی که برای توصیف کد خود می‌نویسیم، بسیار مهم هستند. آنها مستندات رفتار واقعی سیستم هستند. مثل هر مستنداتی که می‌نویسید، باید به این فکر کنید که چه چیزی برای خواننده اهمیت دارد. خودتان را در کفش‌های خواننده قرار دهید و جای او باشید.رابرت مارتین هم در کتاب Clean Code مانند آن را می‌گوید:Well-written unit tests are also expressive. A primary goal of tests is to act as documentation by example. Someone reading our tests should be able to get a quick understanding of what a class is all about. (Clean Code, p175)یونیت‌تست‌هایی که خوب نوشته شده باشند، واضح و رسا هستند. یکی از اهداف اصلی تست‌ها این است که به عنوان مستندات عمل کنند و کسی که تست‌هایمان را می‌خواند، فهم مختصری از اینکه یک کلاس چه‌کاره است به دست بیاورد.نداشتن مستنداتپروژه‌ای که مستندات نداشته باشد، مانند جعبه‌ای است که راهی برای کشف محتویاتش نیست، جز اینکه آن را تکان دهیم! مستندات باید کلید فهم ساختار پروژه، موجودیت‌ها و اصطلاحات به‌کاررفته در پروژه را به برنامه‌نویسان جدید بدهد و آنها را در کمترین زمان با کارهایی که در گذشته انجام شده آشنا کند.استفاده‌نکردن از نام‌های گویانام جدول‌های دیتابیس، ماژول‌ها، کلاس‌ها، متدها و متغیرها را با دقت انتخاب کنید، حتی اگر به قیمت طولانی‌شدنشان تمام شود! این موضوع آن قدر مهم است که رابرت مارتین، فصل دوم کتاب Clean Code را به آن اختصاص داده است و می‌گوید:Choosing good names takes time but saves more than it takes. So take care with your names and change them when you find better ones. Everyone who reads your code (including you) will be happier if you do. (Clean Code, p18)انتخاب نام خوب، وقت‌گیر است ولی در عوض وقت بیشتری را به شما برمی‌گرداند، بنابراین مراقب نام‌هایی که انتخاب می‌کنید باشید و هر وقت جایگزین بهتری پیدا کردید، آنها را تغییر دهید. اگر چنین کنید، کسانی که کد شما را می‌خوانند (از جمله خود شما) شادتر خواهند بود!شاید کلاس CheckUser برای شما آشنا باشد، ولی نفر بعدی که به تیم می‌پیوندد و روی کد شما کار می‌کند هیچ درکی از آن نخواهد داشت و باید به اجبار تمام کد را بخواند تا بفهمد منظور از CheckUser چیست و این کلاس چه مسئولیتی دارد!همه‌چیز را به روش خودتان انجام می‌دهیدبرنامه‌نویسان می‌توانند زبان مشترکی داشته باشند و حرف همدیگر را بهتر بفهمند. فریم‌ورک لاراول Queue را پیاده‌سازی کرده است و هر برنامه‌نویسی که با آن آشنا باشد، می‌فهمد که پوشه‌ی Jobs برای چیست و Job چه کاری دارد. اما تصور کنید که Queue خودم را از صفر پیاده‌سازی کرده باشم و نامش را Servant بگذارم، چه کسی آن را کشف می‌کند؟! قابلیت‌های فریم‌ورک را بشناسید و چرخ را از نو اختراع نکنید، دیزاین‌پترن‌ها را به خوبی یاد بگیرید و در جای مناسب به کار ببرید تا زبان مشترک بین شما و سایر برنامه‌نویسان باشند. اگر چنین باشد، آنها خواهند دانست که کلاس CoffeeFactory یک Factory است!ساختار نادرستابتدا یک نیاز وجود دارد، شما هم یک کلاس با یک متد می‌نویسید. بعد از مدتی، قابلیت جدیدی خواسته می‌شود و شما هم یک متد دیگر اضافه می‌کنید. با هر درخواست، یک متد به کلاس اضافه می‌شود و پس از مدتی با کلاسی همه‌کاره سر و کار داریم که چند هزار خط کد نامرتب دارد. (The blob، یک آنتی‌پترن مشهور)در کتاب Michael Feathers آمده است:When we avoid creating new classes and methods, the existing ones grow larger and harder to understand. (Working Effectively with Legacy Code, p8)وقتی از ساختن کلاس‌ها و متدهای جدید خودداری می‌ورزیم، چیزی که در حال حاضر وجود دارد، بزرگ‌تر و بزرگ‌تر می‌شود و به دشواری درک خواهد شد!سناریوی دیگر این است که دسته‌بندی درستی وجود ندارد و همه‌چیز مثل اجرام فضایی سرگردان است. اگر مدیر از شما بخواهد که قابلیت جدیدی اضافه کنید یا چیزی را تغییر دهید، راهی جز زیر و رو کردن فایل‌های پراکنده ندارید و تازه، تکرار (Duplication) آنقدر زیاد است که اگر چیزی را تغییر دهید، استرس می‌گیرید و با خود می‌گویید: نکنه یه جای دیگه هم هست ولی من پیداش نمی‌کنم؟!تمیزکاری مداوم نداریدریفکتورینگ مداوم به حذف کدهای زائد و رفع خیلی از مشکلات دیگر کمک کرده و مانع از پراکندگی‌هایی می‌شود که فهم پروژه را مشکل می‌کنند. کدهایی که دیگر استفاده نمی‌شوند را باید حذف کرد؛ رابرت مارتین حذف آنها را به کامنت‌کردنشان ترجیح می‌دهد، توجیه‌اش این است که حتی اگر در آینده به آنها نیازی داشته باشیم، با وجود سیستم‌های کنترل سورس کد (مانند git که برایتان آشناست!) قابل بازیابی هستند. (p69)اگر درباره‌ی این مطمئن نیستید، یک کامنت TODO که در آینده به راحتی پیدا می‌شود، می‌تواند مفید باشد و ضرورت تصمیم‌گیری درباره‌ی موارد مبهم را به برنامه‌نویس‌های دیگر تیم (مخصوصاً آنهایی که در جریان کاربرد آن تکه‌کد هستند) یادآوری می‌کند.همانطور که می‌بینید، بخشی از این موارد به دانش فنی برنامه‌نویسان برمی‌گردد و بخشی دیگر (گفتن اینکه برنامه‌نویس باید/نباید به خواسته‌ی مدیریت تن بدهد را کنار بگذاریم) مربوط به تصمیم‌گیری‌های مدیریتی است. باید برای نوشتن تست و ریفکتورینگ زمان خاصی در نظر گرفت، وگرنه مدیری که اینها را تجملی غیر ضروری می‌داند، سقوط نرم‌افزار را تضمین می‌کند!شما با چه مواردی سر و کار داشته‌اید؟</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Thu, 12 Aug 2021 18:44:26 +0430</pubDate>
            </item>
                    <item>
                <title>توسعه‌ی یک اکستنشن برای خانواده‌ی کرومیوم</title>
                <link>https://virgool.io/@WebPajooh/build-an-extension-moutxu0adusi</link>
                <description>Extensions are made of different, but cohesive, components. Components can include background scripts, content scripts, an options page, UI elements and various logic files. Extension components are created with web development technologies: HTML, CSS, and JavaScript.اکستنشن‌‌ها از کامپوننت‌های متفاوت اما منسجم تشکیل می‌شوند. کامپوننت‌ها می‌توانند شامل اسکریپت‌های پس‌زمینه، اسکریپت‌های محتوا، برگه‌های تنظیمات، المنت‌های UI و فایل‌های مختلف مربوط به منطق اکستنشن باشند. کامپوننت‌های اکستنشن با تکنولوژی‌های توسعه‌ی وب ساخته می‌شوند: HTML و CSS و JS.دو سناریو را در نظر بگیریم: مرورگرها هر قابلیتی که به ذهن توسعه‌دهندگان یا کاربران رسیده باشد را پیاده‌سازی کنند.مرورگرها قابلیت‌های ضروری را پیاده‌سازی کنند و در کنار آن پورتی برای توسعه داشته باشند که هر کاربری بنا به نیاز خود قابلیت‌های دیگری را به آنها اضافه کند.در سناریوی اول، حجم مرورگرها سرسام‌آور می‌شود و حافظه‌ی کامپیوتر را صدها برابر آنچه در Memeها می‌بینیم، حیف و میل می‌کنند. در سناریوی دوم، حجم مرورگرها پایین می‌آید و هر کاربری تنها قابلیت‌های اضافی که نیاز دارد را نصب می‌کند و دیگر فلان اکستنشن که متن صفحات را به زبان هندی ترجمه می‌کند سربار حافظه‌ی اصلی و جانبی آنها نخواهد شد. کدام‌یک عاقلانه‌تر است؟ همه‌ی صداها می‌گویند: دو! دو! دو!در این مقاله، اکستنشن کوچکی برای مرورگرهای خانواده‌ی Chromium می‌نویسیم (من از Brave استفاده می‌کنم، گوگل‌کروم شیطان است) و از دور با ساختار یک اکستنشن آشنا می‌شویم.شروع کنیم!حتی اکستنشن‌های فایرفاکس هم یک فایل manifest.json دارند که اطلاعات اکستنشن از آنجا خوانده می‌شود. ما هم از این فایل شروع می‌کنیم:{
    &amp;quotmanifest_version&amp;quot: 3,
    &amp;quotname&amp;quot: &amp;quotext-tut&amp;quot,
    &amp;quotdescription&amp;quot: &amp;quotThis is just for learning purposes&amp;quot,
    &amp;quotversion&amp;quot: &amp;quot1.0.0&amp;quot,
    &amp;quoticons&amp;quot: {
        &amp;quot48&amp;quot: &amp;quoticon48.png&amp;quot,
        &amp;quot128&amp;quot: &amp;quoticon128.png&amp;quot
    }
}از نسخه‌ی مانیفست چشم‌پوشی کنید. در خط بعدی نام، توضیح و نسخه‌ی اکستنشن را تعیین کرده‌ایم و بعد آیکون اکستنشن را در دو سایز اضافه کرده‌ایم. توجه داشته باشید که آیکون‌ها باید در دایرکتوری جاری قرار گرفته باشند. فایل را سیو کرده و اکستنشن را به مرورگر اضافه می‌کنیم: وارد بخش Extensions مرورگر شوید.حالت Developer mode را فعال کنید.روی گزینه‌ی Load unpacked کلیک کنید و پوشه‌ی اکستنشن را انتخاب کنید.اکستنشن به مرورگر اضافه می‌شوداین اکستنشن، بی‌مصرف‌ترین اکستنشن موجود در جهان است، زیرا هیچ قابلیتی را به مرورگر اضافه نمی‌کند و تنها آیکونی با نقاشی افتضاح من دارد! حالا مگر چه قابلیتی قرار است به مرورگر اضافه شود؟ Shareکردن در توئیتر!اکستنشن‌ها چگونه کار می‌کنند؟اکستنشن‌ها می‌توانند به شیوه‌های مختلفی کار کنند؛ می‌توانند به Eventهای مرورگر گوش کنند (Background scripts) یا اینکه در محتوای صفحاتی که باز می‌شوند، فراخوانی شوند (Content scripts) و هر کدام از اینها بسته به موقعیت و نیازمندی ما اضافه می‌شود. مثلاً ما به Content script نیاز داریم تا محتوایی را از داخل صفحه به توئیتر بفرستیم، زیرا Content scriptها قادر به خواندن و دستکاری DOM هستند.افزودن Content scriptبرای این کار، باید کد خود را به اکستنشن معرفی کنیم، پس در فایل manifest.json خواهیم داشت:&amp;quotcontent_scripts&amp;quot: [
    {
        &amp;quotmatches&amp;quot: [
            &amp;quot&lt;all_urls&gt;&amp;quot
        ],
        &amp;quotjs&amp;quot: [
            &amp;quotmain.js&amp;quot
        ]
    }
]فیلد matches تعیین می‌کند که این Content script در چه آدرسی عمل کند. فرض کنید که می‌خواهیم اکستنشنی بنویسیم که گزینه‌ی دانلود را به انتهای پادکست‌های گوگل اضافه کند، پس نیازی نداریم که اسکریپتمان در هر صفحه‌ای لود شود و چنین چیزی خواهیم داشت:&amp;quothttps://podcasts.google.com/*&amp;quotما می‌خواهیم اسکریپتمان در هر صفحه‌ی فراخوانی شود، پس &lt;all_urls&gt; چاره‌ی کار است. در ادامه هم فایل main.js را معرفی کرده‌ایم، پس فایلی با همین نام در پوشه‌ی جاری بسازید. برای جلوگیری از کثیف‌کاری (ویرگول چرا Syntax highlighter ندارد؟) کدهای جاوااسکریپت را به صورت تصویر می‌گذارم (با تشکر از کربن):اگر کمی جاوااسکریپت بلد باشید، می‌بینید که کار راحتی است! در بخش A دکمه‌ای ساخته‌ایم، به آن استایل و غیره داده‌ایم و به body اضافه کرده‌ایم. در بخش B یک رویداد (Event) اضافه کرده‌ایم که هر وقت متنی در داخل صفحه انتخاب شود، عمل می‌کند (این تکه را از این جواب گرفته‌ام) و متن انتخاب‌شده را داخل document.highlighted می‌ریزد. بعد در بخش C کدی نوشته‌ام که چک می‌کند که آیا طول متن انتخاب‌شده از صفر بیشتر است یا نه (چیزی انتخاب شده یا نه؟!)، و بر اساس آن دکمه‌ی Share را نشان می‌دهد یا مخفی می‌کند. ببینیم چه شد:در پایین صفحه، دکمه‌ی Share اضافه شدنکته‌ی کنکوری: لازم نیست که بعد از هر تغییری اکستنشن را حذف و اضافه کنید. در تصویر اول (بخش اکستنشن‌ها) آیکون Refresh را می‌بینید که با کلیک روی آن، مرورگر به صورت خودجوش اکستنشن را آپدیت می‌کند.تا این قسمت از مقاله کاری کرده‌ایم که هر وقت قسمتی از متن Highlight شد، دکمه‌ی شیر در پایین صفحه نمایش داده شود. مرحله‌ی بعد این است که اگر روی دکمه کلیک کردیم، متن انتخابی در توئیتر به اشتراک گذاشته شود:document.getElementById(&#039;share-button&#039;).addEventListener(&#039;click&#039;, () =&gt; {
    window.open(`https://twitter.com/intent/tweet?text=${document.highlighted}`)
})حالا با کلیک روی دکمه‌ی Share، پنجره‌ی اشتراک‌گذاری توئیتر باز می‌شود که نشان می‌دهد راه را درست رفته‌ایم:متن انتخاب شده در سایت توئیتر باز شد و منتظر توئیت‌شدن استافزودن Popupدر مرحله‌ی قبل موفق شدیم قسمتی از متن را انتخاب کنیم و با دکمه‌ی Share در توئیتر به اشتراک بگذاریم. این خوب است، ولی کافی نیست. می‌توانیم دکمه‌ای در بالای مرورگر داشته باشیم که با آن آدرس صفحه‌ی فعلی را هم در توئیتر به اشتراک بگذاریم. ابتدا کد زیر را به فایل manifest.json اضافه می‌کنیم:&amp;quotaction&amp;quot: {
    &amp;quotdefault_popup&amp;quot: &amp;quotpopup.html&amp;quot
},
&amp;quotpermissions&amp;quot: [
    &amp;quottabs&amp;quot
]در قسمت اول، یک popup داریم که محتوای فایل popup.html را نشان می‌دهد و قسمت دوم هم permission است که مجوزهای خاصی را از مرورگر دریافت می‌کند. از آنجا که قصد داریم به URL تب‌ها دسترسی داشته باشیم، باید مجوز tabs را از مرورگر بگیریم. محتوای فایل‌های popup.html و popup.js را به ترتیب می‌بینید:&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;
    &lt;button id=&amp;quotshare-url&amp;quot&gt;Share&lt;/button&gt;

    &lt;script src=&amp;quotpopup.js&amp;quot&gt;
&lt;/body&gt;
&lt;/html&gt;document.getElementById(&#039;share-url&#039;).addEventListener(&#039;click&#039;, async () =&gt; {
    chrome.tabs.query({&#039;active&#039;: true, &#039;lastFocusedWindow&#039;: true}, function (tabs) {
        window.open(`https://twitter.com/intent/tweet?url=${tabs[0].url}`)
    })
})اتفاق عجیبی نیفتاده است! در صورت کلیک روی دکمه‌ی Share، از اولین تبی که متد query برمی‌گرداند (که همان تب فعال و فعلی است) به پراپرتی url دسترسی پیدا کرده‌ایم و تقریباً همان کار قبلی را انجام داده‌ایم.آیکون و Popup اکستنشن ما به بخش بالایی مرورگر اضافه شده استتمام شد! ☺️این مقاله تنها به شکل سطحی و مقدماتی به موضوع پرداخته است و هنوز مباحث بسیاری زیادی برای یادگیری باقی مانده است. شما می‌توانید از قابلیت‌های مختلف مرورگر بعد از اضافه‌کردن permissionشان استفاده کنید و ایده‌های مختلفی را درون مرورگر بیاورید.هرگز از مستندات غافل نشوید:https://developer.chrome.com/docs/extensions/اکستنشن اِشتب که قبلا نوشته بودم را بررسی کنید و در صورت تمایل روی ⭐️ کلیک کنید:https://github.com/WebPajooh/eshteb</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Fri, 14 May 2021 14:38:31 +0430</pubDate>
            </item>
                    <item>
                <title>آشنایی با Browser Automation</title>
                <link>https://virgool.io/@WebPajooh/browser-automation-emp8iuxihjat</link>
                <description>آتومیشن؟!Automation is the technology by which a process or procedure is performed with minimum human assistance.آتومیشن، فناوری انجام یک فرآیند با حداقل کمک از جانب انسان است. در گذشته، انسان‌ ابزاری برای انجام کار در دست داشت و امروز ابزار انسانی در دست دارد. ما فرآیند تولید یک وسیله را به ماشین‌هایی سپرده‌ایم که تحت کنترل انسان از مراحل ابتدایی تا نهایی کار را انجام می‌دهند. در این مقاله مرورگر را کنترل می‌کنیم تا فرآیندی که می‌خواهیم را به جای ما انجام دهد و این همان تعریف Browser Automation (خودکارسازی مرورگر) است.کاربردهای Browser Automationکشف لینک‌های شکسته (Broken Links): تصور کنید که در یک وب‌سایت ده‌هزار لینک وجود دارد و می‌خواهید لینک‌هایی که به خطای 404 می‌رسند را پیدا کنید. با استفاده از Browser Automation می‌توان این کار را به شکل خودکار انجام داد.استخراج داده‌ها از وب: می‌توانیم کدی بنویسیم که هر ساعت آخرین خبرهای یک وب‌سایت را استخراج و در جایی ذخیره کند. مثال‌های زیادی برای این کاربرد وجود دارد، می‌توانید در بخش کامنت‌های همین پست آنها را ذکر کنید!تست اتوماتیک: همیشه کدها را از داخل تست نمی‌کنیم و گاهی نیاز داریم که بررسی محصول را از دید کاربر نهایی انجام دهیم. با Browser Automation می‌توان تست‌های E2E را با مرورگرهای مختلف اجرا کرد و از درستی رفتار وب‌سایت مطمئن شد.ابزارهای لازمدر این مقاله از زبان PHP و پکیج php-webdriver استفاده می‌کنیم. این پکیج سه روش مختلف برای خودکارسازی مرورگر ارائه می‌کند که شاید ساده‌ترین آنها با Chrome Driver انجام شود و برای به‌کارگیری آن باید مرورگر Google Chrome و سپس کروم‌درایور سازگار با نسخه‌ی نصب‌شده را دانلود کنید. بعد از دانلود، در ترمینال درایور را اجرا کنید تا گوش به فرمان کدهای ما باشد:chromedriver --port=4444شروع بازیابتدا لازم است به سروری که Chrome Driver ایجاد کرده بود متصل شویم:use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;

$server = &#039;http://localhost:4444&#039;;
$driver = RemoteWebDriver::create($server, DesiredCapabilities::chrome());حالا با درایور چه کار کنیم؟ می‌خواستم با ویرگول بازی کنم ولی ترجیح دادم که ریسک نکنم، پس از ویکیپدیا استفاده می‌کنیم:$driver-&gt;get(&#039;https://en.wikipedia.org/wiki/Main_Page&#039;);متد get می‌تواند لینک درخواستی ما را در مرورگر باز کند. وقتی می‌گویم باز کند، یعنی واقعاً باز کند! بفرمایید:می‌توانیم موضوعی را در کادر جستجو بنویسیم تا صفحه‌ی مربوط به آن باز شود! امتحان کنیم:use Facebook\WebDriver\WebDriverBy;

$driver-&gt;findElement(WebDriverBy::id(&#039;searchInput&#039;))
    -&gt;sendKeys(&#039;PHP&#039;)
    -&gt;submit();یک متد static به نام id روی کلاس WebDriverBy وجود دارد که عنصری (Element) را در صفحه بر اساس مشخصه (یا همان Attribute) id انتخاب می‌کند. امکان انتخاب یک عنصر بر اساس tag یا class و غیره هم وجود دارد، ولی چون عنصر دیگری با این id در صفحه وجود ندارد، سرراست‌ترین روش همین است. بعد از انتخاب کادر جستجو، عبارت PHP را در آن نوشته‌ایم و سپس از متد submit استفاده کرده‌ایم تا فرم را برای actionاش ارسال کند. با اجرای این بخش از کد، به صفحه‌ی PHP در ویکیپدیا منتقل می‌شویم:می‌توانیم سومین تگ p که از جمله‌ی PHP is a general-purpose... شروع می‌شود را بدون شماره‌های داخل براکت چاپ کنیم:$paragraphs = $driver-&gt;findElements(WebDriverBy::cssSelector(&#039;.mw-parser-output p&#039;));
$text = $paragraphs[2]-&gt;getText();
echo preg_replace(&amp;quot/\[[0-9]+\]/&amp;quot, &#039;&#039;, $text);PHP is a general-purpose scripting language especially suited to web development. It was originally created by Danish-Canadian programmer Rasmus Lerdorf in 1994. The PHP reference implementation is now produced by The PHP Group. PHP originally stood for Personal Home Page, but it now stands for the recursive initialism PHP: Hypertext Preprocessor.همچنین امکان گرفتن Screenshot از خروجی وجود دارد:$pngFileContent = $driver-&gt;takeScreenshot();
Storage::put(&#039;/screenshots/1.png&#039;, $pngFileContent);متد takeScreenshot محتوای png را برگرداند و با فساد Storage در لاراول آن را ذخیره کردیم. اگر خواستید، آدرس فایل را به طور مستقیم به عنوان آرگومان متد takeScreenshot بدهید تا کار ذخیره‌سازی را هم انجام دهد.این تنها گوشه‌ای از قابلیت‌های Browser Automation است و در عمل ده‌ها کاربرد مختلف برای آن وجود دارد. اگر کار جالبی انجام دادید، می‌توانید در کامنت بگویید و با پیشنهادات و انتقاداتتان به تکمیل این مقاله کمک کنید.</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Tue, 13 Apr 2021 22:55:46 +0430</pubDate>
            </item>
                    <item>
                <title>معرفی دیزاین‌پترن Singleton</title>
                <link>https://virgool.io/@WebPajooh/singleton-pattern-z8udi3oc5rio</link>
                <description>پارسا: می‌خوام از کلاس یه آبجکت بسازم...پوریا: خُب با new بساز!پارسا: نمی‌خوام هربار ازش instance جدید بسازم، فقط یه instance می‌خوام! فرض کن با هربار نمونه‌سازی از کلاس، اتفاق بدی برام می‌افته! ترجیح می‌دم فقط و فقط یه بار این اتفاق بد بیفته تا اینکه تکرار بشه!سینگلتون چیست؟Ensures a class only has one instance, and provides a global point of access to it.سینگلتون شما را مطمئن می‌سازد که یک کلاس تنها یک نمونه دارد و یک نقطه‌ی دسترسی سراسری برای آن ارائه می‌دهد.به کد زیر دقت کنید:$object1 = new Cache();
$object2 = new Cache();
var_dump($object1 === $object2);خروجی false است؛ زیرا هر کدام از این دو آبجکت، نمونه‌ی جداگانه‌ای از کلاس Cache هستند. کاری که سینگلتون انجام می‌دهد محدودکردن نمونه‌سازی است تا جواب این شرط true شود. کلمه‌ی Singleton یعنی تَک، پس نمونه‌ای که ساخته می‌شود بی‌نظیر است و مطمئن هستیم که از آن کلاس، بیشتر از یک نمونه وجود ندارد.کاربردچرا باید نمونه‌سازی دوباره از یک کلاس را محدود کنیم؟ جواب این پرسش را خودمان بر اساس نیاز می‌فهمیم، زیرا استفاده از دیزاین‌پترن‌ها بر اساس نیاز است. ما آنها را در هر جای پروژه به کار نمی‌بریم و تا مشکل x نباشد، دست به دامن دیزاین‌پترنی که برای حل آن آمده نمی‌شویم. در کتاب Head First Design Patterns از کلاس‌های مربوط به کش (Cache) و پیکربندی سیستم اسم برده شده که در صورت نیاز یک نمونه از آنها ساخته می‌شود و در ادامه‌ی روند برنامه از همان نمونه استفاده می‌شود. توجه کنید که ساخت نمونه از یک کلاس سینگلتون در هنگام نیاز اتفاق می‌افتد، پس اگر کاربر درخواستی برای سیستم بفرستد که برای اجرای آن نیازی به آن کلاس سینگلتون نباشد، هیچ نمونه‌ای ساخته نمی‌شود. اما اگر از یک متغیر سراسری (Global) در ابتدای بارگزاری سیستم استفاده می‌کردیم، ساخت نمونه در هر صورتی اتفاق می‌افتاد.پیاده‌سازیسینگلتون سه ویژگی کلیدی دارد:امکان نمونه‌سازی مستقیم از آن وجود ندارد.یک نقطه‌ی دسترسی سراسری دارد که نمونه از طریق آن دریافت می‌شود.حداکثر یک نمونه ارائه می‌کند.تلاش کنیم که با استفاده از آموخته‌هایی که از زبان برنامه‌نویسی خود داریم، این سه خصوصیت را پیاده کنیم: برای پیاده‌سازی خصوصیت اول، سازنده‌ی کلاس را خصوصی (Private) تعریف می‌کنیم و برای خصوصیت دوم یک متد Static می‌سازیم و خصوصیت سوم را به وسیله‌ی یک شرط مهیا می‌کنیم.class Singleton
{
    private static $instance;

    private function __construct() {}

    public static function getInstance()
    {
        if (! static::$instance) {
            static::$instance = new static;
        }

        return static::$instance;
    }
}از آنجا که construct__ را به صورت خصوصی تعریف کرده‌ایم، دیگر امکان نمونه‌سازی با new وجود ندارد، پس متد getInstance به عنوان نقطه‌ی دسترسی تعریف شده است و اگر نمونه هنوز وجود نداشته باشد، آن را می‌سازد و بعد نمونه را برمی‌گرداند:$obj1 = Singleton::getInstance();
$obj2 = Singleton::getInstance();
var_dump($obj1 === $obj2);این بار خروجی true است و هردو نمونه یکی هستند، اما یک جای کار می‌لنگد:$obj1 = Singleton::getInstance();
$obj2 = clone $obj1;
var_dump($obj1 === $obj2);هرچند که راه نمونه‌سازی با کلمه‌ی کلیدی new را بسته‌ایم ولی هنوز امکان کپی‌کردن یک نمونه با clone وجود دارد! برای حل این مشکل باید متد clone__ را هم به صورت private بنویسیم:private function __clone() {}یک بار دیگر امتحان کنیم:$obj1 = Singleton::getInstance();
$obj2 = unserialize(serialize($obj1));
var_dump($obj1 === $obj2);دوباره نمونه‌ی دومی ایجاد کردیم و این سینگلتون را نقض می‌کند، اما اگر متد serialize__ یا unserialize__ را هم private کنیم، از اجرای کد بالا جلوگیری کرده‌ایم و به سینگلتون رسیده‌ایم.سینگلتون در لاراولبا لاراول می‌توان منطقی که گفته شد را به صورت دیگری داشت، کافی است که در متد register که درService Providerها وجود دارد، کلاس خود را bind کنید:$this-&gt;app-&gt;singleton(&#039;currency.service&#039;, function () {
    return new Currency();
});از این به بعد می‌توانیم با کلید currency.service به کلاس Currency دسترسی داشته باشیم:$obj1 = app()-&gt;make(&#039;currency.service&#039;);
$obj2 = app()-&gt;make(&#039;currency.service&#039;);
var_dump($obj1 === $obj2);خروجی true است و هردو آبجکت، واقعاً یک نمونه هستند!با ()this-&gt;app$ به کلاس Application دسترسی پیدا کرده‌ایم که از کلاس Container ارث‌بری کرده است. کلاس Container هم متدی به نام singleton دارد که از همان کلاس bind استفاده می‌کند، اما با یک تفاوت کوچک:public function singleton($abstract, $concrete = null)
{
    $this-&gt;bind($abstract, $concrete, true);
}آرگومان سوم، مقدار true را به پارامتری به نام shared می‌دهد که یگانه‌بودن آنچه bind شده است را تعیین می‌کند.منابعی که این مقاله را شکل دادند:Elements of Reusable Object-Oriented SoftwareHead First Design PatternPHP Official DocumentationLaravel Official Documentationشاید این نوشته‌ها هم برایتان جالب باشند:معرفی دیزاین‌پترن Observer</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Wed, 31 Mar 2021 22:37:05 +0430</pubDate>
            </item>
                    <item>
                <title>معرفی دیزاین‌پترن Observer</title>
                <link>https://virgool.io/coderlife/observer-pattern-fp8fbdaez2ji</link>
                <description>دیزاین‌پترن چیست؟دیزاین‌پترن‌ها (Design Pattern)، راه‌حل‌هایی معمول برای حل مشکلات رایج موجود در طراحی نرم‌افزار هستند. آنها علاوه بر ارائه‌ی راه‌حلی منعطف، زبان مشترکی بین افراد تیم به شمار می‌روند تا ارتباط کارآمدتری داشته باشند! مثلاً رضا می‌گوید «یه آبزرور برای این قسمت نوشتم!» و سعید می‌گوید «آها! کارت درسته رفیق!»، زیرا برنامه‌نویسان تیم با دیزاین‌پترن‌ها آشنا هستند و می‌دانند که چرا استفاده می‌شوند و چه نیازی را برطرف می‌کنند! اگر هنوز فکر می‌کنید که توضیحات فوق گنگ است، خواندن این مقاله را متوقف کرده و ابتدا مفهوم دیزاین‌پترن را به طور مفصل بیاموزید.مسئله: فروشگاه و تغییر قیمتوضعیت بازار و مروّت خوب نیست و مسئولین هم دستشان به استعفا نمی‌رود. فروشگاه «دیدی‌حالا» معتقد است که بی‌خبری، خوش‌خبری نیست و می‌خواهد مشتریانش را از تغییر قیمت محصول ویژه‌اش باخبر کند. مدیر فروشگاه تاکید کرده که باید هر مشتری به دلخواه خود وارد این فرآیند شود و هر زمان که خواست از آن انصراف دهد.راه‌حل: Observer Patternتعریف این الگو را از کتاب معروف Gang of Four ببینیم:Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.این پترن، وابستگی یک به چندی میان آبجکت‌ها تعریف می‌کند، به نحوی که اگر یک آبجکت تغییر حالت دهد، تمام آبجکت‌های وابسته اطلاع یافته و به‌روز شوند. این الگو به نام‌های Dependents و Publish-Subscribe نیز شناخته می‌شود و یکی از الگوهای رفتاری (Behavioral Patterns) به حساب می‌آید.دو اصطلاح مهمسوژه (Subject): این همان «یک آبجکت»ای است که در تعریف دیدیم. سوژه یک حالت (State) دارد که می‌تواند تغییر کند و باید آبجکت‌های نامحدودی را به عنوان بیننده (Observer) خود بپذیرد.بیننده (Observer): در این الگو، آبجکت‌هایی داریم که از تغییرات سوژه باخبر می‌شوند و هر زمان که لازم باشد، از نظاره‌کردن سوژه انصراف می‌دهند.ساختارObserver design pattern (Head First Design Patterns)همانطور که ملاحظه می‌کنید برای Subject و Observer، اینترفیس (Interface) داریم و بعد هرکدام را پیاده‌سازی می‌کنیم. مطمئناً می‌دانید که اینترفیس‌ها پیاده‌سازی (Implementation) ندارند و کلاس‌های دیگری آنها را پیاده می‌کنند.کاربردپذیریزمانی که نیاز داریم با تغییر یک آبجکت، آبجکت‌های دیگری تغییر کنند و نمی‌دانیم که دقیقاً چند Observer وجود دارد. پس باید ساختاری داشته باشیم که در صورت لزوم، بی‌آنکه کلاس‌هایمان دچار تغییر شوند، هر آبجکتی را به لیست Subscriberها اضافه یا از آن حذف کنیم.زمانی که باید یک آبجکت قابلیت خبردادن به آبجکت‌های دیگر را داشته باشد، بدون اینکه ماهیت آنها را بداند یا وابستگی محکمی بینشان برقرار باشد.و...پیاده‌سازیبدخواهان PHP بدانند که این زبان از قدیم‌الایام دو اینترفیس برای Subject و Observer داشته است! ? می‌دانید که خواندن و نوشتن کد در ویرگول زجرآور است، برای همین کدهای مثال را در یک مخزن گیت‌هاب قرار داده‌ام و سرانجام کار را در اینجا می‌نویسم:$product = new Product(300000);
$customer1 = new Customer(&#039;Muhammad&#039;);
$customer2 = new Customer(&#039;Parsa&#039;);
$customer3 = new Customer(&#039;Matin&#039;);

$product-&gt;attach($customer1);
$product-&gt;attach($customer2);
$product-&gt;attach($customer3);

$product-&gt;setPrice(350000);به محض تغییر قیمت، خروجی زیر را خواهیم داشت:Muhammad: The price has changed!
Parsa: The price has changed!
Matin: The price has changed!این خروجی نشان می‌دهد که مشتریان ما از تغییر قیمت باخبر شدند و هدف ما هم از استفاده‌کردن Observer Pattern همین بود!منابعی که این مقاله را شکل دادند:Elements of Reusable Object-Oriented SoftwareHead First Design PatternRefactoring.guruPHP Official Documentationشاید این نوشته‌ها هم برایتان جالب باشند:معرفی دیزاین‌پترن Singleton</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Sat, 20 Mar 2021 20:40:40 +0330</pubDate>
            </item>
                    <item>
                <title>ماکرو در لاراول</title>
                <link>https://virgool.io/@WebPajooh/%D9%85%D8%A7%DA%A9%D8%B1%D9%88-%D8%AF%D8%B1-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-w2lmmduyz351</link>
                <description>نمی‌دانم تا به حال دیده‌اید که یکی از Classهای لاراول، از یک Trait به نام Macroable استفاده کرده باشد؟ اگر ندیده‌اید، بدانید که کلاس‌هایی چون Filesystem، Arr، Collection، Request و غیره از این Trait استفاده کرده‌اند...تریت؟!تریت (Trait) یعنی خاصیت. در PHP امکان ارث‌بری چندگانه وجود ندارد و کد زیر با خطا مواجه خواهد شد:class President extends Human, Devil {}اما می‌توانیم با استفاده از Traitها هر چقدر که خواستیم، خصوصیاتی را بین کلاس‌های مختلفی به اشتراک بگذاریم. مثلاً بعضی از کلاس‌های لاراول از تریت Macroable استفاده کرده و متدهایش را به ارث برده‌اند:class Schedule
{
        use Macroable;
.
.
.چه‌کار می‌کند؟فرض کنید که می‌خواهیم قابلیتی را به یکی از کلاس‌های لاراول اضافه کنیم. اگر آن کلاس از Macroable استفاده کرده باشد، به آسانی قابل توسعه است و می‌توانیم بدون تغییر یک خط از کدهای آن کلاس، متدهای جدیدی ایجاد کنیم!مثال بگو!قلی خود را شناساننده‌ی مجموعه‌ی خاصی می‌داند و نامش را هم بر آن گذاشته است. مجموعه‌ی قلی، مجموعه‌ای از اعداد است که تعداد و مجموعشان فرد باشد. از آنجا که در پروژه‌اش بسیار با مجموعه‌ی قلی سر و کار دارد، آرزو می‌کند که ای کاش! امکانی در کالکشن‌های لاراول بود تا با آن قلی‌بودن کالکشن‌هایش را تشخیص می‌داد!برای رساندن قلی به آرزوی دیرینه‌اش، کدهای زیر را در متد boot موجود در AppServiceProvider می‌نویسیم:public function boot()
{
        Collection::macro(&#039;isGholi&#039;, function () {
                return $this-&gt;sum() % 2 !== 0 &amp;&amp; $this-&gt;count() % 2 !== 0;
        });
}از متد macro روی کلاس Collection استفاده کرده‌ایم. آرگومان اول، نام متد جدید است و آرگومان دوم، callable است و در بدنه‌اش دو شرط قلی ر ا بررسی کرده‌ایم: باید مجموع اعداد و هم تعدادشان فرد باشد.اگر قلی در هر جای پروژه به صورت زیر عمل کند:$collection = collect([1, 2, 4]);
echo $collection-&gt;isGholi() ? &#039;قلی است&#039; : &#039;قلی نیست&#039;;جواب می‌گیرد! این دفعه «قلی است»، زیرا مجموع 1 و 2 و 4، 7 است و تعدادشان 3، که هر دو شرط را دارند. پس ما متدی به کلاس کالکشن اضافه کردیم که می‌تواند در سراسر پروژه قابل دسترسی باشد!چی شد؟!طرز کار تریت Macroable بسیار ساده است و هیچ چیز ترسناکی ندارد. بیایید اتفاقی که در این تریت افتاده را با خواندن کدهای مرتبط، مرور کنیم:protected static $macros = [];در این تریت، یک مشخصه به نام macros(ماکروها) و از جنس آرایه تعریف شده است.public static function macro($name, $macro)
{
        static::$macros[$name] = $macro;
}این همان متدی است که برای حل دغدغه‌ی قلی از آن استفاده کردیم. پارامتر اول نام ماکرو است، پس نام ماکرو را به عنوان کلید آرایه‌ی macros در نظر می‌گیرد و callback ما را value آن قرار می‌دهد و به ازای هر نام (مثل isGholi) کدهایی برای اجراشدن داریم! ساده ولی جالب و خلاقانه است!public function __call($method, $parameters)
{
        if (! static::hasMacro($method)) {
                throw new BadMethodCallException(sprintf(
                        &#039;Method %s::%s does not exist.&#039;, static::class, $method
                ));
        }

        $macro = static::$macros[$method];

        if ($macro instanceof Closure) {
                $macro = $macro-&gt;bindTo($this, static::class);
        }

        return $macro(...$parameters);
}این یک متد جادویی (Magic Method) است. وقتی isGholi را روی کالکشن صدا بزنیم، پی‌اچ‌پی متوجه می‌شود که چنین متدی درون کلاس وجود ندارد، برای همین به متد جادویی call__ مراجعه می‌کند. با hasMacro تشخیص داده می‌شود که قبلاً ماکرویی با کلید متد درخواست‌شده در آرایه‌ی macros وجود دارد یا نه. اگر وجود داشت، مقدار آن خوانده می‌شود و در متغیر macro$ ریخته می‌شود. سپس بررسی می‌شود که از نوع Clousure هست یا نه؟ اگر باشد، از bindTo استفاده کرده‌اند تا this$ به جای AppServiceProvider به خود Collection اشاره کند (اگر بایند نشده بود، منطقاً کار نمی‌کرد) و در نهایت ماکروی ما با پارامترهایی که کاربر وارد کرده، اجرا می‌شود.آیا قابلیت ماکرو در لاراول و نحوه‌ی پیاده‌سازی‌اش، خلاقیت ظریفی نیست؟</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Sun, 07 Feb 2021 19:31:22 +0330</pubDate>
            </item>
                    <item>
                <title>توابع compact و extract در PHP</title>
                <link>https://virgool.io/@WebPajooh/compact-and-extract-in-php-fxy7suhhqmtk</link>
                <description>اولین بار فانکشن compact را در فریم‌ورک لاراول استفاده کردم و برای ارسال اطلاعات به ویو، چنین چیزی داشتیم:$posts = Post::all();
return view(&#039;view_name&#039;, compact(&#039;posts&#039;);اما compact چه کار می‌کند؟کار این فانکشن این است که متغیرهایی را دریافت می‌کند و آنها را در آرایه‌ای می‌ریزد؛ نام هر متغیر key و مقدار آن value می‌شود. به این مثال دقت کنید:$name = &#039;John&#039;;
$age = 27;
$arr = compact(&#039;name&#039;, &#039;age&#039;);
echo &#039;Your name is &#039; . $arr[&#039;name&#039;] . &#039; and you are &#039; . $arr[&#039;age&#039;];فانکشن compact می‌تواند حداقل یک پارامتر بگیرد و هر پارامتر، نام یک متغیر است. شما می‌توانید پارامترها را به صورت رشته‌هایی که هر کدام نام یک متغیر باشند وارد کنید (مانند مثالی که گذشت) و یا اینکه از آرایه‌ای از رشته‌ها استفاده کنید و خروجی نیز همانطور که مشاهده کردید، یک آرایه است.فانکشن extractاین فانکشن، برعکس compact عمل می‌کند و از یک آرایه، متغیرهایی می‌سازد. نام هر متغیر، از کلیدهای آرایه (keys) گرفته شده و مقدار متغیر به مقادیر (values) بستگی دارد.$arr = [
&#039;name&#039; =&gt; &#039;john&#039;,
&#039;age&#039; =&gt; 27
];

extract($arr);
echo $name . &#039; is &#039; . $age;پارامتر دوم این فانکشن، اختیاری و مربوط به flagهاست:فلگ EXTR_OVERWRITE: اگر متغیری به همان نام موجود باشد، آنچه درون آرایه باشد را جایگزین می‌کند.فلگ EXTR_SKIP: اگر متغیری به همان نام موجود باشد، جایگزینی رخ نمی‌دهد.فلگ EXTR_PREFIX_SAME: اگر تداخلی باشد، به آنچه درون آرایه باشد، پیشوندی اختصاص می‌دهد. پیشوند، پارامتر سوم است و اگر برای مثال کلید آرایه age$ باشد و پیشوند&#x27;new&#x27; را برای پارامتر سوم در نظر گرفته باشیم، مقدار آن کلید در متغیر new_age$ ریخته می‌شود.فلگ EXTR_PREFIX_ALL: چه تداخلی باشد و چه نباشد، از طریق پارامتر سوم به تمامی متغیرها یک پیشوند اختصاص می‌دهد.برای مشاهده‌ی دیگر فلگ‌ها به برگه‌ی مربوط به extract در مستندات PHP مراجعه کنید.مثالی عملی برای compact و extractکلاسی به نام View ساخته‌ایم که با استفاده از compact و extract می‌تواند یک قالب را با داده‌های واردشده نشان دهد:$title = &#039;Hello, world!&#039;;
$author = &#039;WebPajooh&#039;;
View::make(&#039;view1&#039;, compact(&#039;title&#039;, &#039;author&#039;));کدهای این مثال را به طور کامل در این مخزن آپلود کرده‌ام و دیدن کدهای کوتاه اما مهم View.php را توصیه می‌کنم.نکته‌ای امنیتی مربوط به extractاگر ورودی این فانکشن را از کاربر بگیرید، خطر در کمین شماست! در مستندات آمده است:Warning: Do not use extract() on untrusted data, like user input (i.e. $_GET, $_FILES, etc.). If you do, for example if you want to temporarily run old code that relied on register_globals, make sure you use one of the non-overwriting flags values such as EXTR_SKIP and be aware that you should extract in the same order that’s defined in variables_order within the php.ini.هشدار داده است که از extract، روی داده‌های غیر قابل اعتماد مانند ورودی‌هایی که از کاربران می‌گیرید استفاده نکنید و اگر استفاده کردید، فلگی چون EXTR_SKIP را فراموش نکنید!</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Fri, 11 Dec 2020 19:04:34 +0330</pubDate>
            </item>
                    <item>
                <title>کلاس Countable در PHP</title>
                <link>https://virgool.io/@WebPajooh/countable-class-tab6xcb7su6o</link>
                <description>کلاس Countable در PHPهنگام مطالعه‌ی کدهای لاراول متوجه شدم که بعضی از کلاس‌ها، اینترفیسی به نام Countable را پیاده‌سازی کرده‌اند. با نیم‌نگاهی در مستندات فهمیدم که جریان از چه قرار است و خواستم شما را هم در جریان قرار دهم.چیست و از کجا آمده؟کلاس Countable از اینترفیس‌های خود زبان PHP است. اگر با شیءگرایی در PHP آشنا باشید، می‌دانید که اینترفیس‌ها، قراردادهایی هستند که کلاس‌هایی به پیاده‌سازیشان می‌پردازند و باید عیناً متدهای داده شده را اجرا کنند. واژه‌ی Countable یعنی قابل‌شمارش و همین پرسشی را در ذهن ما می‌اندازد: چه چیزی را می‌شماریم؟چه چیزی را بشماریم؟یک آرایه با چند عنصر می‌سازیم:$users = [&#039;Ali&#039;, &#039;Reza&#039;, &#039;Muhammad&#039;, &#039;Farhad&#039;, &#039;Saeid&#039;];
echo count($users);خروجی عدد 5 است. از آنجا که آرایه 5 عنصر دارد، فانکشن count تعداد عناصر آرایه را برایمان برگردانده است.حال به مثال زیر دقت کنید:class Library
{
   public $books = [];
   
   public function addBook($book)
   {
      $this-&gt;books[] = $book;
   }
}

$lib = new Library();
$lib-&gt;addBook(&#039;Grokking Algorithms&#039;);
$lib-&gt;addBook(&#039;Keep the Aspidistra Flying&#039;);
$lib-&gt;addBook(&#039;The Lord Of The Rings&#039;);
echo count($lib);نتیجه‌ی چنین کدی، هشدار زیر است:count(): Parameter must be an array or an object that implements Countableپارامترِ فانکشنِ count می‌بایست یک آرایه یا آبجکتی باشد که Countable را پیاده‌سازی کرده باشد!بینگو، این همان گم‌شده‌ی ماست! باید در کلاس Library تغییری اعمال کنیم تا به نتیجه‌ی دلخواه خود (دریافت تعداد کتاب‌های اضافه‌شده با فانکشن count) برسیم:class Library implements Countable
{
   public $books = [];

   public function addBook($book)
   {
      $this-&gt;books[] = $book;
   }

   public function count()
   {
      return count($this-&gt;books);
   }
}وقتی آبجکتی که در اصل، کلاس Countable را پیاده‌سازی کرده باشد را به عنوان پارامتر به فانکشن count بدهیم، متد count از کلاس یادشده اجرا می‌شود.</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Fri, 20 Nov 2020 13:52:21 +0330</pubDate>
            </item>
                    <item>
                <title>دمی با require و include</title>
                <link>https://virgool.io/@WebPajooh/require-vs-include-y5nqa06keckv</link>
                <description>دمی با require و includeهفته‌ی پیش در مصاحبه‌ای کاری (برای سمت توسعه‌دهنده‌ی بک‌اند) شرکت کردم که یکی از پرسش‌های مصاحبه‌کننده درباره‌ی require و include بود. با اینکه جوابی ناقص در خصوص متوقف‌شدن یا نشدن برنامه در تفاوت این دو گفتم، اما تصمیم گرفتم که مطلبی راجع به این موضوع بنویسم تا هم این معلومات برای خودم تثبیت شوند و هم دیگران استفاده کنند.کاربرداز هردوی آنها برای فراخوانی کدهای فایلی دیگر در جریان اسکریپت فعلی استفاده می‌کنیم. فرض کنید که تنظیمات و اطلاعات مربوط به دیتابیس، سرویس‌های خارجی و غیره خود را در فایل config.php نوشته‌ایم، و توابع مورد نیاز را در فایل functions.php قرار داده‌ایم و می‌خواهیم در فایل اصلی index.php از آنها استفاده کنیم. باید هردو فایل را در index.php فراخوانی کنیم تا بتوانیم از آنچه نوشته‌ایم، در index.php استفاده کنیم.تفاوت رفتار require و includeدر صورتی که PHP نتواند فایل فراخونی شده را بیابد، در صورت استفاده از include یک E_WARNING و در صورت استفاده از require یک E_ERROR را برمی‌گرداند. پس در صورت استفاده از include با بروز یک مشکل، اسکریپت متوقف نشده و همچنان ادامه خواهد داد، اما در require چنین نیست و اسکریپت با یک Fatal Error متوقف خواهد شد.تفاوت E_ERROR و E_WARNINGکلمه‌ی E_ERROR به خطاهای مهلک زمان‌اجرا (Fatal run-time errors) اشاره دارد که باعث متوقف‌شدن روند برنامه می‌شوند و E_WARNING به هشدارهای غیر مهلک زمان‌اجرا اشاره دارد که بروزشان باعث توقف برنامه نمی‌شود. موارد دیگری مانند E_PARSE (خطاهای زمان کامپایل) و E_NOTICE (اعلان‌های زمان اجرا) وجود دارند که همگی را در این بخش از مستندات پی‌اچ‌پی خواهید یافت.تفاوت require و require_onceبین require و require_once (همچنین include و include_once) یک تفاوت جالب وجود دارد. به کد زیر توجه کنید:core.php&lt;?php
echo &amp;quotThinking...\n&amp;quotscript.php&lt;?php
require &#039;core.php&#039;;
// bla bla bla
require_once &#039;core.php&#039;;اگر فایل script.php را اجرا کنیم، تنها یک بار عبارت Thinking را مشاهده می‌کنیم، زیرا در دفعه‌ی دوم از require_once استفاده کرده‌ایم. با require_once، بررسی می‌شود که آیا این فایل پیش از این فراخوانی شده است یا نه، و در صورتی که پاسخ مثبت باشد، دیگر فراخوانی نخواهد شد. اگر بجای آن از require استفاده می‌شد، عبارت Thinking دو بار چاپ می‌شد، زیرا فراخوانی دو بار اتفاق می‌افتاد.تفاوت require و include در phpامیدوارم که برایتان مفید بوده باشد و پیشنهادات و انتقادات خود را دریغ نفرمایید.</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Sun, 25 Oct 2020 11:16:48 +0330</pubDate>
            </item>
                    <item>
                <title>بررسی یا ساخت تصاویر مربعی با PHP/Laravel</title>
                <link>https://virgool.io/@WebPajooh/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%DB%8C%D8%A7-%D8%B3%D8%A7%D8%AE%D8%AA-%D8%AA%D8%B5%D8%A7%D9%88%DB%8C%D8%B1-%D9%85%D8%B1%D8%A8%D8%B9%DB%8C-%D8%A8%D8%A7-phplaravel-hcytaanghfkc</link>
                <description>چند روز پیش در لینکدن مطلبی درباره‌ی Validation و بررسی مربعی‌بودن تصویر ورودی کاربر در لاراول نوشته بودم که می‌خواهم در ابتدا توضیح مکرری در رابطه‌ی با آن داشته باشم:نسبت ابعاد (Aspect ratio)، نسبت عرض تصویر به ارتفاع آن است. کاری با نسبت 14:3 یا 16:9 نداریم، نسبت طول به عرض مربع قطعاً 1 است؛ به این معنا که طول و عرض هر مربع مساوی است و در نتیجه هر تصویر مربعی شکلی باید از این نسبت پیروی کند.فریم‌ورک لاراول Ruleهای متنوعی دارد که یکی از آنها dimensions است. شما می‌توانید شرط حداکثری برای عرض و ارتفاع تصاویر ورودی در نظر بگیرید:&#039;photo&#039; =&gt; &#039;dimensions:max_width=800,max_height=800&#039;همچنین این Rule از حالات دیگری هم پشتیبانی می‌کند که در مستندات لاراول به آنها اشاره شده است، اما با ratio کار داریم و بررسی‌کردن مربعی‌بودن تصویر واردشده:&#039;photo&#039; =&gt; &#039;dimensions:ratio=1&#039;به این ترتیب اگر کاربر تصویری آپلود کند که یکی از دو ابعاد آن بر دیگری بِچَربد، سیستم ورودی را نپذیرفته و می‌تواند خطایی بازگرداند.نازنینم، تمام اینها را پیش‌تر گفته بودم! یکی از دوستان در کامنت نوشتند:به نظرم روش مرسوم ترش برای این جور مواقع این هست که بجای چک کردن این قضیه و ارور برگردوندن،‌ خودتون تصویر رو به نسبت دلخواه برش بزنید. یعنی تو توضیحاتش به کاربر پیشنهاد بشه که ترجیحا خودتون تصویر مربعی بارگزاری کنید. ولی اگر به هر دلیلی نکرد یا یکی دو پیکسل اختلاف وجود داشت، بجای اینکه کلا فرایند متوقف به اصلاح عکس از سمت کاربر باشه میشه این کارو خودمون انجام بدیم.کامنت بسیار معقول و مفیدی بود! بیایید برای سرگرمی هم که شده، این مورد را با هم پیاده‌سازی کنیم!در زبان PHP توابعی برای کارکردن با تصاویر وجود دارد و اگر بخواهیم تصویری را Crop کنیم، از فانکشن imagecrop استفاده می‌کنیم؛ در مستندات PHP تکه‌کدی دیدم که کار با این فانکشن را به تصویر کشیده، با ذره‌ای تغییر چنین چیزی در اختیار داریم:برش‌زدن تصویر از ابتدای آنتوضیح: ابتدا تصویری را باز کرده‌ایم. در خط دوم کوچک‌ترین ابعاد تصویر را پیدا کرده‌ایم، زیرا اگر بزرگ‌ترین را انتخاب کنیم، تصویر خالی می‌ماند، پس باید کوچک‌ترین را انتخاب کنیم تا تصویر برش بخورد. در خط سوم از ابتدای عرض و ارتفاع تصویر شروع کرده‌ایم و به میزان size از هر دو طرف برش زده‌ایم و در نهایت تصویر را ذخیره کرده‌ایم.خروجی را با هم ببینیم:نتیجه‌ی اولین تلاش!همممم مشکلی داریم! بهتر نبود که برش را از ابتدای تصویر شروع نکنیم تا مرکز تصویر را حذف کنیم؟ برای حل مشکل، يک خط به کدهای قبلی (پیش از Cropکردن) اضافه می‌کنیم و بجای دادن مقدار 0 به x در پارامترهای imagecrop، از این متغیر جدید استفاده می‌کنیم:$x = (imagesx($im) - $size) / 2;برای اینکه بهتر متوجه شوید، تصویر زیر را آماده کرده‌ام که نتیجه‌ی آن حفظ مرکز تصویر است و مشکل قبلی را ندارد:نگه‌داشتن حفظ مرکز تصویرامیدوارم که این پست مورد توجه دوستان قرار گرفته باشد، منتظر دیدگاه‌های خوبتان خواهم بود. ?❤️</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Mon, 21 Sep 2020 13:02:48 +0330</pubDate>
            </item>
                    <item>
                <title>به آسانی ربات تلگرامی خود را بسازید!</title>
                <link>https://virgool.io/@WebPajooh/telebot-for-php-utqohnnayxfi</link>
                <description>یکی از اولین دغدغه‌های کسی که می‌خواهد ربات تلگرامی بسازد، این است که نمی‌داند از چه کتابخانه‌ای استفاده کند. اولین رباتی که ساختم، از هیچ کتابخانه‌ای استفاده نمی‌کرد و نامرتب‌بودن کدها بسیار آزاردهنده بود. بعدها با چند کتابخانه آشنا شدم و ربات‌های مختلفی را با یکی از آنها ساختم، روش استفاده نسبتاً آسان بود و وقتی IDE متدها را پیدا کرده بود، کارها با سرعت خوبی پیش می‌رفت.مدت‌ها گذشت و به Bot API تلگرام، متدهای جدیدی اضافه شد که آن کتابخانه نمی‌توانست پشتیبانی کند. اکنون باید چه می‌کردم؟ مجبور شدم که کلاس‌هایی را به کتابخانه اضافه کنم و برای نویسنده بفرستم تا ادغام کند، اما روندی نبود که در زمان کوتاهی انجام شود، و بعد پرسیدم: چرا برای آسانی کارم، یک کتابخانه برای خودم ننویسم؟ کتابخانه‌ای که نیازهای همیشگی‌ام را برآورده کند و کمبودهای کتابخانه‌های قبلی را نداشته باشد؟معرفینام این کتابخانه را TeleBot گذاشته‌ام؛ کم‌حجم و سبک است و در حال حاضر نیازهای اصلی را رفع می‌کند و در آینده امکانات جدیدی اضافه خواهد شد تا کار توسعه‌دهندگان دیگر را آسان‌تر کند. برای اینکه انعطاف‌پذیری کتابخانه بالا باشد، تمامی متدهای مربوط به تلگرام، به صورت داینامیک کار می‌کنند، پس در صورت ارائه‌شدن متدهای جدید، کلاس اصلی ما دست‌نخورده باقی می‌ماند.راهنمای نصببرای نصب این پکیج، نیاز به Composer دارید؛ با دستور زیر می‌توانید پکیج را به پروژه‌ی خود اضافه کنید:composer require webpajooh/telebotچگونه کار می‌کند؟بعد از ساخت ربات در Botfather یک Token تحویل گرفته‌اید که برای ارتباط با ربات ضروری است. یک شیء از کلاس TeleBot بسازید و توکن را معرفی کنید:$tg = new TeleBot(&#039;Your-Token&#039;);بعد از این، tg$ در اختیار شماست تا دستورها را اجرا کنید. به این مثال دقت کنید:دریافت دستورات کاربر در ربات تلگرام و پاسخ به کاربربا استفاده از listen، منتظر یک دستور از سوی کاربر می‌مانیم؛ اگر کاربر دستوری که توقع داشته‌ایم را ارسال کند، بدنه اجرا می‌شود و در غیر این صورت، سایر مسیرها بررسی خواهد شد. به آسانی می‌توانید ورودی کاربر را تجزیه و تحلیل کنید و نیازی نیست کدهای اضافی بنویسید، در اولین قسمتی که می‌بینید، با d% یک عدد را مشخص کرده‌ایم که کاربر آن را وارد کرده است، و بعد متغیر age$ همان عدد را در خود دارد، و همینطور می‌توانیم متغیرهای بیشتری را در دستورات خود بگنجانیم.یکی از ویژگی‌های کتابخانه‌ی TeleBot این است که ساختن کیبورد را آسان کرده است و نیازی به نوشتن آرایه‌های پر پیچ و خم نیست! در کد بالا یک کیبورد Inline (به قول بعضی از بچه‌ها، کیبورد شیشه‌ای!) با دو دکمه ساخته‌ایم که هر کدام لینک خود را دارند و در آخر با متد get می‌توانیم کیبورد را بگیریم و برای متد sendMessage ارسال کنیم.یکی دیگر از ویژگی‌هایی که می‌تواند به‌دردبخور باشد، لاگری است که یک فایل لاگ را ایجاد کرده و پیامی که توسعه‌دهنده وارد کرده را در آن می‌نویسد. متد log از کلاس Logger فرقی با هلپر tl ندارد و هر دو یک کار را انجام می‌دهند، پس می‌توانید از دومی به عنوان یک جایگزین آسان استفاده کنید.سفارشی‌سازی کیبوردی که در ربات تلگرامی نمایش داده می‌شودکد بالا، یک کیبورد از نوع Reply keyboard ساخته که چهار دکمه دارد. بجای اینکه دکمه‌ها جداگانه اضافه شوند، می‌توانیم همه را به عنوان پارامتر به متد addButtons معرفی کنیم، و بعد با متد chunk تعیین کنیم که در هر سطر، چند دکمه باشد. از آنجا که در ربات‌های فارسی نیاز داریم که اولین دکمه در سمت راست نمایش داده شود، متد rightToLeft کیبورد را راست‌چین می‌کند و یکی از مشکلات دیرینه‌ی ما را حل خواهد کرد.می‌توانید TeleBot را در گیت‌هاب و Packagist ببینید.شما چه پیشنهادی برای این کتابخانه دارید؟ مشارکت آزاد است و انتقادات را با مهربانی می‌شنوم!</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Wed, 03 Jun 2020 13:27:51 +0430</pubDate>
            </item>
                    <item>
                <title>مشهوترین شیوه‌های ترکیب کلمات: کمل‌کیس و...</title>
                <link>https://virgool.io/@WebPajooh/case-styles-grq8rxwdernt</link>
                <description>تصویر از Oskar Yildiz در Unsplashخلاصه‌ی مطلب: camelCasePascalCasesnake_casekebab-caseحذف فاصله‌ی میان کلماتما باید غالباً در برنامه‌نویسی فاصله‌ی میان کلمات را حذف کنیم، زیرا فاصله برای چیز دیگری قرار داده شده است؛ به عنوان یک مثال، نمی‌توانیم مفهوم user login count را به صورت زیر بنویسیم:user login count = 5زبان‌های برنامه‌نویسی رایج، هر کلمه را به عنوان مفهومی جداگانه در نظر می‌گیرند، برای همین هر کدام از کلمات user و login و count به جدا بررسی می‌شوند و با هم معنای واحدی را تشکیل نمی‌دهند! پس باید به صورت زیر عمل کنیم:userLoginCount = 5بهترین روش‌ها برای ترکیب کلماتنمی‌توان گفت که روش «...» بهترین روش برای ترکیب کلمات است، ما در مثال قبلی فاصله‌ی بین کلمات را حذف کردیم و حروفِ کلمات بعد از کلمه‌ی اول را به شکل بزرگ (Capital) نوشتیم. رایج‌ترین روش‌های ترکیب کلمات عبارتند از: camel case، pascal case، snake case و kebab case.روش (Camel case (camelCaseدر این روش فاصله‌ی بین کلمات حذف شده و ابتدای کلماتی که پس از کلمه‌ی اول می‌آیند، به صورت بزرگ نوشته می‌شود. پس از user login count عبارت userLoginCount به دست می‌آید. این روش بسیار رایج است و معمولاً برای نام‌گذاری متغیرها استفاده می‌شود.روش (Pascal case (PascalCaseاین روش مشابه روش قبلی است، با این تفاوت که ابتدای کلمه‌ی اول نیز باید با حرف بزرگ نوشته شود؛ پس user login count را به UserLoginCount تبدیل می‌کنیم. در بسیاری از زبان‌ها از این روش برای نام‌گذاری کلاس‌ها استفاده می‌شود.روش (Snake Case (snake_caseدر روش Snake case، فاصله‌ها را با خط زیر (Underscore) جایگزین می‌کنیم و از user login count، عبارت user_login_count را می‌سازیم. همچنین در نوع All caps تمامی حروف را بزرگ می‌نویسیم، یعنی از این مثال به USER_LOGIN_COUNT می‌رسیم. از حالت اول معمولاً برای نام‌گذاری فیلدهای جداولمان استفاده می‌کنیم و حالت دوم هم برای نام‌گذاری ثوابت (Constants) رایج است.روش (Kebab Case (kebab-caseاین روش به روش قبلی شباهت دارد و به جای خط زیر از خط تیره (Dash) استفاده می‌کنیم، یعنی ترکیب user login count به صورت user-login-count انجام می‌شود.این سبک بیشتر در URLها به چشم می‌خورد، لینک زیر را ببینید:http://www.blog.com/cool-article-1این راه خوب و تمیزی است و خواندن URLها را برای انسان آسان می‌کند.حالا کدامشان «بهتر» است؟! گفتیم که «بهتر»ای وجود ندارد و مهم این است که از فرم استاندارد پیروی کنید و خصوصاً اگر در یک تیم کار می‌کنید، درباره‌ی استفاده‌ی از یک استاندارد به توافق برسید.آنچه خواندید، ترجمه‌ای از این نوشته بود، شما چه تجربیاتی در این باره دارید؟!</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Tue, 05 May 2020 17:14:38 +0430</pubDate>
            </item>
                    <item>
                <title>چند نکته‌ی امنیتی برای اپلیکیشن‌های پی‌اچ‌پی</title>
                <link>https://virgool.io/@WebPajooh/%DA%86%D9%86%D8%AF-%D9%86%DA%A9%D8%AA%D9%87%DB%8C-%D8%A7%D9%85%D9%86%DB%8C%D8%AA%DB%8C-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86%D9%87%D8%A7%DB%8C-%D9%BE%DB%8C%D8%A7%DA%86%D9%BE%DB%8C-j3ljumlykknr</link>
                <description>بر خلاف تصوری که بسیاری از زبان PHP دارند، هنوز هم این زبان یک ابزار کارآمد برای ساخت اپلیکیشن‌های وب به شمار می‌رود. در این پست درباره‌ی نکاتی که می‌توانند به امن‌تر شدن وب‌سایت شما کمک کنند گفتگو می‌کنیم.روتِ وبِ خود را به فایل index و assetها محدود کنید!در پی‌اچ‌پی مدرن، فایل index.php که دروازه‌ی ورود به اپلیکیشن شماست را به علاوه‌ی فایل‌های asset در روت قرار می‌دهید؛ فایل index.php پکیج‌های Composer را لود می‌کند و پس از آن به اجرای کدهای شما برای رسیدن به نتیجه‌ی مورد نظر می‌پردازد. فایل‌های config و مواردی از این قبیل نباید در این قسمت وجود داشته باشند و تنها فایل‌های ایستا (static) را در این محل قرار دهید.مترجم: ساختار پروژه‌ی لاراولی به همین شکل است؛ فایل index.php در public قرار دارد و پکیج‌ها و اپلیکیشنی که توسعه‌دهنده نوشته، همگی از دایرکتوری پیش از public لود می‌شوند.نمایش خطاها را غیر فعال کنید!در php.ini می‌توان با display_errors، پی‌اچ‌پی را وادار کرد تا خطاهایی که در حین اجرا رخ می‌دهند را نمایش دهد. خطاهای نشان‌داده‌شده می‌توانند حاوی اطلاعات محرمانه و کوئری‌های دیتابیس باشند که دیدن آنها توسط کاربران می‌تواند خطرناک باشد و امنیت وب‌سایت را تهدید کند، پس در حالت production باید غیر فعال باشند.در فایل php.ini، به تنظیم این قابلیت به صورت زیر بپردازید:display_errors = Off 
display_startup_errors = Offاما خطاها را از کجا بفهمیم؟ می‌توانید به شکل زیر عمل کنید:log_errors = 1
error_log = /var/log/php-error.logامکان تغییردادن مسیر فایل لاگ یا قراردادن آن در لاگ‌های سطح سیستم عامل نیز وجود دارد و از این پس با بررسی این فایل از خطاهای به‌وجودآمده مطلع می‌شوید.مترجم: در فریم‌ورک لاراول، گزینه‌ی APP_DEBUG در فایل env. مسئول این بخش است و اگر فعال باشد، کدهای شما هم قابل رؤیت هستند!از password_hash استفاده کنید!هرگز کلمات عبور کاربران را همانطور که هستند، در دیتابیس ذخیره نکنید و در بعضی مقالات هم توصیه کرده‌اند که از md5 یا hash استفاده کنید، که این‌ها هم نباید استفاده شوند! در پی‌اچ‌پی از تابع password_hash برای هش‌کردن کلمات عبور استفاده می‌کنیم:&lt;?php
$hash = password_hash(&#039;iamapassword&#039;, PASSWORD_DEFAULT);
if (password_verify(&#039;iamapassword&#039;, $hash)) {
   echo &amp;quotThe hash matches!\n&quot;
}تابع password_hash یک رشته و یک الگوریتم (که PASSWORD_DEFAULT بهترین گزینه است) را می‌گیرد و کلمه‌ی عبور هش‌شده را برمی‌گرداند که بعداً در هنگام ورود کاربر، با فانکشن password_verify به بررسی و تطبیق آن می‌پردازیم و آن هم یک مقدار بولی (True یا False) را برمی‌گرداند و به این صورت از درستی کلمه‌ی عبوری که کاربر برای یک نام کاربری وارد کرده است، مطلع می‌شوید.تمام ارتباطات را رمزگذاری کنید!در اپلیکیشن پی‌اچ‌پی شما، ارتباطاتی با دیگر سرویس‌ها وجود دارد که می‌بایست رمزگذاری شود. وقتی از curl یا soap استفاده می‌کنید، https را فراموش نکنید تا از رمزگذاری اطلاعات رد و بدل شده مطمئن باشید. همچنین اگر از FTP استفاده می‌کنید، از یک فرم مطمئن مانند FTPS یا SFTP استفاده کنید.از فریم‌ورک‌های قالب استفاده کنید!همانطور که می‌دانید، کدهای PHP می‌توانند کدهای HTML را در درون خود داشته باشند، اما در حالت پیشفرض به بررسی خروجی‌ها نمی‌پردازد. اگر یکی از خروجی‌ها حاوی یک کد مخرب جاوااسکریپت باشد، در صفحه اجرا می‌شود و این می‌تواند امنیت کاربران و حتی مدیران را به خطر بیندازد! راه حل، استفاده از یک موتور قالب مانند Twig است که به طور خودکار به بررسی و پاکسازی خروجی‌ها می‌پردازد و دیگر فراموشی شما، حادثه‌ساز نخواهد شد!مترجم: لاراول از موتور قالبی به نام Blade استفاده می‌کند و دیگر نیازی به استفاده از Twig و موتورهای قالب دیگر نخواهید داشت. کد زیر نشان می‌دهد که چگونه عمل پاکسازی خروجی را انجام دهیم دهیم و چطور گاهی از آن چشم‌پوشی کنیم:&lt;h2&gt;{{ $title }}&lt;/h2&gt;
&lt;h2&gt;{!! $title !!}&lt;/h2&gt;پیشنهاد می‌کنم که پست با htmlspecialchars انجام دهید و ندهید! را هم مطالعه کنید.آنچه خواندید، ترجمه‌ای از مقاله‌ی زیر بود:https://dev.to/restoreddev/security-tips-for-a-php-application-4e9a</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Sat, 11 Jan 2020 14:31:57 +0330</pubDate>
            </item>
                    <item>
                <title>علایم نشان‌دهنده‌ی اینکه در برنامه‌نویسی افتضاح خواهید بود!</title>
                <link>https://virgool.io/@WebPajooh/%D8%B9%D9%84%D8%A7%DB%8C%D9%85-%D9%86%D8%B4%D8%A7%D9%86%D8%AF%D9%87%D9%86%D8%AF%D9%87%DB%8C-%D8%A7%DB%8C%D9%86%DA%A9%D9%87-%D8%AF%D8%B1-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87%D9%86%D9%88%DB%8C%D8%B3%DB%8C-%D8%A7%D9%81%D8%AA%D8%B6%D8%A7%D8%AD-%D8%AE%D9%88%D8%A7%D9%87%DB%8C%D8%AF-%D8%A8%D9%88%D8%AF-j8kqqp7nnorc</link>
                <description>من که بیست خط کد نوشته‌ام، چرا بیست و یک ارور دارد؟خیلی‌ها می‌پرسند که &quot;چگونه بدانم که برنامه‌نویس موفقی می‌شوم؟&quot; و این پست، جوابی کافی برای این پرسش است. برنامه‌نویسی یک مهارت پایه‌ای انسانی مانند خواندن، نوشتن و حساب است و هرکسی می‌تواند با صرف وقت و کوشش آن را بیاموزد، اما:کمبود کنجکاویاگر درباره‌ی کامپیوترها و اینکه تکنولوژی چطور کار می‌کند کنجکاو نیستید، هرگز در برنامه‌نویسی موفق نخواهید شد!کمبود استقلال و زیرکیاگر نتوانید مشکلات خود را حل کنید، برنامه‌نویس موفقی نمی‌شوید؛ اگر بیشتر از هجده‌سال سن دارید، کسی چیزی را به شما یاد نمی‌دهد و باید خود به جستجو و یادگیری مشغول شوید. در دنیای توسعه (development) به دنبال هرچیزی که باشید، آن را در اینترنت پیدا می‌کنید، کافی است که سری به گوگل بزنید و به مستندات زبانی که با آن کار می‌کنید مراجعه کنید.عدم تداوم در مواجهه‌ی با مشکلاگر در مواجهه‌ی با یک مشکل، به سادگی پا پس بکشید و تسلیم شوید، هرگز در برنامه‌نویسی موفق نخواهید بود! ذات برنامه‌نویسی، حل‌کردن مسائل و مشکلات است و دلیل اختراع کامپیوتر هم همین بوده که به حل مسائل کمک کند و باید بدانید که با غلبه‌کردن بر هر چالش، درک عمیق‌تر و توانایی بیشتری در حل چالش‌های آینده خواهید یافت.عدم احساس موفقیت بعد از حل یک مشکلاگر پس از غلبه‌کردن بر یک چالش، احساس موفقیت نکرده و هیجان‌زده نمی‌شوید، برنامه‌نویس خوبی نخواهید شد! همیشه به موفقیت‌های خود افتخار کنید، استراحت داشته باشید و برای کاری که انجام داده‌اید، به خودتان تبریک بگویید!بی‌حوصلگی در روند یادگیریاگر در یادگیری بی‌حوصله هستید و توقع دارید که همه چیز را به سادگی و در سریع‌ترین زمان ممکن بیاموزید،  هرگز برنامه‌نویس موفقی نخواهید شد! دنیای فناوری مانند یک اقیانوس وسیع است که کسی به انتهای آن نمی‌رسد و همین باعث شده که هیچکس نتواند ادعای استادبودن داشته باشد. اگر نتوانید آنچه تاکنون یادگرفته‌اید را بپذیرید و «کمی بیشتر» یاد بگیرید، به هیچ‌جا نرسیده و تسلیم خواهید شد!آیا فکرکردن را حوصله‌سربر می‌دانید؟!برنامه‌نویسی یک فعالیت فکری است و اگر فکرکردن حوصله‌ی شما را سر می‌برد، چگونه توقع دارید که برنامه‌نویس خوبی شوید؟! فکرکردن مانند رفتن به باشگاه بدن‌سازی است و هرچه بیشتر فکر کنید، قوی‌تر خواهید شد و مهارت حل مسئله‌ی بیشتری کسب خواهید کرد.تفکر متعصبانه، سطحی و بی‌نظمگاهی بعضی از افراد را می‌بینیم که متعصب هستند و دیدگاهشان تحت هیچ شرایطی تغییر نمی‌کند، و گروهی دیگر بی‌نظم فکر می‌کنند و ممکن است برنامه‌ای که برای نوشتن آن به ده‌خط کد نیاز است را با صدخط تمام کنند! استفاده‌نکردن از دیدگاه‌های دیگران مانع از پیشرفت شده و از بالارفتن کیفیت کاری که انجام می‌دهید می‌کاهد.جواب خوب و جواب بد، یا جواب درست؟!اگر هدف نهایی از نظر شما یافتن یک جواب درست باشد، نه اینکه به مجموعه و طیفی از جواب‌های خوب و بد فکر کنید، برنامه‌نویس موفقی نخواهید شد! داشتن یک ذهن خلاق به شما کمک می‌کند که طیفی از جواب‌ها را داشته باشید و با گزینه‌های مختلفی بازی کنید و راه حل‌های مختلف را ارزیابی کنید.خوب‌توجه‌نکردن به جزئیاتاگر از چیزهای کوچک غافل می‌شوید، برنامه‌نویس خوبی نمی‌شوید؛ کامپیوترها، ماشین‌های دقیقی هستند و وقتی نوبت به برنامه‌نویسی برسد، باید همانطور که کامپیوتر انتظار دارد رفتار کنید و حتی فضاهای خالی و سیمی‌کالن‌ها دارای اهمیت هستند و در صورت عدم ورود درست دستورات، با خطا مواجه خواهید شد. خوشبختانه ابزارهایی هستند (IDEها را در نظر بگیرید، مترجم) که شما را در یافتن خطاها یاری می‌دهند و اشتباهاتتان را اصلاح می‌کنند.من تب پول دارم!اگر تبِ پول دارید و به هدف میلیونرشدن قدم به دنیای نرم‌افزار گذاشته‌اید، عقب‌گرد کرده و سریعاً محل را ترک کنید! اگر برنامه‌نویسی را تنها وسیله‌ای برای رسیدن به تجارت و ثروتمندشدن می‌بینید و تمرکزتان بجای روند بر روی هدف باشد، برنامه‌نویس موفقی نخواهید شد و در مواجهه‌ی با مشکلات بی‌تاب شده و حتی یک سیمی‌کالن می‌تواند بلیت برگشت شما باشد!لُب کلامعلیرغم اینکه برنامه‌نویسی، مهارت پیچیده و سختی به شمار می‌رود، اکثر افراد قادر به یادگرفتن آن هستند و می‌توانند که بر موارد لیست بالا غلبه کنند و به موفقیت برسند. اگر شوق آموختن و حل مسئله دارید، لیست بالا را به خاطر داشته باشید، شروع کنید و منابع موجود آنلاین که می‌توانند به یادگیری شما شتاب بدهند را دنبال کنید، پشیمان نمی‌شوید!آنچه خواندید، ترجمه‌ای مختصر و گزینشی از مقاله‌ی زیر بود:https://blog.usejournal.com/10-signs-you-will-suck-at-programming-5497a6a52c5c</description>
                <category>WebPajooh</category>
                <author>WebPajooh</author>
                <pubDate>Wed, 08 Jan 2020 11:56:20 +0330</pubDate>
            </item>
            </channel>
</rss>