<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های sepisoltani</title>
        <link>https://virgool.io/feed/@sph_1996</link>
        <description>Senior backend developer</description>
        <language>fa</language>
        <pubDate>2026-04-15 10:40:58</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/114973/avatar/vRxLAS.png?height=120&amp;width=120</url>
            <title>sepisoltani</title>
            <link>https://virgool.io/@sph_1996</link>
        </image>

                    <item>
                <title>من هم رفتم!</title>
                <link>https://virgool.io/@sph_1996/%D9%85%D9%86-%D9%87%D9%85-%D8%B1%D9%81%D8%AA%D9%85-vk6hnsyviibp</link>
                <description>ایران عزیزم! از پنجره هواپیمایی که ایران رو به سمت قطر ترک میکرد.
هنوز یادم نمیره! چندین سال پیش بود دقیق یادم نیست شاید ۱۰ سال پیش بود. اون زمان یه وبلاگ داشتم و همیشه دوس داشتم ظاهر و قیافه وبلاگمو عوض کنم! بنر قالب رو عوض کنم یا این که رنگ پس زمینه قالب وبلاگم رو عوض کنم ولی خب بلد نبودم. از سر کنجکاوی سورس کد وبلاگمو دانلود کردم و با نرم افزار نوت پد بازش کردم و وقتی کد هارو دیدم تعجب کردم. گفتم یا خدا اینا چیه!  کم کم تغیراتی رو که میخواستم اعمال کردم و بیشتر علاقه مند شدم و رفتم دنبال یادگیری برنامه نویسی. از کلاس های برنامه نویسی شروع کردم. یه سری دوره شرکت کردم و رفتم دانشگاه رشته آی تی. اونجا دیگه بیشتر علاقه مند شدم. بعد رفتم چند تا شرکت و برنامه نویسی اندروید رو امتحان کردم. از اندروید خوشم نیومد و رفتم سراغ همون وب. شرکت های زیادی توی اصفهان استخدام شدم و به عنوان برنامه نویس ارشد کار کردم و حقوق خوبی داشتم. بعد شروع کردم یادگیری بلاکچین و قرارداد های هوشمند چون بهش علاقه داشتم. حالا ۱۰ سال از اون قصه میگذره و من یه برنامه نویس با تجربم. این شد که تصمیم گرفتم برم شرکت های بهتری کار کنم. دیگه کار توی شرکت های ایران منو راضی نمیکرد. واسه همین تصمیم گرفتم شانسمو برای کار توی شرکت های خارجی امتحان کنم.اولین کاری که کردم این بود که زبانمو تقویت کردم. البته من از بچگی کلاس زبان رفته بودم و تا حدودی زبانم خوب بود ولی خب خیلی از کلمه ها از یادم رفته بود یا بعضی از گرامر ها از خاطرم رفته بودن. واسه همین رفتم یه دوره کوتاه مدت کلاس زبان و خودم هم سعی کردم توی خونه کتاب های زبان رو بخونم تا بتونم خودمو برای مصاحبه ها آماده کنم.در حین اینکه داشتم زبان میخوندم و مرور میکردم سعی کردم رزومه خودمو آماده کنم و آپدیت کنم. دست به قلم شدم و تمام چیز هایی که بلد بودم رو نوشتم و یه رزومه سه صفحه ای آماده کردم و به خودم گفتم ایول این دیگه بهترین رزومه ای هست که میشه نوشت. خلاصه خوشحال بودم :)) ولی خب اشتباه بودم. بعدا میگم چرا اشتباه بود!یکی از اصلی ترین کار هایی که کردم این بود که رفتم توی لینکدین و ثبت نام کردم و یه پروفایل خیلی خوب ساختم و تمام قسمت های پروفایلم رو تکمیل کردم. یه عکس خیلی خوب گذاشتم برای پروفایلم. قسمت مهارت ها و سوابق کاری رو کامل پر کردم و هر شرکتی که کار کرده بودم رو وارد کردم و یه توضیح کوتاه هم برای هر کدوم نوشتم که تو هر شرکتی من چه کار هایی کردم و چه مسولیت هایی داشتم. در نهایت پروفایل خودمو کامل کردم و شروع کردم برای شرکت های خارجی رزومه فرستادم. توی لینکدین شرکت های خارجی هر شرکت یه قسمت jobs داره که شما میتونید آگهی های شغلی اون شرکت رو ببینید و واسشون اپلای کنید. من هم دقیقا همین کار رو کردم.تقریبا سه ماه وقت گرفت و من هر هفته برای شرکت ها رزومه میفرستادم تا اینکه تونستم از بین هزار شرکتی که اپلای کردم ۱۲ تا مصاحبه بگیرم و از بین ۱۲ مصاحبه از ۲ تا شرکت جاب آفر گرفتم. یک شرکت سوئدی و یک شرکت هلندی.در نهایت از بین این دو شرکت شرکتی که توی هلند بود رو انتخاب کردم و دلایل زیادی داشت که هلند رو انتخاب کردم و الان که دارم این مطلب رو مینویستم ۶ ماهه که اینجام و راضی ام از انتخابم.دوس دارم در ادامه پست هایی در مورد مهاجرت کاری به اروپا بنویسم و تجربه های خودمو با شما به اشتراک بزارم و در مورد هلند بنویسم. پس با من همراه باشید.</description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Sun, 04 Sep 2022 04:41:28 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش زبان Solidity (قسمت پنجم)</title>
                <link>https://virgool.io/@sph_1996/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%B2%D8%A8%D8%A7%D9%86-solidity-%D9%82%D8%B3%D9%85%D8%AA-%D9%BE%D9%86%D8%AC%D9%85-mve76cdsnrcd</link>
                <description>مفهوم Inheritance در سالیدیتی چیست و چه کاربری دارد؟هر contract می تواند از یک یا چند contract دیگر ارث بری کند. در صورتی که یک contract فرزند یک contract دیگر باشد به فانکشن های پابلیک contract والد خود نیز دسترسی دارد. به این ویژگی ارث بری یا inheritance گفته می شود. از این ویژگی می توان برای  پیاده سازی سناریو های منطقی و واقعی استفاده کرد. برای ارث بری کردن از یک contract باید جلو تعریف contract از کلید واژه is استفاده کرد و نام contract که قرار است از آن ارث بری انجام شود را نوشت.به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/32a4a89ac2893c2f53c63f8ceeb26b65 در مثال فوق قرارداد Cat از قرارداد Animal ارث بری می کند در نتیجه در قرارداد Cat می توان به فانکشن walk که در قرارداد والد تعریف شده دسترسی پیدا کرد و آن را فراخوانی کرد. مفهوم Import در سالیدیتی چیست و چه کاربردی دارد؟هنگامی که چندین فایل contract داشته باشیم می توان قرارداد هارا در یک دیگر import کرد و از آن ها در یکدیگر استفاده کرد. به مثال زیر توجه کنید: https://gist.github.com/sepisoltani/dcee0f4b1f82d3a0da9f2b3670482e6f در مثال فوق در کنار دایرکتوری که فایل قرارداد newContract قرارگرفته یک فایل contract دیگر به اسم SomeOtherContract قرار گرفته که در بالای قرارداد newContract بوسیله کلید واژه import ایمپورت شده است تا بتوان آن را ارث بری کرد.مفهوم Data location در سالیدیتی چیست؟در زبان سالیدیتی متغیر هارا به صورت می توان ذخیره سازی کرد.  ذخیره سازی متغیر در storage  ذخیره سازی متغیر در memory متغیر اگر در storage ذخیره شود به این معناست که دیتای آن برای همیشه در بلاکچین نگه داری میشود. (مثل نگه داری اطلاعات در disk کامپیوتر). متغیر اگر در memory ذخیره شود دیتای آن موقتی است و از بین می رود. ( مثل ذخیره سازی در RAM کامپیوتر). در هنگام تعریف متغیر ها می توان از دو کلید واژه storage و memory استفاده کرد تا مشخص شود متغیر در کجا ذخیره شود. در اکثر موارد لازم نیست توسعه دهندگان تشخیص دهند که متغیر کجا ذخیره شود. خوشبختانه compiler زبان solidity خود قابلیت تشخیص این مورد را دارد. state variables (متغیر هایی که در بالای قرارداد تعریف میشوند نه در فانکشن ها) به صورت پیشفرض در storage ذخیره می شوند و اطلاعات آن ها برای همیشه در بلاکچین نوشته می شود. بقیه متغیر ها( متغیر هایی که در فانکشن ها تعریف می شوند) به صورت دیفالت در  memory ذخیر می شوند و مقدار آن ها پس از پایان فراخوانی فانکشن ها از بین می رود.در بعضی از موارد لازم می شود که یک متغیر را به صورت storage در فانکشن های ذخیره سازی کرد که در این موارد از کلید واژه storage هنگام تعریف آن متغیر باید استفاده کرد.مفهوم function visibility در سالیدیتی چیست و چه کاربردی دارد؟در جلسات پیشین در مورد public و private بودن فانکشن ها توضیح دادم. علاوه بر public و private در سالیدیتی دو مورد دیگر نیز مشابه این موارد وجود دارد. internal و external.فانکشن های internal مشابه private هستند. با این تفاوت که در صورتی که در قرارداد والد تعریف شده باشند در قرارداد های فرزند نیز به آن فانکشن ها می توان دسترسی پیدا کرد و آن هارا فراخوانی کرد.فانکشن های external نیز مشابه public هستند. با این تفاوت که در صورتی که یک فانکشن به صورت external تعریف شده باشد فقط می توان بیرون از قراردادی که در آن تعریف شده به آن دسترسی پیدا کرد و آن را فراخوانی کرد.به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/c8f37821801756cce4d6e7533de68258 در مثال فوق ۲ قرارداد تعریف شده است. قراردادی به اسم Sandwich و قراردادی به اسم BLT.قرارداد BLT از قرارداد Sandwich ارث بری می کند. در نتیجه در قرارداد ‌BLT و در فانکشن eatWithBacon می توانیم به فانکشن eat که در قرارداد والد تعریف شده است درسترسی پیدا کنیم و آن را فراخوانی کنیم. (‌به دلیل اینکه به صورت internal تعریف شده است.)مفهوم Interface در سالیدیتی چیست و چه کاربردی دارد؟در سالیدیتی می توان Interface تعریف کرد. Interface ها در سالیدیتی مشابه contract ها تعریف می شوند. با این تفاوت که در اینترفیس ها فقط می توان امضای  فانکشن هارا تعریف کرد. به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/40fcbf948efe9b0daf87028c90c05a91 در مثال فوق یک Interface تعریف شده است که شامل تعریف فانکشن getNum می باشد. همانطور که می بینید فقط امضا و تعریف این متد مشخص شده است و بدنه ی فانکشن پیاده سازی نشده. برای اتصال از یک contract به یک contract دیگر در شبکه بلاکچین از اینترفیس ها استفاده می شود. فرض کنید در حال توسعه یک contract هستیم. در بین کار نیاز می شود تا یک فانکشن از یک contract دیگر در بلاکچین را فراخوانی کنیم و از اطلاعات آن استفاده کنیم. برای این کار باید ابتدا یک interface از فانکشنی که قرار است صدا بزنیم بسازیم که در قسمت بعدی به آن می پردازم.امیدوارم این قسمت مورد توجه شما قرار گرفته باشد.در قسمت بعدی موارد بیشتری را پوشش می دهم. در صورتی که نیاز داشتید حتما مطالب قسمت های قبلی را نیز مطالعه کنید.در صورتی که تمایل داشتید می توانید من را در لینکدین دنبال کنید و یا سوالات خود را در نظرات بپرسید. ممنونم</description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Fri, 08 Oct 2021 16:02:11 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش زبان Solidity (قسمت چهارم)</title>
                <link>https://virgool.io/@sph_1996/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%B2%D8%A8%D8%A7%D9%86-solidity-%D9%82%D8%B3%D9%85%D8%AA-%DA%86%D9%87%D8%A7%D8%B1%D9%85-cxmzxzpt40m7</link>
                <description>مفهوم event در سالیدیتی چیست؟فرض کنید لازم است هنگامی که یک اتفاقی داخل بلاکچین رخ داد یک سری عملیات دیگری به صورت اتوماتیک شروع بشود. در این مواقع شما باید یک رخداد یا یک event داخل contract تعریف کنید. سپس هر موقع آن اتفاق خاص اتفاق افتاد این event را emit یا اجرا کنید. سپس در قسمت های مختلف دیگری می توان به این event گوش داد و اقدامات لازم را انجام داد.به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/035cfa13bb60f6b58c6a4e35f2fb4cdc در contract فوق یک فانکشن با اسم add تعریف شده است که ۲ عدد دریافت می کند و سپس مقدار را بر میگرداند. یک event  با نام IntegersAdded نیز در بالای contract تعریف شده است. در بدنه ی فانکشن هنگامی که عملیات جمع پایان می پذیرد event به اصطلاح fire می شود ( یا emit می شود). حال هر کجای contract یا هر کجا از front end برنامه (در جاوا اسکریپت) می توان به این رخداد گوش داد و هنگامی که این event رخ داد اقدامات لازم را انجام داد . مثلا یک alert به کاربر نمایش داد. نمونه کد جاوااسکریپ برای گوش دادن به event ها می تواند به صورت زیر باشد: https://gist.github.com/sepisoltani/e05ca9e0ad51eee96c418378a228544b مفهوم address در سالیدتی چیست و چه کاربری دارد؟بلاک چین اتریوم از حساب ها (account) تشکیل شده است که می توانید مانند حساب های بانکی به آنها فکر کنید. یک حساب دارای یک balance یا مانده اتر (Ether) (ارز مورد استفاده در بلاک چین اتریوم) است و می توانید پرداخت های اتر را به سایر حساب ها ارسال و دریافت کنید ، درست مانند حساب بانکی شما که می تواند پول را به حساب های بانکی دیگر منتقل کند.هر حساب یا account دارای یک address است که می توانید مانند شماره حساب بانکی به آن فکر کنید. این یک شناسه منحصر به فرد است که به آن حساب اشاره می کند.در واقع  باید بدانید که هر آدرس متعلق به یک کاربر خاص (یا یک قرارداد هوشمند خاص) است. در سالیدی address ها یک نوع دیتا تایپ هم هستند. فرمت ظاهری address ها به صورت زیر هستند :0x0cE446255506E92DF41614C46F1d6df9Cc969183مفهوم mapping در سالیدیتی چیست و چه کاربردی دارد؟تا به اینجای کار با چند دیتا تایپ در سالیدیتی آشنا شده ایم. دیتا تایپ دیگری مانند آرایه نیز وجود دارد که به آن map می گویند. map ها هم برای نگه داری اطلاعات استفاده می شوند ولی به صورت key و value هستند. مانند آرایه های key value در زبان PHP. به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/93582f518205b7671d690be26afa5166 در قرارداد فوق یک map به اسم accountBalance تعریف شده است. در این map می توان اطلاعات را به صورت key value ذخیره کرد. یعنی هر آدرس به یک عدد از نوع uint اشاره کند.از این روش می توانیم در برنامه های مالی استفاده کنیم. یعنی موجودی هر شخص را به آدرس خودش مپ کنیم. در آینده می توانیم از map استفاده کنیم و موجودی هر آدرسی که خواستیم را دریافت کنیم.مفهوم msg.sender در سالیدیتی چیست و چه کاربری دارد؟در سالیدیتی ، متغیرهای global خاصی وجود دارد که برای همه فانکشن ها در دسترس است. یکی از آن ها msg.sender است که به آدرس شخص (یا قرارداد هوشمند) که فانکشن فعلی را فراخوانده است اشاره می کند.توجه: در سالیدیتی ، اجرای فانکشن های contract ها همیشه باید با یک عنصر خارجی شروع شود. یک contract روی بلاکچین هیچ کار خاصی انجام نمی دهد تا زمانی که کسی یکی از فانکشن های آن را فراخوانی کند. بنابراین همیشه یک عنصر خارجی که یک فانکشن از contract را کال کند وجود خواهد داشت. آدرس این شخص یا قرارداد هوشمند را می توان به کمک msg.sender دریافت کرد.به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/12e6b9247a055a86e8814ba7874d0e79 یک mapping با اسم favoriteNumber تعریف شده که می تواند جفت هایی به صورت آدرس و یک عدد مورد علاقه آن آدرس را ذخیره کند. یک فانکشن به اسم setMyNumber نیز تعریف شده که یک عدد را دریافت می کند و چون به صورت public نیز تعریف شده همه می توانند آن را فراخوانی کنند و یک عدد برای آن ارسال کنند. سپس این فانکشن این عدد را داخل mapping به آدرس شخصی که این فانکشن را صدا زده (msg.sender) مپ می کند و ذخیره می کند. فانکشن دیگری نیز به صورت public تعریف شده است که باز هم چون به صورت public تعریف شده هر شخصی می تواند این فانکشن را صدا بزند. سپس این فانکشن با استفاده از msg.sender از map عدد مورد علاقه آن ادرس را می خواند و بر می گرداند. در صورتی که آدرس در داخل map یافت نشود عدد صفر return می شود.مفهوم require در سالیدیتی چیست و چه کاربردی دارد ؟گاهی لازم است یک فانکشن فقط در صورتی که یک شرط بر قرار باشد اجرا شود. در غیر این صورت خطایی رخ بدهد و کد اجرا نشود. در این مواقع require استفاده می شود. به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/883d638a3bde231f3a7687aeddf0e869 در مثال فوق یک فانکشن با اسم sayHiToVitalik تعریف شده است که یک name به صورت string دریافت می کند و یک رشته با مقدار Hi را return می کند. در مثال فوق از require استفاده شده است تا فقط در صورتی که پارامتر ارسالی به فانکشن هنگام فراخوانی مساوی با مقدار Vitalik بود مقدار Hi بازگشت داده شود. در هنگام فراخوانی این فانکشن اگر هر مقدار به جز Vitalik ارسال شود require جلوی اجرای فانکشن را می گیرد و برنامه خطا می دهد. از require بیشتر در مواقعی استفاده می شود که نیاز داشته باشیم هر کاربر یا آدرس فقط یک بار یک فانکشن را  فراخوانی کند.امیدوارم این قسمت مورد توجه شما قرار گرفته باشد.در قسمت بعدی موارد بیشتری را پوشش می دهم. در صورتی که نیاز داشتید حتما مطالب قسمت های قبلی را نیز مطالعه کنید.در صورتی که تمایل داشتید می توانید من را در لینکدین دنبال کنید و یا سوالات خود را در نظرات بپرسید. ممنونم</description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Wed, 06 Oct 2021 19:26:24 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش زبان Solidity (قسمت سوم)</title>
                <link>https://virgool.io/@sph_1996/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%B2%D8%A8%D8%A7%D9%86-solidity-%D9%82%D8%B3%D9%85%D8%AA-%D8%B3%D9%88%D9%85-euettghkp9en</link>
                <description>مفهوم private و public در فانکشن های سالیدیتی چیست؟به صورت پیشفرض در زبان سالیدیتی فانکشن ها به صورت public هستند. یعنی اگر مشخص نشود که فانکشن public است یا private به صورت پیشفرض سالیدیتی آن را public در نظر می گیرد و هر شخصی می تواند این فانکشن را فراخوانی کند. گاهی لازم است یک فانکشن فقط توسط بقیه فانکشن های درون contract فراخوانی شود. در این مواقع باید فانکشن را به صورت private تعریف کرد. به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/66e312289831b9845ebcd32996ed402f همانطور که مشاهده می کنید توسط کلید واژه private در انتهای تعریف فانکشن این فانکشن به صورت private تعریف شد و حالا فقط توسط خود contract قابل فراخوانی است. معمولا به صورت توافقی توسعه دهندگان سالیدیتی اول اسم فانکشن های private را با خط تیره شروع می کنند.مفهوم return در فانکشن های سالیدیتی چیست؟مانند بقیه زبان های برنامه نویسی برای برگرداندن نتیجه عملیات درون یک فانکشن از return استفاده می شود. در سالیتی تایپ برگشتی فانکشن ها هم باید مشخص شوند. در مثال زیر تایپ خروجی فانکشن string تعریف شده است. به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/b2ccb9f146d67940e1ea19474988cc20 مفهوم function modifier چیست و چه کاربری دارد؟در فانکشن فوق هیچ متغیری تغییر نکرده. فقط مقدار یک متغیر از contract برگردانده شده و این مقدار دست خوش هیچ تغییری نشده است. به این فانکشن ها فانکشن های view می گویند. یعنی فقط اطلاعات را می خوانند. در واقع باید فانکشن فوق را به صورت زیر تعریف کرد: https://gist.github.com/sepisoltani/ecb4d2706d845017f4680acf9d24d9df گاهی فانکشن ها در سالیدیتی به هیچ متغیری از contract وابسته نیستند. نه متغیر های contract را تغییر می دهند و نه می خوانند. در واقع فقط یک سری پارامتر دریافت می کنند و یک سری عملیات را روی آن دریافتی ها انجام می دهند. در سالیدیتی به این نوع فانکشن ها pure گفته می شود. به مثال زیر دقت کنید . این یک فانکشن pure است و به صورت pure نیز تعریف شده: https://gist.github.com/sepisoltani/549815ef1552cc185575dcdfcafe9d45 همان طور که مشاهده می کنید فانکشن multiply فوق فقط با متغیر های دریافتی خودش یعنی متغیر a و متغیر b کار می کند و هیچ عملیات خواندن و یا نوشتن روی بقیه متغیر های contract ندارد. لذا به صورت pure تعریف شده است.نکته: در سالیدیتی لازم نیست pure بودن یا view بودن فانکشن ها توسط ما تشخیص داده شوند. خوشبختانه کامپایلر سالیدیتی و IDE ها به ما warning می دهند و ما متوجه می شویم.مفهوم Keccak256 چیست و چه کاربری دارد؟فانکشن Keccak256 یک hash فانکشن از نوع SHA3 است و در سالیدیتی از پیش تعریف شده است. این فانکشن ورودی را تبدیل به یک عدد ۲۵۶ بیتی هگزا دسیمال تصادفی تبدیل می کند.در واقع یک تغییر کوچک در ورودی باعث یک تغییر بزرگ در خروجی می شود. ورودی این تابع باید حتما به صورت bytes باشد. یعنی در واقع هر پارامتری که به این فانکشن به عنوان ورودی پاس داده می شود باید pack شود.به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/0e4cfe50df80eb3890096366da2fae22 همانطور که مشاهده می کنید با یک تغییر کوچک در ورودی (aaaab تبدیل به aaaac) حاصل خروجی کاملا متفاوت است. در مثال فوق از فانکشن Keccak256 استفاده شده تا یک عدد سودو کد تصادفی ساخته شود که در ساخت برنامه های dApp اتریوم بسیار کاربردی است.مفهوم type casting چیست و چه کاربری دارد؟گاهی لازم است data type ها را به هم تبدیل کرد. به مثال زیر دقت کنید: https://gist.github.com/sepisoltani/ea65f7d2e8d2525d232aac0bc9fa0003 در کد فوق حاصل ضرب a در b یک uint است نه uint8. در نتیجه چون نتیجه حاصل ضرب در متغیر c ( که از نوع uint8 تعریف شده است) ریخته شده است برنامه خطا می دهد. برای رفع خطا می توانیم تایپ b را به uint8 تغییر دهیم. کافیست از فانکشن uint8 استفاده کنیم و متغیر b را برای آن ارسال کنیم تا تغییر تایپ یا type casting صورت بگیرد.امیدوارم این قسمت مفید واقع شده باشه برای شما.قسمت های جدید به زودی آپلود می شوند.لطفا قسمت های قبلی یعنی قسمت اول و قسمت دوم را هم مطالعه کنید.اگر دوست داشتید من را در لینکدین دنبال کنید. ممنونم</description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Wed, 06 Oct 2021 13:10:44 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش زبان Solidity (قسمت دوم)</title>
                <link>https://virgool.io/@sph_1996/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%B2%D8%A8%D8%A7%D9%86-solidity-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-unwesr9kc7aq</link>
                <description>مفهوم آرایه (Arrays) چیست و چه کاربردی دارد؟مثل اکثر زبان های برنامه نویسی از آرایه ها برای نگه داری چندین مقدار استفاده می کنیم. در سالیدیتی ۲ نوع آرایه وجود دارد. آرایه fixed و آرایه dynamic.ارایه های fixed طول ثابتی دارند و قابل افزایش نیستند. آرایه های dynamic طول ثابتی ندارند و می توان طول آن ها را افزایش داد و مقادیر بیشتری به آن ها اضافه کرد.در هنگام تعریف یک آرایه باید data type عناصر آن مشخص باشد. به مثال های زیر دقت کنید: https://gist.github.com/sepisoltani/597618f74a4a95aa6670eb5feb75d732 شما می توانید یک آرایه از نوع struct هم تعریف کنید . در قسمت اول آموزش یک struct از نوع Person تعریف کردیم. به مثال زیر دقت کنید . یک آرایه از نوع Person و به صورت داینامیک تعریف شده. https://gist.github.com/sepisoltani/23dd1f34e16176c0decb6f200a3072e6 به خاطر دارید که در قسمت قبلی گفتم state variable ها برای همیشه در بلاکچین اتریوم ذخیره می شوند؟ با تعریف آرایه های داینامیک می توانیم یک سری اطلاعات را داخل storage نگه داری کنیم. مثل یک دیتابیس. به آن عنصر اضافه کنیم. اجزای ارایه را تغییر بدهیم. به آن عنصر اضافه کنیم. یا از آن عنصر حذف کنیم. دقیقا مثل یک پایگاه داده.مفهوم public arrays چیست و چه کاربری دارد ؟در هنگام تعریف یک آرایه می توانیم آن را public تعریف کنیم. در واقع یک آرایه public از بیرون contract خودش قابل خواندن است. ولی قابل نوشتن نیست. آرایه های public می توانند برای نگه داری اطلاعات public داخل contract ها مورد استفاده قرار بگیرند.مفهوم function چیست و چگونه یک function تعریف کنیم ؟مانند اکثر زبان های برنامه نویسی از فانکشن ها برای دسته بندی کد هایی که رفتار به خصوصی دارند استفاده می کنیم. به فانکشن زیر دقت کنید. https://gist.github.com/sepisoltani/e6b9bccce9d01eb39a814ef0e7fb5da7  یک فاکشن با اسم eatHotDogs تعریف شده است که ۲ پارامتر (ورودی های تابع) می گیرد. یکی از نوع uint و کی از نوع string. بدنه ی این فانکشن فعلا خالی هست. توجه کنید که فانکشن به صورت public تعریف شده است. اگر دقت کنید متوجه می شوید که مشخص کردیم متغیر name کجا ذخیره شود . در داخل مموری (memory) ! . برای دیتا تایپ های از نوع reference type یعنی arrays - struct ها و mapping ها و string ها حتما باید مشخص کنیم کجا ذخیره بشوند. حالا reference type چی هست ؟در سالیدیتی به ۲ روش می توان اطلاعات را به فانکش ها پاس داد. یکی By value و دیگری By reference.پاس دادن به صورت By value به این معنی است که کامپایلر سالیدیتی یک کپی جدید از مقدار پارامتر ایجاد کرده و آن را به فانکشن شما منتقل می کند. این به فانکشن شما اجازه می دهد تا مقدار را بدون نگرانی از تغییر مقدار پارامتر اولیه تغییر دهید. یعنی اگر مقدار پارامتر را داخل فانکشن تغییر دهید مقدار پارامتری که از بیرون فانکشن پاس دادید تغییر نمی کند. پاس دادن به صورت By reference هم یعنی که فانکشن شما با اشاره به متغیر اصلی فراخوانی می شود. بنابراین، اگر فانکشن شما مقدار متغیری را که دریافت می کند تغییر دهد، مقدار متغیر اصلی که از بیرون فانکشن پاس دادید تغییر می کند.اگر بخواهیم یک متغیر را به روش By value به فانکشن پاس بدهیم باید از کلید واژه memory  در تعریف پارامتر فانکشن استفاده کنیم. در فانکشن بالا متغیر name به صورت memory تعریف شده است. یعنی مقدار متغیری که به این پارامتر پاس داده می شود فقط درون فانکشن تغییر می کند.توجه: یک قرارداد و توافق بین تمام توسعه دهندگان سالیدیتی وجود دارد که اول اسم پارامتر های فانکشن ها باید با _ شروع بشود . من از این قرارداد در تمام قسمت های این سری  آموزش استفاده می کنم.برای فراخوانی کردن یک فانکشن به صورت زیر عمل می کنیم.  https://gist.github.com/sepisoltani/c82187009c87037c8468af20ce96ac49 افزودن عنصر به آرایه ها چگونه است ؟به contract زیر دقت کنید . یک struct تعریف شده با نام Person. هر  Person باید یک name و یک age داشته باشد.  همچنین یک آرایه که می تواند تعدادی Person را نگه داری کند به اسم people تعریف شده است.به فانکشن درون contract دقت کنید . بدنه این فانکشن یک Person جدید ایجاد می کند و آن را به آرایه people اضافه می کند. https://gist.github.com/sepisoltani/65b3d8e710f0063e4099b3e44a427a7b توجه کنید که push عنصر جدید را  همیشه به انتهای آرایه اضافه می کند.امیدوارم لذت برده باشید. در قسمت بعدی موارد بیشتری را آموزش می دهیم. اگر قسمت قبل را مطالعه نکرده اید از لینک زیر استفاده کنید و حتما مطالعه کنید. آموزش سالیدیتی قسمت اولاگر دوست داشتید من را در لینکدین دنبال کنید.</description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Tue, 05 Oct 2021 13:50:15 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش زبان Solidity (قسمت اول)</title>
                <link>https://virgool.io/@sph_1996/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%B2%D8%A8%D8%A7%D9%86-solidity-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-qnoyqfzubsy3</link>
                <description>مقدمهدرست مثل برنامه‌ های کامپوتری، قرارداد های هوشمند اتریوم به یک زبان برنامه‌ نویسی نوشته می‌ شوند که solidity نام دارد. لازم به ذکر است که زبان‌های برنامه‌نویسی دیگری هم وجود دارند که می‌توان برای ایجاد قراردادهای هوشمند در شبکه اتریوم، از آنها استفاده کرد، اما این روزها انتخاب اکثر توسعه دهندگان این حوزه سالیدیتی است. در ادامه با هم این زبان را فرا می گیریم.مفهوم contract چیست ؟کد های solidity در contract ها گنجانده شده اند. یک contract بلوک اساسی برنامه های سالیدیتی است. همه متغیرها و فانکشن ها متعلق به یک contract هستند و تعریف contract نقطه شروع همه پروژه های شما خواهد بود. ( برای مثال در زبان جاوا همه چیز ابتدا در class تعریف می شود).یک contract خالی با نام HelloWorld به این شکل است: https://gist.github.com/d540a230f552f1032770f68ae8f54b33.git مفهوم pragma چیست و چه کاربردی دارد ؟به دلیل این که زبان solidity یک زبان کامپایلریست همیشه باید نسخه کامپایلر را در ابتدای contract ها مشخص کرد تا در آینده با معرفی نسخه های جدید کامپایر مشکلی برای اجرای کد شما ایجاد نشود و مشخص باشد که برنامه شما با چه نسخه ای از کامپایلر کار می کند.به عنوان مثال در contract زیر تصمیم داریم کد ما نسخه های ۰.۵.۰ و بالاتر تا نسخه ۰.۶.۰ از کامپایلر را پشتیبانی کند ( خود نسخه ۰.۶.۰ را شامل نمی شود) . پس باید به صورت زیر آن را در ابتدای  contract تعریف کنیم. https://gist.github.com/sepisoltani/905ed4fd6ad20ae45aa8a73cad495a2b مفهوم state variable چیست و چه کاربردی دارد ؟همان متغیر در بقیه زبان های برنامه نویسی هستند با این تفاوت که وقتی مقداری را به آن اختصاص می دهیم آن مقدار برای همیشه داخل contract storage ذخیره می شود. (مثل ثبت اطلاعات داخل یک دیتابیس).به مثال زیر توجه کنید. مقدار ۱۰۰ برای همیشه داخل متغیر myUnsignedInteger در بلاکچین ذخیره می شود. در واقع در این مثال ما یک state variable از نوع uint تعریف کرده ایم (با نام myUnsignedInteger) و مقدار ۱۰۰ را داخل آن ذخیره کرده ایم . توجه کنید که این مقدار برای همیشه داخل بلاکچین ذخیره می شود.  https://gist.github.com/sepisoltani/57c58175f827f605b01f10293fdf4a35 مفهوم unsigned integer یا uint چیست و چه کاربری دارد؟یک data type است و فقط قابلیت نگه داری اعداد مثبت را دارد . برای نگه داری اعداد مثبت و منفی باید از int استفاده کرد.نکته : در سالیدیتی وقتی uint تعریف می شود منظور همان uint256 هست. که قابلیت نگه داری یک عدد مثبت ۲۵۶ بیتی را دارد. uint های دیگری نیز می توان تعریف کرد. مانند uint8 و uint16 و uint32 که در موارد خاص استفاده می کنیم ولی به صورت کلی و برای سادگی از همان uint بیشتر استفاده می کنیم.مفهوم string چیست و چه کاربری دارد ؟برای نگه داری مقادیر رشته ای از دیتا تایپ string استفاده می کنیم.مفهوم struct چیست و چه کاربری دارد ؟گاهی مواقع لازم می شود که یک دیتا تایپ خاص را خودمان تعریف کنیم. بوسیله struct می توانیم یک دیتا تایپ تعریف کنیم که خود شامل property های دیگر باشد. برای مثال به struct زیر دقت کنید. در این جا یک struct با نام Person ساخته شده است که شامل دو مشخصه با نام های age و name است. age از نوع int و name از نوع string. https://gist.github.com/sepisoltani/64627053d3a91be2bfa3de9de6437d5f مفهوم math operations چیست و چه کاربری دارد ؟مانند تمام زبان های برنامه نویسی سالیدی هم قابلیت انجام عملیات ریاضی را دارد.عملیات جمع :  x + yعملیات منها : x - yعملیات ضرب : x * yعملیات تقسیم : x / yعملیات باقیمانده تقسیم دو متغیر : x % yعملیات به توان رسانی در متغیر : x ** yدر قسمت بعدی به مفاهیم آرایه ها و فانکشن ها در سالیدیتی می پردازم. حتما قسمت بعدی را دنبال کنید و مطالعه کنید. ممنون که همراهی کردید.اگر دوست داشتید من را در لینکدین دنبال کنید.</description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Tue, 05 Oct 2021 12:16:13 +0330</pubDate>
            </item>
                    <item>
                <title>بهینه سازی مصرف gas در قرارداد های هوشمند اتریوم</title>
                <link>https://virgool.io/@sph_1996/%D8%A8%D9%87%DB%8C%D9%86%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D9%85%D8%B5%D8%B1%D9%81-gas-%D8%AF%D8%B1-%D9%82%D8%B1%D8%A7%D8%B1%D8%AF%D8%A7%D8%AF-%D9%87%D8%A7%DB%8C-%D9%87%D9%88%D8%B4%D9%85%D9%86%D8%AF-%D8%A7%D8%AA%D8%B1%DB%8C%D9%88%D9%85-v8kaal2bckez</link>
                <description>مفهوم gas چیست ؟ در Solidity ، کاربران شما هر بار که تابع یا فانکشنی را در DApp شما فراخوانی می کنند باید gas پرداخت کنند. کاربران gas را با Ether (ارز  اتریوم) خریداری می کنند ، بنابراین کاربران شما باید Ether را برای اجرای فانکشن های DApp شما هزینه کنند.میزان gas مورد نیاز برای اجرای یک تابع بستگی به پیچیدگی منطق آن تابع دارد. هر عملیاتی هزینه gas دارد که بستگی به میزان منابع محاسباتی برای انجام آن عملیات دارد (به عنوان مثال نوشتن در storage بسیار گرانتر از افزودن دو عدد صحیح است و هزینه gas بیشتری دارد). در نهایت مجموع کل gas یک تابع برابر است با مجموع هزینه gas تمام عملیات های آن تابع.از آنجا که اجرای توابع و فانکشن ها برای کاربران شما هزینه واقعی دارد، بهینه سازی کد در اتریوم بسیار مهم تر از سایر زبان های برنامه نویسی است. اگر کد شما بهینه نباشد، کاربران شما مجبور به پرداخت gas بالا برای انجام توابع شما هستند و این می تواند تا میلیون ها دلار هزینه غیر ضروری برای هزاران کاربر به همراه داشته باشد.چرا باید برای هر بار فراخوانی توابع در قرارداد هوشمند باید gas پرداخت کنیم؟اتریوم مانند یک کامپیوتر بزرگ اما کند و بسیار امن است. وقتی یک تابع را اجرا می کنید، هر node در شبکه باید همان تابع را برای تأیید خروجی خود اجرا کند. هزاران node که هر تابع را تأیید می کنند، اتریوم را غیر متمرکز می کنند و داده های آن را تغییر ناپذیر می کنند.سازندگان اتریوم می خواستند مطمئن شوند که کسی نمی تواند شبکه را با یک حلقه بی نهایت مسدود کند یا تمام منابع شبکه را با محاسبات بسیار زیاد مصرف کند. بنابراین آنها این کار را انجام دادند تا معاملات رایگان نباشند و کاربران باید هزینه زمان محاسبه و همچنین ذخیره را بپردازند.چطور کد بنویسیم تا هزینه gas پایین بیاید ؟در زبان برنامه نویسی Solidity هنگام تعریف متغیر ها باید برای آن ها دیتا تایپ خاصی را مشخص کنیم. مثلا string یا uint256 یا uint8 و یا uint32. فرض کنید در قرارداد زیر ما متغیر های زیر را تعریف کرده ایم : https://gist.github.com/31263b26e7c30d7deff94e662cac4e22.git بر خلاف باور شما هیچ فرقی نمیکند که ما یک متغیر را از کدام نوع uint تعریف کنیم . تمام uint ها در زمان تعریف مقدار ۲۵۶ بیت از فضا را اشغال می کنند در نتیجه تعریف کردن uint8 به جای uint256 فرقی در هزینه gas نمی کند . پس کجا می توانیم هزینه gas را بهبود ببخشیم ؟ در هنگام تعریف struct ها .هنگامی که  uint های مختلفی را داخل یک struct تعریف میکنیم میتوانیم با تعریف uint های کوچک تر این هزینه را کاهش بدهیم . به مثال زیر توجه کنید. فرض کنید ما ۲ عدد struct تعریف کرده ایم :  https://gist.github.com/6b5e7998e2441c02d43704524418ee61.git در این Contract  دو تا struct تعریف شده . One و Twoزمانی که استراک Two اجرا میشود هزینه gas کمتری نسبت به استراک One دارد . دلیل این مورد این است که در صورتی که در یک استراک تایپ های مختلفی داشته باشیم اگر تایپ های مشابه را کنار همدیکر تعریف کنیم کد از لحاظ هزینه gas  هزینه کمتری را در میطلبد. در استراک Two دو تایپ  uint32 داریم که در کنار هم تعریف شده اند. اگر تایپ سوم یعنی uint256 را در بین ان ها قرار میدادیم معادله به هم میخورد و هزینه gas افزایش پیدا میکرد. پس برای بهبود هزینه gas باید در تعریف struct ها دقت به خرج داد. همچنین چون از نوع uint32 نیز استفاده کرده ایم اجرای این struct هزینه gas کمتری را در برمیگیرد.در مقاله های بعدی موارد بیشتری را برای بهبود دادن هزینه gas بیان میکنم.امیدوارم مفید واقع شده باشه.اگر به آموزش های برنامه نویسی علاقه دارید آموزش های من رو از کانال یوتوبم دنبال کنید. ممنونلینک کانال یوتوب من</description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Mon, 30 Aug 2021 16:44:07 +0430</pubDate>
            </item>
                    <item>
                <title>توکن JWT را کجا ذخیره کنیم ؟ localStorage یا  sessionStorage ؟</title>
                <link>https://virgool.io/coderlife/%D8%AA%D9%88%DA%A9%D9%86-jwt-%D8%B1%D8%A7-%DA%A9%D8%AC%D8%A7-%D8%B0%D8%AE%DB%8C%D8%B1%D9%87-%DA%A9%D9%86%DB%8C%D9%85-lwys99cacgpy</link>
                <description>کوکی httpOnlyسلام دوستان .  معمولا هنگام توسعه فرانت اند برای لاگین کردن باید از API استفاده کنیم و نهایتا access_token دریافت شده رو ذخیره کنیم تا در درخواست های بعدی ازش استفاده کنیم!روال کار به صورت زیر است :فرض کنید نام کاربری و کلمه عبور را به صورت درخواست POST برای آدرس زیر ارسال کرده ایم :http://example.com/api/v1/loginو سپس درخواست ما با موفقیت صورت گرفته و احراز هویت شده ایم و access_token را به صورت زیر در جیسون دریافت کرده ایم :HTTP/1.1 200 OK
{&amp;quotaccess_token&amp;quot: &amp;quoteyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB&amp;quot,&amp;quotexpires_in&amp;quot:3600
}حال باید access_token دریافتی را در مرورگر ذخیره کنیم تا در درخواست های بعدی به صورت زیر از آن استفاده کنیم.معمولا سمت کلاینت توکن را در header قرار داده و ارسال میکنیم . به صورت زیر :HTTP/1.1
GET /posts
Host: example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cBهنگامی که توکن دریافت شد برای ذخیره آن در مرورگر دو انتخاب پیش رو هست : HTML5 Web Storage (localStorage or sessionStorage)Cookiesحالا هر روش را به صورت جداگانه توضیح میدم .1.  ذخیره سازی توکن در HTML5 Web Storage :فرض کنید توکن را در قطعه کد زیر دریافت کرده ایم . حال باید آن را ذخیره کنیم اگر بخواهیم در HTML5 Web Storage آن را ذخیره کنیم باید یا در localstorage یا در sessionStorage آن را ذخیره کنیم . به این صورت میتوان عمل کرد : function tokenSuccess(err, response) {
if(err){
throw err;
}
//ذخیره کردن در سشن استوریج
window.sessionStorage.accessToken = response.body.access_token;
//و یا ذخیره سازی در اوکال استوریج
window.localStorage.accessToken = response.body.access_token;
}به دلیل اینکه هم localStorage و هم sessionStorage از طریق جاوا اسکریپت قابل خواندن هستند هکر به راحتی میتواند با روش XSS یعنی با تزریق کد javascript در داخل تگ های خاص سایت مقادیر ذخیره شده در داخل آن ها را بخواند و توکن ذخیره شده را بدست بیاورد و از آن سو استفاده کند .  برای آشنایی بیشتر در مورد حملات XSS میتوانید به این لینک مراجعه کنید . ذخیره سازی توکن در localstorage و  sessionStorage به دلیل این که در مقابل این گونه حملات آسیب پذیر هستند پیشنهاد نمیشود.2.  ذخیره سازی توکن در Cookies :فرض کنید توکن را در قطعه کد زیر دریافت کرده ایم . حال باید آن را ذخیره کنیم اگر بخواهیم در cookie آن را ذخیره کنیم باید به این صورت عمل کنیم :function tokenSuccess(err, response) {
if(err){
throw err;
}
//ذخیره کردن در کوکی
.access_token=&amp;quoteyJhbGciOiJIUzI1NiIsI.eyJpc3MiOiJodHRwczotcGxlL.mFrs3Zo8eaSNcxiNfvRh9dqKP4F1cB; expires=Thu, 18 Dec 2013 12:00:00 UTC&amp;quot
}این روش هم در مقابل حملات XSS آسیب پذیر هست و ذخیره سازی در کوکی هم رد میشود .قبل از این که روشی ایمن را توضیح بدهم در مورد کوکی ها مطالبی رو توضیح میدم پرچم ها و مشخصات یک کوکی:هر کوکی دارای ۷ شاخص یا ویژگی است که دو مورد اولی آن الزامی و بقیه موارد اختیاری هستند. شاخص‌های کوکی عبارت اند از:نام کوکی (الزامی)Set-Cookie: access_token=1s8a65d1as3;محتوای کوکی (الزامی)Set-Cookie: access_token=1s8a65d1as3;تاریخ انقضاءSet-Cookie: access_token=1s8a65d1as3; Expires=Mon, 01 Jun 2014 12:45:30 GMT;مسیر و دامین کوکیSet-Cookie: access_token=1s8a65d1as3; Expires=Mon, 01 Jun 2014 12:45:30 GMT;Domain=hello.example.com; Path=/test;ارسال امن(secure)Set-Cookie: access_token=1s8a65d1as3; Expires=Mon, 01 Jun 2014 12:45:30 GMT;Domain=hello.example.com; Path=/test;Secureمانع از دسترسی سایر پروتکل‌ها (httpOnly)Set-Cookie: access_token=1s8a65d1as3; Expires=Mon, 01 Jun 2014 12:45:30 GMT;Domain=hello.example.com; Path=/test;Secure;HttpOnlyتوجه داشته باشید که فلگ Secure را با HttpOnly اشتباه نگیرید. فلگ HttpOnly کانال انتقال را به پروتکل‌های HTTP و HTTPS محدود می‌کند درحالی که فلگ Secure ارسال کوکی را به کانال‌های امن محدود می‌کند. پس وجود هر دو فلگ امنیت را بیشتر می‌کند.کوکی که به صورت httpOnly ساخته شده باشد نمی توانند توسط هیچ پروتکلی غیر از HTTP استفاده شوند. چنین کوکی هایی اطمینان می دهند که فقط وب سایت هایی که آنها را ایجاد کرده اند می توانند از آن ها استفاده کنند.در واقع این نوع کوکی ها قابل خواندن با جاوااسکریپ نیستند پس در نتیحه در مقابل حملات XSS آسیب پذیر نیستند .  بهتر است در سمت کلاینت کاری با توکن نداشته باشید! ذخیره توکن در سمت کلاینت و با هر روشی امنیت کافی رو نداره !توکن باید در داخل یک کوکی و به صورت httpOnly  از طرف خود سرور در داخل مرورگر ذخیره شود.در واقع در رخواست لاگین وقتی شما احراز هویت شدید در سمت سرور توکن ایجاد میشه و داخل یک کوکی به صورت httpOnly قرار میگیره و روی مرورگر شما میشینه . حالا بعد از هر درخواست ajax ( با axios و یا fetch و یا به صورت ساده ) این کوکی  به صورت خودکار از مرورگر شما ارسال میشه سمت سرور و دیگه شما لازم نیست درگیر ارسال و ذخیره سازی اون باشید.در سمت سرور این کوکی خونده میشه و اگر توکن داخلش معتبر بود ریسپانس رو به شما میده .مثال و پیاده سازی در فریمورک Laravel :‌ به متد login در کنترلر زیر دقت کنید : این متد یک ایمیل و یک پسورد دریافت میکند و سپس در صورتی که این اطلاعات صحیح بود یک توکن ایجاد میکند و یک ریسپانس بر میگرداند .public function login(LoginRequest $request)
{
    $email = $request-&gt;input(&#039;email&#039;);
    $password = $request-&gt;input(&#039;password&#039;);
    try {
        $this-&gt;oauthLoginAdmin-&gt;setEmail($email);
        $this-&gt;oauthLoginAdmin-&gt;setPassword($password);
        $token = $this-&gt;oauthLoginAdmin-&gt;login();
        return  response(&amp;quot&amp;quot, 200)-&gt;cookie(&#039;access_token&#039;, $data[&#039;access_token&#039;], 8000, null, null, true, true);
    } catch (\Exception $exception) {
        return $this-&gt;respondUnauthorizedError();
    }
}وقتی کاربر احراز هویت شد از طریق کد زیر یک کوکی به صورت httpOnly در ریسپانس قرار میگیرد . لازم نیست  خود توکن در ریسپانس بادی قرار بگیرد cookie(&#x27;access_token&#x27;, $data[&#x27;access_token&#x27;], 8000, null, null, true, true);پارامتر ششم این متد که به صورت true ارسال شده است مشخص میکند که این کوکی باید secure باشد. یعنی این کوکی فقط با پروتکل امن https ارسال و دریافت بشه .پارامتر هفتم این متد که به صورت true ارسال شده است مشخص میکند که این کوکی باید httpOnly باشد.در این کد سرور این کوکی در مرورگر کاربر ذخیره میشود و لازم نیست توکن به صورت دستی توسط کد جاوا اسکریپت ذخیره شود و در دخواست های بعدی به صورت دستی در هدر قرارگیرد و ارسال شود . چون کوکی در تمام درخواست های بعدی ارسال میشود و این ویژگی کوکی هاست که در تمام درخواست ها ارسال میشوند.به کد زیر در سمت کلاینت توجه کنید :در کد زیر سمت کلاینت یک ایمیل و یک پسورد سمت سرور ارسال میشود و در صورتی که احزار هویت انجام شده بود یک درخواست دیگر سمت سرور ارسال میشود و لیست پست ها دریافت میشود.
    $( document ).ready(function() {
        $.ajax(&amp;quothttps://example.com/api/v1/login&amp;quot, {
            method: &#039;POST&#039;,
            xhrFields: { withCredentials: true },
            data: {email: &#039;admin@admin.com&#039;, password: &#039;123456&#039;},
            success: function (res,status,xhr){
                $.ajax(&amp;quothttps://example.com/api/v1/posts&amp;quot, {
                    method: &#039;GET&#039;,
                    xhrFields: { withCredentials: true },
                    headers: {&#039;Accept&#039;:&#039;application/json&#039;},
                    success: function (res){
                        console.log(res)
                    },
                });
            },
        });
    });باید دقت کنید که در تمامی درخواست ها   xhrFields: { withCredentials: true },را قرار دهید .این قطعه کد به مرورگر میفهماند که باید در درخواست های ajax تمام cookie ها و authentication headers را هم ارسال کند . بررسی توکن سمت سرور :‌برای کنترل کردن این کوکی در سمت سرور یک middleware ایجاد میکنیم . در این middleware ابتدا کوکی را بررسی میکنیم و توکن را از داخل آن دریافت میکنیم و بررسی میکنیم توکن valid هست یا نه .class AuthApiMiddleWare extends ResponseController
{ 
    public function handle($request, Closure $next)
    {
            if ($request-&gt;hasCookie(&#039;access_token&#039;)) {
                $token = $request-&gt;cookie(&#039;access_token&#039;);
              // check if token is valid or invalid 
             // if invalid return 401 . 
             // if valid  return$next($request);
            }
    }
}خلاصه مطلب : کار ارسال و دریافت و بررسی توکن را بر عهده سرور بگذاریم و توکن را در داخل کوکی httpOnly از سمت سرور بر روی مرورگر کاربر ذخیره سازی کنیم . این روش باعث میشود تا سایت در مقابل حملات XSS ایمن باشد . از طرفی چون کوکی به صورت secure در سمت سرور ایجاد میشود فقط از طریق https قابل ارسال و دریافت میشود .  به طور کلی :پرچم HttpOnly برای پیشگیری و جلوگیری از حملات Injection و سرقت نشست‌ها استفاده می‌شود. درمجموع می‌توان این پرچم را به‌عنوان لایه‌ای برای ایمن‌سازی کوکی‌ها و نشست‌ها در نظر گرفت.</description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Mon, 28 Sep 2020 12:01:50 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش دیزاین پترن Strategy و پیاده سازی آن با مثال</title>
                <link>https://virgool.io/@sph_1996/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%AF%DB%8C%D8%B2%D8%A7%DB%8C%D9%86-%D9%BE%D8%AA%D8%B1%D9%86-strategy-%D9%88-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D8%A2%D9%86-%D8%A8%D8%A7-%D9%85%D8%AB%D8%A7%D9%84-w9gcjhcjf4wg</link>
                <description>الگو های طراحی در برنامه نویسیسلام دوستان .قصد دارم الگو های طراحی یا همون design pattern ها در برنامه نویسی را به صورت ساده و کاربردی و همراه با مثال توی چند پست توضیح بدم .حالا اصلا الگو های طراحی چی هستند ؟ خب در طی زمان های گذشته تا به امروز بعضی مواقع برنامه نویس ها در هنگام کد نویسی به یک سری مشکلات و پیچیدگی هایی داخل کد نویسی بر میخوردند که باعث میشد ساعت ها فکر کنند و آزمون و خطا کنند و روش های مختلف در ساختار کد رو بررسی کنند تا بتونند بهترین روش رو به کار بگیرند و اون مساله یا الگوریتم رو پیاده سازی کنند . کم کم این مشکلات و روش های حل خیلی رایج و شناخته میشن و در طی زمان باعث میشه برنامه نویس ها اون روش های حل مساله رو داکیومنت کنن و در کیس مورد نظر خودش استفاده کنند .  در واقع دیزاین پترن ها یک سری روش حل مساله هستند که هر کدومشون یک پیچیدگی را حل میکنند و باعث میشن کار برنامه نویس راحت تر بشه . شما باید با مطالعه کردن اون ها یاد بگیرید که هر کدومشون تو چه مواقعی استفاده میشن و چه کاری رو آسون میکنند .دیزاین پترن ها اصلا وابسته به زبان برنامه نویسی خاصی نیستند و اصلا مهم نیست که شما با چه زبانی دارید کد میزنید . شما فقط باید کاربرد هر کدوم رو بدونید و بدونید چه وقتی از کدوم استفاده کنید . این مهم ترین چیزه . من برای مثال زدن توی آموزش ها از زبان PHP استفاده میکنم ولی شما میتونید با یه سرچ ساده پیاده سازی هر دیزاین پترنی رو داخل هر زبانی پیدا کنید .خب بریم سراغ اولین الگوی طراحی که خیلی کاربردی هست . آموزش الگوی طراحی Strategy :از این الگو زمانی استفاده میکنیم که بخوایم یه کاری رو به چند روش پیاده سازی کنیم . چند تا مثال میزنم . برای مثال : لاگین کردن کاربر داخل سایت ! لاگین کردن به چندین روش صورت میگیره . مثلا لاگین کردن با ایمیل . لاگین کردن با ارسال کد احراز هویت بوسیله پیامک به کاربر . لاگین کردن با گیت هاب و یا لینکداین . در واقع ما چندین روش رو برای لاگین کردن در نظر میگیرم.مثال بعدی پیاده سازی درگاه پرداخت هست . مثلا پرداخت با درگاه ملی . پرداخت با درگاه صادرات . پرداخت با درگاه زرین پال و غیره . در واقع ما سیستم پرداخت رو میتونیم به چند روش پیاده سازی کنیم و فقط نمیخایم از یک درگاه استفاده کنیم.در این مواقع باید از الگوی طراحی استراتژی استفاده کنیم .برای پیاده سازی مواد لازم چیه ؟  اول از همه یک Interface لازم داریم . در داخل این اینترفیس استراتژی خودمون رو تعیین میکنیم. بعد به تعداد روش هایی که میخایم پیاده سازی کنیم کلاس ایجاد میکنیم و اون کلاس هارو ملزم میکنیم تا از قانون و اینترفیس ما پیروی کنند و در واقع یک استراتژی واحد را (که همون اینترفیسمونه) رو implement کنن.مثال : فرض کنید یه سایت داریم و میخایم هر بار کاربر جدیدی داخل سایت عضو شد به اون خوش آمد بگیم . چند روش داریم ؟روش اول : خوش آمد گویی با ارسال یک پیامک به شماره تماس کاربر روش دوم :‌ خوش آمد گویی با ارسال ایمیل خوش آمدگویی به کاربر دو روش بالا رو میتونیم پیش بینی و پیاده سازی کنیم و از هرکدام خواستیم استفاده کنیم . ممکنه امروز بخایم فقط با پیامک خوش آمد بگیم . ولی ممکنه فردا  فقط بخوایم با ایمیل خوش آمد بگیم . خب بریم سراغ پیاده سازی . اول از همه اینترفیس رو ایجاد میکنیم و یه اسم مناسب براش میزاریم . این اینترفیس داره میگه هر کلاسی از من استفاده کرد باید متد sayWelcome رو پیاده سازی کنه.در واقع ما  اینجا استراتژی مشخص میکنیم . interface WelcomeStrategy
{
    function sayWelcome($user);
}خب . حالا باید برای هر روشی یک کلاس ایجاد کنیم که از اینترفیس پیروی کنه. برای خوش آمد گویی با ایمیل یک کلاس و برای خوش آمد گویی با پیامک یک کلاس جدا باید بسازیم .اول کلاس خوش آمد گویی با ایمیل را مینویسیم :class WelcomeWithEmail implements WelcomeStrategy
{

    function sayWelcome($user)
    {
        // get user email address
        // use mailer php class
        // send email to user email address
        return &amp;quotwelcome with email&quot;
    }
}حالا کلاس خوش آمد گویی با روش ارسال پیامک :class WelcomeWithSMS implements WelcomeStrategy
{

    function sayWelcome($user)
    {
        // get user phone number
        // use sms class
        //send sms to user phone number
        return &amp;quotwelcome with sms&quot;
    }
}به دو کلاس فوق توجه کنید . هر دو کلاس قانون را پیاده سازی کردند . در واقع هر دو کلاس دارند متد sayWelcome رو پیاده سازی میکنند ولی هر کلاسی داره به یک روش مخصوص به خودش این کارو میکنه . اولی داره ایمیل ارسال میکنه ولی دومی داره پیامک ارسال میکنه . ولی در هر صورت دارند خوش آمد میگن . ولی هر کدام به یک روش . حالا باید یک کلاس جدید به اسم Welcome بسازیم :require_once &#039;WelcomeStrategy.php&#039;;

class Welcome
{
    private $strategy;

    public function __construct(WelcomeStrategy $strategy)
    {
        $this-&gt;strategy = $strategy;
    }

    public function say($user)
    {
        return $this-&gt;strategy-&gt;sayWelcome($user);
    }
}به متد سازنده کلاس بالا نگاه کنید . وقتی یک Interface رو به متد سازنده بدیم در واقع داریم به این کلاس میگیم وقتی میخوایم از تو شی جدید بسازیم میتونیم هر کلاسی که این interface رو پیاده سازی میکنه برات ارسال کنیم . در واقع میتونیم هر کلاسی که اینترفیس WelcomeStrategy را implement کرده باشه براش بفرستیم . خب این چه فایده ای داره ؟ دقیقا اینجا میتونیم هر بار که خواستیم یه روش خوش آمد گویی بهش ارسال کنیم و متد sayWelcome اون رو صدا کنیم .خب حالا باید از کلاس Welcome استفاده کنیم . فرض کنید اینجا کاربر لاگین شده و اجرای کد به این فایل رسیده و ما میخایم حالا بهش خوش آمد بگیم . باید کلاس Welcome رو ازش استفاده کنیم (یک شی بسازیم ازش) و بهش بگیم از کدوم روش استفاده کن . در واقع توی حالت زیر بهش گفتیم با پیامک خوش آمد بگو : &lt;?php
require_once &#039;WelcomeWithSMS.php&#039;;

$welcome = new Welcome(new WelcomeWithSMS());
$user = new stdClass();
$result = $welcome-&gt;say($user);
echo $result;در واقع توی این خط :$welcome = new Welcome(new WelcomeWithSMS());داریم استراتژی خودمون رو معلوم میکنیم که میخایم چطوری خوش آمد بگیم . بهش گفتیم از کلاس WelcomeWithSMS استفاده کن و بعد متد say را صدا بزن . کاربر هم براش ارسال میکنیم.$result = $welcome-&gt;say($user);ولی اگه بخایم با ایمیل خوش آمد بگیم چی ؟‌ فقط باید شی ساختن از کلاس  Welcome رو تغییر بدیم و اینجوری ازش شی بسازیم . در واقع استراتژی را تغیید بدیم .$welcome = new Welcome(new WelcomeWithEmail());به این صورت :&lt;?php
require_once &#039;Welcome.php&#039;;
require_once &#039;WelcomeWithEmail.php&#039;;

$welcome = new Welcome(new WelcomeWithEmail());
$user = new stdClass();
$result = $welcome-&gt;say($user);
echo $result;
خب . به این روش میگن دیزاین پترن استراتژی . خیلی خوب و کاربردی هست . یعنی چی ؟فرض کنید یک ماه دیگه ما تصمیم بگیریم که فقط با ارسال نوتیفیشکن به کاربر خوش آمد بگیم.باید چیکار کنیم؟خیلی ساده یک کلاس جدید ایجاد میکنیم با اسم WelcomeWithNotification و از اینترفیس implement میکنیم . و فقط موقع خوش آمد گویی بعد از لاگین کردن کاربر از اون روش استفاده میکنیم .به این شکل :$welcome = new Welcome(new WelcomeWithNotification());  دوستان امیدوارم مفید واقع شده باشه براتون.اگر سوالی بود در خدمتم .  تا آموزش بعدی بدرود </description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Fri, 11 Sep 2020 23:34:17 +0430</pubDate>
            </item>
                    <item>
                <title>راه اندازی Laravel و Nginx و PhpMyAmdin و MariaDB و Redis بر روی Docker</title>
                <link>https://virgool.io/@sph_1996/%D8%B1%D8%A7%D9%87-%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C-laravel-%D9%88-nginx-%D9%88-phpmyamdin-%D9%88-mariadb-%D9%88-redis-%D8%A8%D8%B1-%D8%B1%D9%88%DB%8C-docker-ru7ips2kzjvy</link>
                <description>سلام . امیدوارم که حالتون خوب باشه . امروز میخواستم تجربه خودم رو در اختیارتون قرار بدم . توی این مطلب قصد دارم بهتون آموزش بدم که چطور میشه یک پروژه لاراول رو با داکر راه اندازی کرد . توی این مطلب قصد ندارم در مورد مبحث داکر توضیح بدم . در واقع شما باید با داکر کار کرده باشید و با دستورات داکر آشنایی داشته باشید و با container ها و image های داکر آشنا باشید .همچنین در مورد داکر فایل و داکر کامپوز هم اطلاعات داشته باشد . پس  راه اندازی لاراول روی داکر موضوعی هست که میخوام در موردش توضیح بدم . خب اول از همه برای راه اندازی یه پروژه با فریمورک لاراول به چند تا تکنولوژی دیگه هم نیاز داریم . به یک دیتابیس نیاز داریم که من MariaDB رو انتخاب میکنم  و همچنین از PhpMyAmdin هم برای مدیریت دیتابیس استفاده میکنیم. به یک وب سرور نیاز داریم که من Nginx رو انتخاب میکنم برای این آموزش . همچنین به Redis نیاز داریم که به عنوان cache driver و همچنین queue driver  توی پروژه لاراولی ازش استفاده کنیم .برای شروع کار باید image  تمام تکنولوژی هایی که میخواهیم رو از داکر هاب دانلود کنیم و از روی هر ایمیج یک کانتینر ایجاد کنیم و بعد کانتینر هارو به وسیله نتورک داکر به هم متصل کنیم . برای Redis و Nginx و PhpMyAdmin و MariaDB ایمیج های رسمی موجود هست . ولی چون خود پروژه لاراولی ما هم باید درون یک کانتینر اجرا شود باید ایمیج مخصوص به خودش را بسازیم تا بتونیم اون ایمیج رو اجرا کنیم . برای ساختن ایمیج دلخواه باید از Dockerfile استفاده کرد .ابتدا پروژه لاراول را در مسیر زیر ایجاد کنید . /var/www/exampleProject/یک فایل با نام Dockerfile در پوشه روت پروژه ایجاد کنید . بوسیله این فایل ما image مخصوص خودمون رو برای پروژه ایجاد میکنیم . خب میدونید که هر  ایمیج داکر از یک ایمیج دیگر ساخته میشود . در واقع هر ایمیج داکر یک base image دارد . ما هم برای ساختن ایمیج خودمان از ایمیج رسمی php:7.4-fpm استفاده می کنیم . محتویات داخل  Dockerfile به صورت زیر است . FROM php:7.4-fpm
# فایل کامپوزر رو توی ورکینگ دایرکتوری قرار میدیم
COPY composer.lock composer.json /var/www/

# توی این قسمت ورکینگ دایرکتوری خودمون رو مشخص میکنیم
WORKDIR /var/www

# هر دیپندنسی که لازم داریم رو نصب میکنیم
RUN apt-get update --fix-missing &amp;&amp; apt-get install -y  \
    build-essential \
    libssl-dev \
    zlib1g-dev \
    libpng-dev \
    libjpeg-dev \
    libfreetype6-dev \
    zlibc \
    mariadb-client \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    locales \
    jpegoptim optipng pngquant gifsicle \
    vim \
    unzip \
    git \
    curl \
    zip

#از اوپکش استفاده میکنیم برای افزایش سرعت اجرای کد های پی اچ پی 
RUN docker-php-ext-install opcache  &amp;&amp; docker-php-ext-enable opcache

# کش رو خالی میکنیم
RUN apt-get clean &amp;&amp; rm -rf /var/lib/apt/lists/*

# اکستنشن های لازم برای لاراول و پی اچ پی رو نصب میکنیم
RUN docker-php-ext-install pdo_mysql exif pcntl
#RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install bcmath

# کامپوزر رو نصب میکنیم
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# پورت 9000 رو اکسپوز میکنیم . در انتها سرور اف پی ام رو اجرا میکنیم
EXPOSE 9000
CMD [&amp;quotphp-fpm&amp;quot]خب به وسیله این داکر فایل ما از بیس ایمیج رسمی php ورژن 7.4 استفاده میکنیم . و تمام مواردی که لازم داریم رو نصب میکنیم . مثل ویرایشگر vim . نکته مهم اینه که پروژه رو در مسیر /var/www/ داخل این ایمیج کپی میکنیم . در واقع ورکینگ دایرکتوری ما داخل کانتینری که از این ایمیج بعدا ساخته میشه  این مسیر میشه .خب کار ما با Dockerfile تمومه . حالا باید از docker compose استفاده کنیم تا کل پروژه و سرویس هارو مدیریت کنیم و کانتینر هارو ران کنیم . فایل docker-compose.yml رو کنار فایل Dockerfile ایجاد کنید و اطلاعات زیر رو داخلش کپی کنید . تک تک این سرویس هارو بعدا توضیح خواهم داد .  version: &#039;3&#039;
services:

  #PHP Service
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: sepisoltani/php:newVersionWithStartShFile
    container_name: app
    restart: unless-stopped
    tty: true
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
      CONTAINER_ROLE: app
    working_dir: /var/www
    volumes:
      - ./:/var/www
      - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
    networks:
      - app-network


    #mariadb Service
  db:
    image: mariadb
    container_name: db
    restart: unless-stopped
    tty: true
    ports:
      - &amp;quot3306:3306&amp;quot
    environment:
      MYSQL_DATABASE: laravel_db
      MYSQL_ROOT_PASSWORD: ِYOUR_MYSQL_ROOT_PASSWORD
      SERVICE_TAGS: dev
      SERVICE_NAME: mariadb
    volumes:
      - /dbdata:/var/lib/mysql/
      - ./mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - app-network

    # Phpmyadmin Service
  phpmyadmin:
    depends_on:
      - db
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    restart: always
    ports:
      - &#039;8082:80&#039;
    environment:
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: YOUR_MYSQL_ROOT_PASSWORD
    networks:
      - app-network

    # Redis Service
  redis:
    image: redis:latest
    container_name: redis
    environment:
      - REDIS_PASSWORD=YOUR_REDIS_PASSWORD
    ports:
      - &amp;quot6379:6379&amp;quot
    restart: unless-stopped
    command: [&amp;quotredis-server&amp;quot, &amp;quot--requirepass&amp;quot, &amp;quotYOUR_REDIS_PASSWORD&amp;quot]
    networks:
      - app-network
      
#Docker Networks
networks:
  app-network:

#Volumes
volumes:
  dbdata:
    driver: localخب هالا تک تک سرویس هارو توضیح میدم . اول بپردازیم به سرویس appapp:
  build:
    context: .
    dockerfile: Dockerfile
  image: sepisoltani/php:latest
  container_name: app
  restart: unless-stopped
  tty: true
  environment:
    SERVICE_NAME: app
    SERVICE_TAGS: dev
    CONTAINER_ROLE: app
  working_dir: /var/www
  volumes:
    - ./:/var/www
    - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
  networks:
    - app-network خب این قسمت از فایل داکر کامپوز داره یک سرویس با نام app یا در واقع یک کانتینر با همین اسم رو از روی ایمیجی که با داکر فایل ساختیم اجرا میکنه و بعد اسم این ایمیج رو  sepisoltani/php:latest قرار میده. شما میتونید هر اسمی خواستید بزارید .  توی قسمت volumes هم کل پروژه لاراول که داخل ایمیج توی مسیر /var/www/ کپی کردیم رو در یک فضا بیرون از کانتینر نگهداری میکنیم . که باید /. باشه . یعنی مسیر فعلی فایل داکر کامپوز توی هاست OS خودمون.برای تنظیمات PHP هم فایل local.ini در مسیر زیر در داخل کانیتنر وجود دارد /usr/local/etc/php/conf.d/ که به مسیر زیر در داخل هاست OS خودمون بایند میکنیم  /var/www/exampleProject/php/local.ini/ پس یک دایرکتوری در داخل پروژه لاراولی میسازیم و فایل local.ini رو ایجاد میکنیم . میتونیم هر تنظیمی خواستیم داخلش قرار بدیم . برای مثال :upload_max_filesize=40M
post_max_size=40M در واقع اینطوری هر موقع کانتینر بمیره اطلاعات از بین نمیرن . یعنی این دو مسیر به هم bind هستند . در قمست networks هم یک شبکه با نام app-network  به این کانتینر اختصاص میدیم . که باید آخر همین فایل هم تعریف شده باشه .قسمت بعدی مربوط به سرویس دیتابیس هست db:
  image: mariadb
  container_name: db
  restart: unless-stopped
  tty: true
  ports:
    - &amp;quot3306:3306&amp;quot
  environment:
    MYSQL_DATABASE: laravel_db
    MYSQL_ROOT_PASSWORD: MYSQL_ROOT_PASSWORD
    SERVICE_TAGS: dev
    SERVICE_NAME: mariadb
  volumes:
    - /dbdata:/var/lib/mysql/
    - ./mysql/my.cnf:/etc/mysql/my.cnf
  networks:
    - app-network
توی این قسمت سرویس دیتابیس رو راه اندازی میکنیم . از ایمیج رسمی mariadb استفاده میکنیم سپس اسم کانتینر رو db قرار میدیم . پورت 3306 دیتابیس رو به 3306 هاست OS خودمون بایند میکنیم سپس در قسمت environment نام دیتابیس و یوزر دیبتابیس رو مشخص میکنیم . در قسمت volumes  باید یک فضا برای اطلاعات درون دیتابیس روی هاست OS خودمون در نظر بگیریم و اطلاعات رو بهش بایند کنیم تا در صورت کشته شدن کانتینر اطلاعات دیتابیس مثل جدول ها و سطر ها از بین نرند . پس مسیر زیر در داخل کانتینر را /var/lib/mysql به مسیر زیر  /dbdata/ در داخل هاست OS بایند میکنیم. سپس فایل تنظیمات دیتابیس را هم به بیرون بایند میکنیم . یعنی فایل تنظیمات در مسیر /etc/mysql/my.cnfرو به مسیر ./mysql/my.cnfبایند میکنیم و میتونیم هر کانفیگی خواستیم درون فایل my.cnf قرار بدیم . مثلا :[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log قسمت بعدی سرویس phpmyadmin هست  . # Phpmyadmin Service
phpmyadmin:
  depends_on:
    - db
  image: phpmyadmin/phpmyadmin
  container_name: phpmyadmin
  restart: always
  ports:
    - &#039;8082:80&#039;
  environment:
    PMA_HOST: db
    MYSQL_ROOT_PASSWORD: MYSQL_ROOT_PASSWORD
  networks:
    - app-network
    توی این قسمت از ایمیج رسمی phpmyadmin استفاده میکنیم . و پورت 80 رو به یه پورت دلخواه مثلا 8082 بایند میکنیم . سپس در قسمت environment هاست دیتابیس رو db قرار میدیم . چون اسم کانتینر دیتابیس رو db گزاشته بودیم . پسورد دیتابیس هم قبلا در سرویس دیتابیس ست کردیم و همون رو قرار میدیم . قسمت بعدی سرویس ردیس هست که توضیح میدم . # Redis Service
redis:
  image: redis:latest
  container_name: redis
  environment:
    - REDIS_PASSWORD=YOUR_PASSWORD
  ports:
    - &amp;quot6379:6379&amp;quot
  restart: unless-stopped
  command: [&amp;quotredis-server&amp;quot, &amp;quot--requirepass&amp;quot, &amp;quotYOUR_PASSWORD&amp;quot]
  networks:
    - app-network 
    توی این سرویس از ایمیج رسمی redis:latest استفاده میکنیم. نام سرویس رو redis قرار میدیم و یه پسورد هم در نظر میگیریم . سپس پورت ردیس رو به بیرون بایند میکنیم . در قسمت command هم مشخص میکنیم که کانتینر با این دستور ران بشه.سرویس آخر که خیلی هم مهم است سرویس وب سرور یا همون Nginx است که باید تعریف بشه که به صورت زیر توضیح میدم .#Nginx Service
webserver:
  image: nginx:alpine
  container_name: webserver
  restart: unless-stopped
  tty: true
  ports:
    - &amp;quot80:80&amp;quot
  volumes:
    - ./:/var/www
    - ./nginx/conf.d/:/etc/nginx/conf.d/
  networks:
    - app-network  از ایمیج رسمی nginx:alpine استفاده میکنیم . اسم سرویس رو webserver قرار میدیم . و پورت 80 رو به 80 هاست خودمون بایند میکنیم . سپس کل پروژه رو به مسیر /var/www/ داخل این کانتینر بایند میکنیم . همچنین فایل کانفیگ nginx رو که درون مسیر زیر در داخل کانتینر هست/etc/nginx/conf.d/رو به مسیری که دوست داریم بایند میکنیم . مثلا به مسیر زیر :./nginx/conf.d/سپس هر کانفیگی برای nginx داشتیم درون مسیر زیر در داخل هاست os خودمون قرار میدیم ../nginx/conf.d/app.confمثلا فایل app.conf شامل کانفیگ های nginx هست . server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    access_log off;
    root /var/www/public;
    client_max_body_size 40m;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}در قسمت زیر مسیر کانتینر app که سرویس fpm داخلش ران شده را مشخص کردیم . البته این سرویس روی پورت 9000 هست که قبلا اکسپوز کردیم .   fastcgi_pass app:9000;در قسمت آخر فایل داکر کامپوز  هم network و volume هارو مشخص میکنیم به این صورت .#Docker Networks
networks:
  app-network:

#Volumes
volumes:
  dbdata:
    driver: localنتورک app-network و volume دیتابیس تعریف شده اند . خب . حالا کامپوز فایل ما آماده شده . میتونیم کل سرویس هارو با دستور زیر اجرا کنیم . docker-compose up -d وقتی کانتینر ها ران شدند میتونیم فایل env پروژه لاراول رو تغییر بدیم . مثلا برای اطلاعات دیتابیس مقادیر زیر رو در نظر میگیریم . DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME=YOUR USER NAME
DB_PASSWORD=YOUR PASSWORDدر واقع DB_HOST رو باید db قرار بدیم چون اسم سرویس دیتابیس رو db در نظر گرفته بودیم . چون تمام سرویس های داکر داخل یک نتورک هستند پس میتونیم تمام سرویس هارو با نام سرویسشون در دسترس داشته باشیم . برای اتصال لاراول به ردیس هم میتونیم این مقادیر رو قرار بدیم .REDIS_HOST=redis
REDIS_PASSWORD=ِYOUR PASSWORD
REDIS_PORT=6379بعد از این که تماس سرویس ها ران شدند میتونیم با رستور زیر لاگ هر کانتینر رو مشاهده کنیم. docker logs CONTAINER_NAME از دستور زیر هم میتونیم برای باز کردن bash هر کانتینر استفاده کنیم . مثلا برای کانتینر app میتونیم اینطوری وارد شیم . docker-compose exec app bashتوجه داشته باشید که تمام دستورات artisan را باید داخل bash کانتینر app اجرا کرد . خب امیدوارم این آموزش واستون مفید واقع شده باشه . ممنون از وقتی که برای این آموزش گذاشتید .</description>
                <category>sepisoltani</category>
                <author>sepisoltani</author>
                <pubDate>Thu, 02 Jan 2020 14:06:49 +0330</pubDate>
            </item>
            </channel>
</rss>