<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Juan Miguel Medina Prieto</title>
    <description>The latest articles on DEV Community by Juan Miguel Medina Prieto (@jmmedina00).</description>
    <link>https://dev.to/jmmedina00</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F297325%2F78c51aa1-1ea9-412b-9308-e94a431e4d6d.jpg</url>
      <title>DEV Community: Juan Miguel Medina Prieto</title>
      <link>https://dev.to/jmmedina00</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jmmedina00"/>
    <language>en</language>
    <item>
      <title>Writing a custom overlay with React</title>
      <dc:creator>Juan Miguel Medina Prieto</dc:creator>
      <pubDate>Tue, 23 Feb 2021 11:38:50 +0000</pubDate>
      <link>https://dev.to/jmmedina00/writing-a-custom-overlay-with-react-5c3k</link>
      <guid>https://dev.to/jmmedina00/writing-a-custom-overlay-with-react-5c3k</guid>
      <description>&lt;p&gt;Whenever I have ideas for personal projects and I don't trash them completely because either they're impossible for me to carry out reasonably well, or they have already been done by everyone and their mother, they tend to be oriented towards content creation or videogame competitions, usually as a result of me finding out a chore that may be automated is being performed manually.&lt;/p&gt;

&lt;p&gt;Obviously, I don't blame anyone for not commissioning a programmer to code a tool or a bot to help them with those, mostly because it's normally on the low end where I find these sorts of hiccups, and considering the little money (if any) that will move around those environments, it's unlikely investing into a fancier workflow will be a wise decision to their eyes. However, even if I know my projects will probably go unnoticed by those people, it's still a good chance for me to try out a somewhat original idea and generate some code that doesn't consist of doing the same thing you've already seen a million times on the web for anyone to take a look at.&lt;/p&gt;

&lt;h1&gt;
  
  
  The idea
&lt;/h1&gt;

&lt;p&gt;That said, I'd like to tell you how I came up with this overlay idea. I was watching one of the streamers I tend to watch on Twitch on a somewhat regular basis, except that day, they were broadcasting a little tournament under a charity event. A link was provided to pledge some money for the cause, and between rounds and matches, the streamer would get to the campaign's website and copy new donations to a textbox in one of their scenes manually.&lt;/p&gt;

&lt;p&gt;That brought up a question to my mind: "isn't there a bot for that?". As I imagined, no one was aware of such a thing in the stream. The service used as the basis for the campaign was &lt;a href="https://tiltify.com/" rel="noopener noreferrer"&gt;Tiltify,&lt;/a&gt; and the first thing I tried upon figuring out the name of the website was searching &lt;code&gt;tiltify api&lt;/code&gt;, and sure enough, &lt;a href="https://tiltify.github.io/api/" rel="noopener noreferrer"&gt;there's an HTTP API for it.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjik9i859tvvxaiuuvth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjik9i859tvvxaiuuvth.png" alt="One of many campaigns held on Tiltify"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having those docs handy, it's clear I can create a relatively simple overlay using the &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; library and the default template it provides with the Node script &lt;code&gt;create-react-app&lt;/code&gt; as a starting point. The purpose of that is to try it out some and practice working with &lt;a href="https://github.com/ReactiveX/rxjs" rel="noopener noreferrer"&gt;RxJS.&lt;/a&gt; I already have some experience with it as a result of having been working with Angular's HTTP client in the development of an application made with Angular.&lt;/p&gt;

&lt;p&gt;Unlike what I first thought, I decided to end up using &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; as well to carry the configuration to the components that need it and not have to either pass the parameters manually or transform them inside the component. Its Angular equivalent is NgRx, although that one works quite a bit differently from Redux, which only makes sense because NgRx is made to suit the way Angular works best. Redux by itself (and a helper package) seems to be better suited to React.&lt;/p&gt;

&lt;h1&gt;
  
  
  The layout
&lt;/h1&gt;

&lt;p&gt;Being a simple overlay that consumes an API, the application has only two views: the &lt;code&gt;Overlay&lt;/code&gt; itself, which will be loaded by the &lt;code&gt;App&lt;/code&gt; if the necessary configuration in the query parameters is present, and a &lt;code&gt;LinkGenerator&lt;/code&gt; that should make it easier for streamers to paste the required information and do further configuration if they want, then get a link copied to the clipboard that will get the display view loaded into their scene.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndh7kzlvlskxurrwz5ym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndh7kzlvlskxurrwz5ym.png" alt="The link generator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Overlay&lt;/code&gt; component's job is to get the data from Tiltify's API and render each item of the list as a &lt;code&gt;Donation&lt;/code&gt; instance, showing its name and amount. They may be displayed in the order the API yields them (sorted from latest to oldest donation) or sorted descendingly by price, showing the greatest amounts donated on top.&lt;/p&gt;

&lt;p&gt;The amount is further delegated into the &lt;code&gt;AmountDisplay&lt;/code&gt;, which makes sure to layout the donated amount according to the configured position for it and desired currency. For example, a price in euros has the euro sign placed to the right (eg. 100€), while a price in dollars is written the opposite way (eg $100). Only adjusting the specific parameters is needed so that the display isn't dependant on CSS styles. This should allow the same styles to be used with different currencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuq100ww930en5wp51gqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuq100ww930en5wp51gqe.png" alt="A diagram of how the components are related to each other"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The limitations
&lt;/h1&gt;

&lt;p&gt;As you might imagine, most of the &lt;em&gt;limitations&lt;/em&gt; came from the Tiltify API itself, simply because it is quite bare at the moment and the documentation is also lacking. The most significant issue for my purposes was the fact that there's no WebSocket option. In short, WebSocket keeps a communication channel open for longer than a regular HTTP request, thus allowing the sender and receiver to keep sending messages to each other. That way, for example, the application would get updates from the API without having to send another request. Since that wasn't an option, the next better possibility was setting up an observable which calls the code responsible for fetching all the donations every minute.&lt;/p&gt;

&lt;p&gt;Also related to the API itself, as of the moment of writing this article, no options to sort the donations seem to be provided, and since that has to be done manually, I think it's best to fetch them all regardless of whether they will be displayed as they are fetched or sorted by amount descendingly. Besides, the tokens I generated to test the overlay with real data were constantly banned in a matter of hours for some reason; I do suspect it's because I accessed multiple campaigns from other people with them. After all, when I try just one campaign with one token, it keeps working all the time.&lt;/p&gt;

&lt;p&gt;As I didn't want to create a sample campaign without a proper way to add test donations that didn't involve spending real money, and because there's no easy way of finding a campaign that is getting a lot of new donations regularly, I decided to add a &lt;em&gt;test mode&lt;/em&gt; that should allow anyone else making custom styles to test how everything should look without continuously fetching data from Tiltify. It can be triggered in dev mode if you add &lt;code&gt;&amp;amp;test=1&lt;/code&gt;, or basically a &lt;code&gt;test&lt;/code&gt; query parameter with any value that is truthy to any URL the &lt;code&gt;LinkGenerator&lt;/code&gt; makes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F85ripjabwx3hqx8ss5mn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F85ripjabwx3hqx8ss5mn.png" alt="How the test mode looks with default styles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My desire to add unit tests to all the code I wrote caused some unexpected changes as well. Particularly in the overlay, which is actually exposed by itself in a module and connected to the store in another. That's because I couldn't fake the timers needed to test the observable created as a result of the mapping from the state to the component. Besides, even if I pulled it off, all the mocking necessary to make the test suite work correctly would have made it excessively complicated. Thus, I tested the overlay without any connection, separated the code responsible for bringing the current list of donations to its own &lt;em&gt;service&lt;/em&gt;, thus testing it by itself, too, and left the connection to the store untested due to the timers' problem I described before.&lt;/p&gt;

&lt;h1&gt;
  
  
  The result
&lt;/h1&gt;

&lt;p&gt;The finished overlay application is available to try out &lt;a href="https://jmmedina00.github.io/tiltify-donors-overlay/" rel="noopener noreferrer"&gt;here.&lt;/a&gt; The source code and documentation about its usage and customization can be found in the repo below.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jmmedina00" rel="noopener noreferrer"&gt;
        jmmedina00
      &lt;/a&gt; / &lt;a href="https://github.com/jmmedina00/tiltify-donors-overlay" rel="noopener noreferrer"&gt;
        tiltify-donors-overlay
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An overlay that shows the latest (or highest) donations for a Tiltify's campaign
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Tiltify Overlay&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Overlay made with React that shows a Tiltify campaign's donations, sorted by newest to oldest or by amount given descendingly. An article about the making of this application is provided &lt;a href="https://dev.to/jmmedina00/writing-a-custom-overlay-with-react-5c3k" rel="nofollow"&gt;here.&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage instructions (as-is)&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get your campaign's id from the campaign's dashboard. This &lt;a href="https://info.tiltify.com/support/solutions/articles/43000011766-the-campaign-dashboard-an-in-depth-explanation" rel="nofollow noopener noreferrer"&gt;article&lt;/a&gt; describes exactly where it is.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to the &lt;a href="https://info.tiltify.com/support/solutions/articles/43000031909-my-account-settings" rel="nofollow noopener noreferrer"&gt;account settings,&lt;/a&gt; then go to &lt;em&gt;Your applications,&lt;/em&gt; create an application, enter its settings and get its access token from the section at the bottom.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter &lt;a href="https://jmmedina00.github.io/tiltify-donors-overlay/" rel="nofollow noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill in the form with the campaign and the token, and select some options as you wish. When it's done, click &lt;em&gt;Generate link.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to OBS and add a browser source, paste the link that was copied to your clipboard and set up the dimensions as you prefer. Make sure to remove the default custom CSS and not mark &lt;em&gt;Shutdown source when not visible&lt;/em&gt; or &lt;em&gt;Refresh browser when scene becomes&lt;/em&gt;…&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jmmedina00/tiltify-donors-overlay" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Feel free to ask me any questions about anything in the code or regarding my &lt;em&gt;design decisions&lt;/em&gt; and I'll try to give the best reply I can provide.&lt;/p&gt;

</description>
      <category>node</category>
      <category>react</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How OAuth works in a nutshell</title>
      <dc:creator>Juan Miguel Medina Prieto</dc:creator>
      <pubDate>Mon, 18 Jan 2021 14:59:24 +0000</pubDate>
      <link>https://dev.to/jmmedina00/how-oauth-works-in-a-nutshell-4cmi</link>
      <guid>https://dev.to/jmmedina00/how-oauth-works-in-a-nutshell-4cmi</guid>
      <description>&lt;p&gt;If you have been using social media or similar apps for any amount of time, you probably have noticed buttons in a lot of those like these by now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zlQEh7UR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8l7hirb62rcb3pqu7xk4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zlQEh7UR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8l7hirb62rcb3pqu7xk4.png" alt='An example of "Continue with" buttons on dev.to' width="620" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maybe you have used any sort of website that does analytics or posts things for you on those social media, either automatically or in some sort of special condition. You might also have seen &lt;a href="https://youtu.be/BxV14h0kFs0"&gt;that strange video&lt;/a&gt; with the exact amount of views in both the title and the view counter every single time you watch it. And all those things have something in common: &lt;strong&gt;they all use OAuth.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What's OAuth?
&lt;/h1&gt;

&lt;p&gt;OAuth is usually referred to as an &lt;em&gt;open standard for access delegation.&lt;/em&gt; That short definition could be described as a &lt;em&gt;protocol&lt;/em&gt;, if you will, that allows a piece of software (such as an application or a console script) to access and/or modify a user's information or resources, thus acting on their behalf if needed, via an external API, usually from a social network, without requiring the user to input their password anywhere other than its official app or website. Besides, that website will usually make sure the user agrees with letting that accessing application use whatever it needs.&lt;/p&gt;

&lt;p&gt;As described above, it's frequently used as an authentication method in websites that are external to those social media, so that the user doesn't have to fill in yet another form when applying for a job or getting started in a  platform, as it simply asks the software that provides OAuth access to some user data to automate that process. Some of them, such as Medium, will go as far as to only provide that option for a user to either login or &lt;em&gt;create an account&lt;/em&gt;, omitting the traditional login form completely.&lt;/p&gt;

&lt;p&gt;However, that's only the tip of the iceberg, since OAuth is simply providing access to an API in the name of a user who explicitly allowed the app that consumes it to do so, and as long as it has the permission, it may not only access data but modify it at will. That may be used by an app to import files from your Drive, schedule posts externally, automatically create them from other sources, or even provide automatic services via a Facebook page or a Twitter account, much like bots in Telegram or Discord do, among other things.&lt;/p&gt;

&lt;h1&gt;
  
  
  How it works
&lt;/h1&gt;

&lt;p&gt;If you're familiar with HTTP APIs, you'll be aware of the fact that as soon as you have the authorization, using any endpoints needed to do what you want is relatively easy: just prepare an HTTP request with the correct method and endpoint, making sure to place the access token where necessary (usually in the &lt;code&gt;Authorization&lt;/code&gt; header), then execute it and analyze the response you get from it.&lt;/p&gt;

&lt;p&gt;But, unlike &lt;em&gt;traditional&lt;/em&gt; services, where you simply request an access token by providing a username or email along with a password, how do you get authorization to use an API via OAuth? Well, being a &lt;em&gt;standard&lt;/em&gt;, any OAuth provider will make your app go through a specific flow to get the token, which can be resumed in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Your app generates a unique link (at least, it should be) to the OAuth provider's login page&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The user goes through it and they login in if they haven't already, then they're usually presented with the list of permissions your app needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the user grants access (or has done it in the past, thus skipping the prompt), the provider redirects the user back to the app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The app gets a single-use access code via that redirection if everything went well, which will be used by the app to ask the API for a proper access token, allowing it to act on the behalf of the user on that service.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The process in detail
&lt;/h1&gt;

&lt;p&gt;The steps above describe how the flow works at a high level, but doesn't cover everything that's required to properly consume an OAuth provider. What's described here obviously applies to most OAuth providers you're going to find and the changes from one provider to the next will usually be very minor, often boiling down to certain parameters' length or whether some parameters are actually required or are just &lt;em&gt;recommended&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup on the provider's side
&lt;/h2&gt;

&lt;p&gt;Before you're able to use any part of an OAuth-authenticated API, you'll need to get a client ID and a secret from the provider. This usually  means registering your application in the developer console or settings for your account, and once you generate those strings, the credentials section will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--djQyzHkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fl1x0ulcpzymeutgkd5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--djQyzHkL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fl1x0ulcpzymeutgkd5w.png" alt="How the credentials page for an OAuth App in GitHub looks" width="880" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may consider those as a username and password of sorts for applications that wish to act on the behalf of users. The client ID will always be used twice: to generate the URL and to request the access token once the temporary code is available reaches your application. The secret, as you may imagine, is used in that last step as a password or key to make sure that the token request is legitimate, and while the client ID may be leaked without much issue, &lt;strong&gt;the secret should be reset as soon as you're aware of its unauthorized use.&lt;/strong&gt; That's the whole reason some developer consoles will force you to create a new one if you lose the previous one, although most of the time, you simply need to click a button if you want to see it again.&lt;/p&gt;

&lt;p&gt;The way you do the registration process varies from one service to the next. Make sure to check out the documentation to learn how to properly do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the link
&lt;/h2&gt;

&lt;p&gt;Once you obtain the client ID and secret, you'll be able to generate a proper link to the authorization endpoint. It will carry the data it needs through the query parameters on the URL, and those parameters are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;client_id&lt;/code&gt;&lt;/strong&gt;: the app's client ID obtained from the developer console&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;redirect_uri&lt;/code&gt;:&lt;/strong&gt; the app's URL the provider should redirect the user back to once the authentication and prompt, if any, are done. It will receive the single-use code necessary for obtaining the access token in most cases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;response_type&lt;/code&gt;:&lt;/strong&gt; if it's required, it should always be set to &lt;code&gt;code&lt;/code&gt;, since that's the flow that's been discussed above and the &lt;code&gt;token&lt;/code&gt; flow, while apparently faster, is much more insecure, to the point &lt;a href="https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1/"&gt;it's going to be removed in OAuth 2.1.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;state&lt;/code&gt;:&lt;/strong&gt; a string of your choice, normally used to mitigate &lt;a href="https://en.wikipedia.org/wiki/Cross-site_request_forgery"&gt;CSRF attacks&lt;/a&gt; or, in other words, making it harder for attackers to tamper with your application via a user's interaction with another website. It'll be sent along with the temporary code back to the app, where it'll be verified. It's usually recommended, but some providers require a state to be present.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;scope&lt;/code&gt;:&lt;/strong&gt; the list of scopes or &lt;em&gt;permissions&lt;/em&gt; your app needs from the user to function, separated however the provider specifies it (some require spaces between scopes, others require colons). &lt;strong&gt;Make sure to only specify the scopes you need&lt;/strong&gt; (for example, don't ask to modify repos if you only need to read user private information), not only because a user should distrust an app that asks a lot of permissions for basic stuff, but if an attacker gets the access token, they'll be able to cause much more damage than with a token with very limited write permissions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the generic parameters you are guaranteed to need when going through an OAuth flow. There are others specific to a particular provider, so check their documentation if you intend to use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requesting the token
&lt;/h2&gt;

&lt;p&gt;Once the user has authorized the application back at the OAuth provider, they will be redirected to it on the specified &lt;code&gt;redirect_uri&lt;/code&gt;. It will include the access &lt;code&gt;code&lt;/code&gt; and the previously specified &lt;code&gt;state&lt;/code&gt; if any. Once the state is verified, the next step is to ask for the access token.&lt;/p&gt;

&lt;p&gt;This is done by sending a POST request to the API's endpoint responsible for this. Both the client ID and the secret are required to validate the request, usually as the body parameters &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;client_secret&lt;/code&gt;, respectively, except for Reddit, which fetches the ID and secret as a username and password in the &lt;a href="https://tools.ietf.org/html/rfc2617#section-2"&gt;&lt;code&gt;Authorization: Basic&lt;/code&gt; style&lt;/a&gt;, the same as the Apache server's password protection works.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;code&lt;/code&gt; is always passed as a body parameter and in most cases, two more parameters are required, which are &lt;code&gt;grant_type&lt;/code&gt; (which is always set to "authorization_code") and &lt;code&gt;redirect_uri&lt;/code&gt;, which should probably be set to the same value the authorization link had in its query.&lt;/p&gt;

&lt;p&gt;The response to this request will contain the &lt;code&gt;access_token&lt;/code&gt; among other information, which might be useful or not depending on the use case. Some providers also allow getting a &lt;code&gt;refresh_token&lt;/code&gt;, either by setting a specific parameter or automatically, which may be used later to get a new access token, usually by doing the same process, only providing the refresh token rather than a temporary code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Some sample code
&lt;/h1&gt;

&lt;p&gt;Even though there are quite a lot of libraries that make this whole process easier, particularly when generating the links, I think it's a good practice to try to understand how things work at a lower level, then switch to higher-level libraries, much in the same way as learning a programming language, then learning a framework is recommended.&lt;/p&gt;

&lt;p&gt;I decided to code a quick sample app in PHP and JavaScript, without using any frameworks, that showcases the most basic case use for OAuth with several providers: getting basic user data to identify them quickly. While it's not proper signup or login, it will hopefully make sense to anyone who takes a look at it.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jmmedina00"&gt;
        jmmedina00
      &lt;/a&gt; / &lt;a href="https://github.com/jmmedina00/sample-oauth-app"&gt;
        sample-oauth-app
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A demo app that can be logged into via other services. The purpose is to show the flow of OAuth in different web services.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Sample OAuth App&lt;/h1&gt;
&lt;p&gt;Web application developed by Juan Miguel Medina Prieto as a proof of concept of using OAuth for authentication purposes. Accesses the APIs of Google, GitHub, GitLab and Dropbox in order to get information from the user that grants permission to their respective OAuth app. Reddit is also showcased, though it can't really be used in the app's flow as it doesn't provide the user's email.&lt;/p&gt;
&lt;h2&gt;
Running the application&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Make sure you have &lt;a href="https://docs.docker.com/get-docker/" rel="nofollow"&gt;Docker&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/install/" rel="nofollow"&gt;Docker Compose&lt;/a&gt; installed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to each service's development console and create an application or project (make sure to choose web app for Reddit and OAuth App for GitHub). Follow the required steps in order to get an OAuth client ID and secret, and only toggle the required scopes for each service:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Google: &lt;code&gt;https://www.googleapis.com/auth/userinfo.email&lt;/code&gt; and &lt;code&gt;https://www.googleapis.com/auth/userinfo.profile&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;GitHub: &lt;code&gt;user:read&lt;/code&gt; and &lt;code&gt;user:email&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;GitLab: &lt;code&gt;read_user&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dropbox: &lt;code&gt;account_info.read&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reddit: &lt;code&gt;identity&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clone this repository, either with &lt;code&gt;git clone&lt;/code&gt; or…&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jmmedina00/sample-oauth-app"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Feel free to ask any questions or suggestions, I'll try my best to give a meaningful reply.&lt;/p&gt;

</description>
      <category>oauth</category>
    </item>
    <item>
      <title>Mapping, reducing and filtering in PHP</title>
      <dc:creator>Juan Miguel Medina Prieto</dc:creator>
      <pubDate>Fri, 03 Jan 2020 19:40:12 +0000</pubDate>
      <link>https://dev.to/jmmedina00/mapping-reducing-and-filtering-in-php-2dc</link>
      <guid>https://dev.to/jmmedina00/mapping-reducing-and-filtering-in-php-2dc</guid>
      <description>&lt;p&gt;There's this thing called "functional programming" that you might have heard about, particularly if you have been learning about JavaScript and let's not mention if you have used languages like Scala or Haskell. It's obviously not as popular as object-oriented programming and using it will likely have its pros and cons, just as any programming paradigm or any programming language. Unfortunately, I can't say I'm experienced enough to compare both of those without ending up essentially copying and pasting someone else's post, and even then it will highly depend on your chosen technology or what you need to achieve.&lt;/p&gt;

&lt;p&gt;What I can say, though, is that, apart from some useful tips you can choose to apply or not depending on what's needed, there are usually some pretty useful functions for handling arrays and avoiding &lt;em&gt;unnecessary&lt;/em&gt; uses of foreach blocks, thus making the code a lot easier to read. Some of those functions are map, reduce and filter, which are available in PHP as "array_map", "array_reduce" and "array_filter".&lt;/p&gt;

&lt;h1&gt;
  
  
  array_map
&lt;/h1&gt;

&lt;p&gt;Given an array, this function will transform its values by using them as a parameter in a given callback function, returning, as a result, a new array with those transformed values just in the same order, and even preserving their keys.&lt;/p&gt;

&lt;p&gt;This function will take two parameters as a minimum:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;callback function&lt;/strong&gt; (or its name, if it's declared somewhere else) that will take as many parameters as many arrays will be used.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;strong&gt;array&lt;/strong&gt;, whose values will be run through as the first parameter of the callback function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More arrays, preferably of the same size as the first one, if the callback function accepts more than one parameter.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's see an example. The following snippet takes these numbers and sums their squares to their triple value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;squareAndTripleValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$tranformedNumbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"squareAndTripleValue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$numbers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;If we didn't use array_map,&lt;/strong&gt; the code would come out like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;squareAndTripleValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$tranformedNumbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$tranformedNumbers&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;squareAndTripleValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This one isn't terribly longer than the first one, but imagine you have to do operations like these over and over again. Moreover, running an array through a function by using foreach might be confusing if the array declaration was removed, as it's not really necessary, but it's considered a best practice because it prevents potential issues, especially if the variable is overwritten. Overall, using array_map when possible makes the code look a little bit cleaner and easier to understand.&lt;/p&gt;

&lt;p&gt;Here's a repl.it for you to further try it out:&lt;/p&gt;


&lt;div class="ltag__replit"&gt;
  &lt;iframe height="550px" src="https://repl.it/@jmmedina00/phpmap?lite=true"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;p&gt;You can check the PHP documentation on array_map &lt;a href="https://www.php.net/manual/en/function.array-map.php"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  array_reduce
&lt;/h1&gt;

&lt;p&gt;This function takes the elements of an array and iterates them through a callback as one of the parameters, where it will be applied to the result of previous elements being applied to an initial value.&lt;/p&gt;

&lt;p&gt;It will take two parameters as a minimum:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;An &lt;strong&gt;array&lt;/strong&gt;, whose values will be passed as a callback's parameter one at a time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A &lt;strong&gt;non-void callback function&lt;/strong&gt; with two parameters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;&lt;em&gt;carry&lt;/em&gt;&lt;/strong&gt; for the initial value or the result from the previous iteration. It should be returned once the second parameter is applied to it.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;current iterated item inside the array&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An initial value, which will be the "carry" value in the first iteration. It's sort of optional since the function will run without it, but you usually shouldn't omit it, as that initial value will be NULL in that case, likely causing problems.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this example, we'll get the sum of the numbers on an array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;doSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$carry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$carry&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"doSum"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And here's the snippet &lt;strong&gt;without using array_reduce, but following its style.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;doSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$carry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$carry&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;doSum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Again, the second one isn't terribly longer than the first one, but it's a little bit cleaner and easier to use, provided that you remember what array_reduce does. Regardless, this an example that would work even if the third parameter was omitted and, in fact, would also work in the foreach version if the $sum declaration was omitted. PHP would complain in that case, though. However, as I said earlier, it's not a great idea to do that on a regular basis: just as an example, doing the product of those numbers would require setting the initial value to 1.&lt;/p&gt;

&lt;p&gt;You can experiment with array_reduce in this repl.it:&lt;/p&gt;


&lt;div class="ltag__replit"&gt;
  &lt;iframe height="550px" src="https://repl.it/@jmmedina00/phpreduce?lite=true"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;p&gt;You can check the PHP documentation on array_reduce &lt;a href="https://www.php.net/manual/en/function.array-reduce.php"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  array_filter
&lt;/h1&gt;

&lt;p&gt;Given an array, this function will return an array with only the values that (or the values whose keys) return TRUE when running through the desired callback if any. Otherwise, a single value won't be part of the resulting array if its key or itself is FALSE when converted to a boolean.&lt;/p&gt;

&lt;p&gt;It will take one parameter as a minimum, up to three:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;array&lt;/strong&gt; to be filtered by the callback function if any.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;callback function&lt;/strong&gt; that will take one parameter or two.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A special flag that will change this function's behavior.&lt;/strong&gt; By default, the array's values will be tested against the callback, but if you set the ARRAY_FILTER_USE_KEY constant as the third parameter, it will test the keys, instead. Using the ARRAY_FILTER_USE_BOTH constant will cause the function to send the key as a second parameter along with the value.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's do three examples, each one for showing what each constant actually does. In the first one, we'll get just the even numbers (divisible by 2) from the original array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$evenNumbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"testNumber"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here's the same snippet &lt;strong&gt;without using array_filter:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;testNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$evenNumbers&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Just with this example, you can notice that, when using foreach, doing the same as array_filter would do requires the use of an if statement, increasing the number of indentations and making the code clearly harder to read than the array_filter version. Furthermore, keep in mind that the behavior of these two snippets is a bit different, as array_filter always keeps the original key, while foreach would require to declare its usage and assigning it directly inside the block.&lt;/p&gt;

&lt;p&gt;In the next snippet, we'll get the numbers with an even key (or index, as PHP assigns incrementing numbers as keys by default to new values). Since the keys start by 0, we're actually obtaining the numbers in an odd position:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nv"&gt;$evenNumbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"testNumber"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;ARRAY_FILTER_USE_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And here's the same code &lt;strong&gt;without using array_filter:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;testNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$evenNumbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And finally, in this snippet, we're going to get only the numbers that are even and have an even key:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;testNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$evenNumbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here's a repl.it you can experiment with:&lt;/p&gt;


&lt;div class="ltag__replit"&gt;
  &lt;iframe height="550px" src="https://repl.it/@jmmedina00/phpfilter?lite=true"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;p&gt;You can check the PHP documentation on array_filter &lt;a href="https://www.php.net/manual/en/function.array-filter"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  A quick little tip
&lt;/h1&gt;

&lt;p&gt;Before ending, have a look at this snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Let's suppose you got this %verb% template from a file, a query or whatever"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$verbs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"nice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"beautiful"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"boring"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"stupid"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$fullTexts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$verb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%verb%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$verb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$template&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;$verbs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/*Yes, you can directly define the function as an argument
instead of referencing its name somewhere else*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have been working on PHP for any amount of time, you'll notice the mapping function wouldn't be able to see the $template, thus making the $fullTexts totally empty. That's because all the variables in a function are treated in the function's scope, and the global values outside cannot be seen. We could consider this issue as the biggest downside of using the functions described above instead of foreach.&lt;/p&gt;

&lt;p&gt;To solve it, you just have to declare the variable with the &lt;em&gt;global&lt;/em&gt; keyword before giving it any use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$fullTexts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$verb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="nv"&gt;$template&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%verb%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$verb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$template&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;$verbs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check more info about the variable scope &lt;a href="https://www.php.net/language.variables.scope"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;These three little functions can be quite useful when working on arrays, as they can help make your code a lot easier to read than if using a foreach block for several small tasks like those. However, it would best to overwrite the array you're working on with each step, as chaining those operations would come out like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$squaresFromEvenNumbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's called &lt;a href="http://callbackhell.com/"&gt;callback hell&lt;/a&gt;, and it will make the code just as hard to read as if you were just using foreach for these tasks. Even though in JavaScript it's somehow difficult to solve, due to its asynchronous nature, this issue only crops up when functions like these are introduced and are easy to solve: just do each step separately, as I said earlier.&lt;/p&gt;

&lt;p&gt;Finally, when should you use each of these functions? Well, as a rule of thumb:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use array_map when you want an array with the same length as the input array and the resulting elements are the result of a transformation of the input elements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use array_filter when you want the same array as the input one, excluding elements that don't fulfill a given condition.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;array_reduce should be used when you want to transform an array into a totally different structure or a single value that couldn't be achieved with any other function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use foreach only when you need to do something else and the operations on each element don't return a result.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to learn more, don't forget to check out &lt;a href="https://www.php.net/manual/en/ref.array.php"&gt;PHP's official documentation on array functions&lt;/a&gt;, including array_map, array_reduce, and array_filter.&lt;/p&gt;

</description>
      <category>php</category>
      <category>functional</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Moving away from Ubuntu: peeking at FreeBSD (Part 3)</title>
      <dc:creator>Juan Miguel Medina Prieto</dc:creator>
      <pubDate>Mon, 30 Dec 2019 16:24:13 +0000</pubDate>
      <link>https://dev.to/jmmedina00/moving-away-from-ubuntu-peeking-at-freebsd-part-3-5ch5</link>
      <guid>https://dev.to/jmmedina00/moving-away-from-ubuntu-peeking-at-freebsd-part-3-5ch5</guid>
      <description>&lt;p&gt;So, in the previous two parts of this &lt;em&gt;post&lt;/em&gt; (if they can be considered so instead of two posts), I was playing around with FreeBSD in a virtual machine in order to determine how different it is from using Ubuntu or some other Debian-based Linux distributions. For the moment, I've been using that as a server (more or less), accessing it via SSH and installing a FAMP, or Apache along with PHP and MySQL under FreeBSD. But I'm not done yet, as I also want to check how hard it will be to install a desktop environment and test it like that.&lt;/p&gt;

&lt;h1&gt;
  
  
  Installing GNOME
&lt;/h1&gt;

&lt;p&gt;So, first of all, I should try using pkg in order to try to find some packages related. I'll try searching popular Linux desktops, such as GNOME, KDE, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ktza4WjT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ekpj3l7s8km0hgtosb4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ktza4WjT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ekpj3l7s8km0hgtosb4x.png" alt='Searching "gnome" with pkg' width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don't know whether I should be surprised by having found actual results related to GNOME, but it's pretty good news, regardless, as this tells us FreeBSD may have really similar desktop experiences to what Linux users have. Now, I'm not sure which is the correct package to install GNOME 3. It seems like "gnome3-3.28_2" is the good one, but I still want to search what's available for KDE.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XYwvwfeN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/n8zik6fkmjnqaxm2ofae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XYwvwfeN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/n8zik6fkmjnqaxm2ofae.png" alt='Searching "kde" with pkg' width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it looks like Plasma is here, too! After looking around in the results, I see that "kde5-5.16.5.19.08.1" looks like the most likely package to install KDE Plasma as a whole. Regardless, I'm going to proceed with GNOME, and maybe I'll try Plasma next, just to see whether the default configuration can handle two desktops installed at the same time or additional steps are required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZO6DG5SX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c4t93f48wsumblumwhy8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZO6DG5SX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c4t93f48wsumblumwhy8.png" alt="pkg install gnome3-3.28_2" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this, ladies and gentlemen, is the fattest part of any desktop Linux installation: the desktop itself. Oh well, I'll go ahead and check the VM's screen while the installation goes on, just to see whether something funny happens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v-Ya8gqk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sbu1b1xld58u4cxrvkyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v-Ya8gqk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sbu1b1xld58u4cxrvkyh.png" alt="GNOME is installed" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Around 10-15 minutes later, the scrolling stops and I get what follows. Well, this is just the end of it, as there's a bunch of messages from the other packages that have been installed along with GNOME. I'll just restart the machine and see if any magic happens, hoping that the installation has taken care of everything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cSg3hycQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jy37qknve6qmr44v8wx6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cSg3hycQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jy37qknve6qmr44v8wx6.png" alt="This is everything the screen shows" width="720" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unlucky me, I guess it doesn't start it right away by default, not even after I log in as any of the users. I guess I'll have to check &lt;a href="https://www.freebsd.org/gnome/"&gt;GNOME's page on FreeBSD's website&lt;/a&gt;. Apparently, the final step that was required for GNOME to load after rebooting is &lt;a href="https://www.freebsd.org/gnome/docs/faq2.html#full-gnome"&gt;right here&lt;/a&gt;, which is just another line in /etc/rc.conf:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sifwd9Be--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hrtopw8camrie2trjxzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sifwd9Be--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hrtopw8camrie2trjxzu.png" alt="Altering rc.conf to make GNOME load on boot" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After doing that, I reboot the machine yet again and... Nothing, I'm still getting just the text mode. I check the manual page again... And I see the "startx" command, which I'm apparently missing since there's no manual page for that, and the command isn't recognized, either. I prefer to check "X11" in pkg, as I think that's the environment related to that command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fi3jw6B---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/8lac3y99wwy4x7ngy5am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fi3jw6B---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/8lac3y99wwy4x7ngy5am.png" alt="pkg search x11" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Interesting, there are some packages for virtual machines. I guess I'll have to try some of those.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bAb2rbNP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/czwz1xmop2gfd84yjpc1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bAb2rbNP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/czwz1xmop2gfd84yjpc1.png" alt="Trying to install one of the packages" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, it doesn't seem to be what I need, so I'll just cancel that and try that test program instead, just to see whether things change in there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yR5-rsME--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ozcb4kxm9iuy8gmf2v18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yR5-rsME--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ozcb4kxm9iuy8gmf2v18.png" alt="The test program" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing, it looks like whatever that I thought I'm missing is already installed, and I can't find something related to X11 in the manual, either, so I try listing the statuses for the services the manual page lists for starting GNOME without requiring a system restart, and this is what I get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZfSp6FmM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vedzd3my94d3qhi76wmw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZfSp6FmM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vedzd3my94d3qhi76wmw.png" alt="GNOME's services" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So "gdm" is not running and "dbus" needs to be added to the rc.conf file. I'm pretty sure that's the piece of the puzzle I'm missing, so I'll add that and try again. One reboot later... Nothing, still out of luck, so I'll try checking GNOME's service itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pxUSuUBD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/45efp0xpvlxz3q3015yw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pxUSuUBD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/45efp0xpvlxz3q3015yw.png" alt="Another failed attempt" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks like GDM is still not running. I try starting it manually, and the only response I get is "Starting gdm". However, while trying to log in to see whether something changes, I see there's "service -e", which will list all services that will start on boot, I try it and...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Kht00Ec--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/z0ntp7nac0x5cs71jmqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Kht00Ec--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/z0ntp7nac0x5cs71jmqg.png" alt="Services started on boot" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sure enough, there's no GNOME to be seen anywhere. I guess I should look for "gnome" in /usr/local/etc/, just to see if there's something that I'm missing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RAm_mcU3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/owyhg81cb9olepf55ucd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RAm_mcU3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/owyhg81cb9olepf55ucd.png" alt="GNOME files in /usr/local/etc" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is, indeed, something, but probably not what I need to start GNOME up. Just as a final attempt, I'll try to install X.Org, since I think that's the server lots of desktops use as their basis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cNQMshv6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/d69tf6u0mmkyhustb9y2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cNQMshv6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/d69tf6u0mmkyhustb9y2.png" alt="Installing X.Org" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It seems that the package was missing, so I'll go ahead and install it. As long as I don't have to deal with the older desktop environments...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PcyKzosi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0y2mqeh89zmlonyq2oh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PcyKzosi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0y2mqeh89zmlonyq2oh3.png" alt="X.Org is installed" width="736" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, that's done, even though that package doesn't seem too happy about having been installed. Oh well, I'll just reboot and see if, this time, the magic does happen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_iq88qpp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ln97df7tlngc9la1xmli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_iq88qpp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ln97df7tlngc9la1xmli.png" alt="GNOME in all its glory" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it does! Finally, I can use a more graphical environment to use this UNIX computer. Luckily, I can log in to my user just as if I were using a Linux machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DJ_pZfC0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rqnipg0iq6aoqrkq90tl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DJ_pZfC0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rqnipg0iq6aoqrkq90tl.png" alt="The volume control" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing I try is playing around with the volume control, and sure enough, I can hear some sound, so probably the web browser can load regular pages as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3GU4b1yI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/yywd1be73hpk9zo8drw3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3GU4b1yI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/yywd1be73hpk9zo8drw3.png" alt="Using the web browser" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, I can enter YouTube and sort of watch videos, even though the pre-installed web browser (which isn't Firefox, by the way) isn't capable of handling it properly. I also notice that the environment itself, just upon opening a few windows, gets very laggy, most likely due to the little amount of VRAM that VirtualBox assigns to a FreeBSD's machine by default. So I increase it to the maximum to see whether that gets better or not and that works... even though the web browser still glitches a lot while trying to play the video, so I guess it's just not working properly.&lt;/p&gt;

&lt;p&gt;Also worthy of mention is the fact that logging in with GNOME won't automatically create the folder system Linux desktops are known for, such as Documents, Images, Desktop, etc. And of course, there's no app store to be found, so this is far from ideal for the average person to use who isn't willing to learn how to use pkg to search and install any software. Oh, and did I mention there's no option to add icons or files to the desktop? That's likely due to the fact the "Desktop" folder is missing, but that's probably annoying as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  Installing KDE
&lt;/h1&gt;

&lt;p&gt;So now that GNOME is more or less &lt;em&gt;usable&lt;/em&gt;, I'll try installing KDE directly from the desktop's terminal, just so that you can see how it looks by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sSSbjvUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e2zlsalwue0qdm93jws6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sSSbjvUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e2zlsalwue0qdm93jws6.png" alt="Installing KDE" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yeah, it's not pretty, but I do guess it could be worse than that. It's pretty much just the same GNOME terminal you'd find in your default Ubuntu installation, even though the color scheme is a lot uglier and simpler. In fact, csh behaves just as badly with that terminal as the one I'm using, &lt;del&gt;which is MATE's default terminal, by the way. It's good enough for me.&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FscrNKs6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ft0ld7x7in5dg5b8w3vo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FscrNKs6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ft0ld7x7in5dg5b8w3vo.png" alt="KDE is installed" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So this one obviously takes longer to install, around 20-25 minutes, but it's already done, so I'll try rebooting in order to see whether something is different or, even better, KDE is also listed when logging in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6O3RMdi_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pbfjmvcz1dpnh2nvyq76.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6O3RMdi_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pbfjmvcz1dpnh2nvyq76.png" alt="Available desktops" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, it looks like this one is going to be a lot simpler than installing GNOME, probably because I already did the hardest work before. So I try entering my password while having selected "Plasma" and...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M2uJwS_h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ytkygtl60yx8lf7e7uwc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M2uJwS_h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ytkygtl60yx8lf7e7uwc.png" alt="Running KDE in FreeBSD" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This looks pretty similar to what I would have found after installing Kubuntu or any KDE-based distro. Oddly enough, it seems like Plasma has created the Desktop folder in order to place icons and whatnot inside. Also, there's also Discover, KDE's app store, but it doesn't seem to work at all. Again, not ideal for someone who isn't very experienced with computers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BzsQ6b4Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/o1hg6twaqswtgqsu8dhm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BzsQ6b4Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/o1hg6twaqswtgqsu8dhm.png" alt="Discover isn't working" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I don't hear any sound when playing around with the volume control, I navigate through KDE's configuration and manage to find an audio test, which does seem to work, even though it sounds a bit choppy at times.&lt;/p&gt;

&lt;p&gt;Once again, I test the pre-installed web browser by making it play a YouTube video and...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZklAphMi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qq77k70ahfrunqm2owuf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZklAphMi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qq77k70ahfrunqm2owuf.png" alt="Konqueror playing a video" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, it seems to work a lot better than GNOME's browser, but this one can't really play the video at full speed, probably because of the fact that either it doesn't have proper drivers to handle the VM's &lt;em&gt;graphics card&lt;/em&gt;, or the VM itself can't really do a lot in terms of performance as KDE isn't likely that well optimized for FreeBSD.&lt;/p&gt;

&lt;p&gt;Overall, by just taking a quick look at both KDE and GNOME, I'd say KDE would manage to give a better experience to the user without having to touch a lot of things, especially when it comes to the configuration panel since KDE itself looks a bit more polished and less bare than GNOME by default. Maybe I'm a bit biased, as I prefer KDE over GNOME, but both are usable while not ideal when compared to Linux desktop distributions or even desktops by themselves.&lt;/p&gt;

&lt;h1&gt;
  
  
  My thoughts on FreeBSD
&lt;/h1&gt;

&lt;p&gt;Well, after spending almost three posts doing some basic things with FreeBSD, I think I can point out some thoughts about actually using it, both from the terminal and from a desktop. I'm aware of the fact I might be wrong since, just in case someone didn't notice, I'm a lot closer to having a first impression about it than having actual experience with it and being able to speak with authority on the subject.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;On the surface, &lt;strong&gt;FreeBSD and Linux are very similar to each other when it comes to the user experience,&lt;/strong&gt; which makes sense, as FreeBSD comes from a derived version of Unix and Linux is just a Unix clone. If you access a service such as a website or an (S)FTP server, it would be really difficult to notice something different while being hosted under any of them unless developer information was visible (such as a phpinfo() call in PHP under Apache) to the user or some hidden files could be noticed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Even though this is likely due to something I haven't configured properly (or maybe my hard drive isn't all that capable of handling it), I've been noticing some &lt;em&gt;delay&lt;/em&gt; between pressing Return after typing a command and seeing the shell react when working on Linux machines, particularly on Ubuntu Server. On FreeBSD, however, those &lt;em&gt;delays&lt;/em&gt; are a lot less likely to happen, at least when it's a fresh installation. I don't think Ubuntu has worse performance than FreeBSD if a fair benchmark was made, but the fact that &lt;strong&gt;the Unix clone has a lot less enabled services by default,&lt;/strong&gt; thus making it faster to run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When it comes to the directory structure and naming conventions (like the one for the HDDs), and especially commands for performing more specific tasks, such as formatting partitions or handling them, &lt;strong&gt;things change quite a bit, so get ready for having to search how to do stuff&lt;/strong&gt; upon seeing a command isn't found or produces a very different output from what you'd expect on Ubuntu or where the files you need are located (if you're looking for files installed by a package, I found that "pkg info -l &amp;lt;packageyouneed&amp;gt;" will do the trick).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The service enabling procedure is particularly irritating, as you usually have to add lines manually to a text file in order for a service to start on boot. Sometimes it goes even further than that, requiring you to add those lines so that the service can run at all. Still, unless FreeBSD is using SystemV or some clone/fork/relative, I might have found out why people are still using "service" on Ubuntu: so that everyone can quickly understand one service is being managed upon seeing a "service" command.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Even though it doesn't always look too polished on the output, "pkg" is probably just as pleasing to use as "apt", but I will say the lookup commands for packages might be quite confusing, as some are just for installed packages. Regardless, the messages some packages spit out to the console upon finishing the installation are quite useful when we're talking about a server or some package that isn't likely to work on the first try.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The documentation provided by the manual is just as useful as what you'd find in any Linux distribution, although I couldn't help looking FreeBSD's online manual, which I'd dare say is a bit better and more comprehensive. Anyway, I should point out that a good chunk of the manual pages, probably a lot more what you'd find on Ubuntu, for example, is aimed at developers, which is probably quite handy for them, but it may scare some users away. Plus, it's a bit irritating to find some C header info when searching how to format a partition.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Speaking of documentation, when you enter a terminal, you are greeted by a default message, then a random tip about moving around the system or performing some task. That's quite useful, to be honest, and has in fact helped me in some of the situations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When talking about using FreeBSD in a desktop computer, I'd say it doesn't work quite as well as when using Linux distributions. GNOME is a good example of that, even though I can't really say for sure since I was just playing around with the two more demanding Linux desktops in a virtual machine. Regardless, I can say the experience wouldn't be quite ideal for the average computer user since there's no option to install software other than using "pkg" in the terminal and it got a bit glitchy upon opening a few windows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And obviously, even though I can't really test it, the compatibility isn't probably all that great for regular computers. The fact that some packages have messages about compatibility issues tells me that using FreeBSD on real hardware, compared to even not that popular Linux distributions can be a bit worse, especially when using less generic hardware. It might be really similar to running Linux on a server, but I can't say for sure. Oh, and the binaries: I doubt Linux binaries will work all that well in FreeBSD, so you should try to compile from source if you can't find the software you need through pkg.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In conclusion, FreeBSD is a quite interesting operating system, even though I wouldn't recommend it in general except for a few rare scenarios. One would be on a desktop, where you could try some of the lighter environments such as MATE or Xfce, in order to check whether they run any better than in Ubuntu or Debian, provided that you're lucky enough to have support for your hardware. I could advise to give it try a lot more confidently on an older server since it is a bit lighter by default than most Linux distributions. Obviously, I wouldn't recommend it by any means to anyone that doesn't have a lot of experience moving around the terminal or even with computers, as things are harder to achieve in FreeBSD than in Linux.&lt;/p&gt;

</description>
      <category>unix</category>
      <category>experimenting</category>
      <category>freebsd</category>
      <category>shell</category>
    </item>
    <item>
      <title>Moving away from Ubuntu: peeking at FreeBSD (Part 2)</title>
      <dc:creator>Juan Miguel Medina Prieto</dc:creator>
      <pubDate>Fri, 27 Dec 2019 15:11:32 +0000</pubDate>
      <link>https://dev.to/jmmedina00/moving-away-from-ubuntu-peeking-at-freebsd-part-2-49j6</link>
      <guid>https://dev.to/jmmedina00/moving-away-from-ubuntu-peeking-at-freebsd-part-2-49j6</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/jmmedina00/moving-away-from-ubuntu-peeking-at-freebsd-part-1-2jne"&gt;Previously,&lt;/a&gt; I had installed FreeBSD in a VirtualBox VM and had started tinkering with it: I simply created another user and set it up so that I could log in as root from it via SSH. Besides, I attempted to set bash as root's shell, even though I failed. While doing so, I had started using pkg, FreeBSD's software management tool, and this time, I'm going to use it even further, as I'll be installing a LAMP stack (that is, Apache or a web server, PHP and MySQL) and then I'll be trying to set a desktop environment up.&lt;/p&gt;




&lt;h1&gt;
  
  
  The disk structure
&lt;/h1&gt;

&lt;p&gt;Before that, though, I want to try something: attaching another virtual HDD to the VM, to check the way FreeBSD lays it out. Let's recall the following image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F761dfse4fc8dvkdg6rfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F761dfse4fc8dvkdg6rfg.png" alt="Disk setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was the result the final partitioning screen showed during the installation, display only one disk with two partitions: the main one, and the swap partition. It's quite a bit different and more complex than what's usual in Linux (the disk itself would have been called "sda" and their partitions, "sda1" and "sda2", respectively). And out of curiosity, I want to explore this naming convention a bit further by experimenting a little bit.&lt;/p&gt;

&lt;p&gt;As such, I add two more disks in the storage section while the VM is turned off, and the setup comes out like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6nrlhz0etvfx7uuri5gs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6nrlhz0etvfx7uuri5gs.png" alt="Three disks in the VM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, I turn it on and log in via SSH. Then I try to run the command "lsblk", but it's not found, so I check the manual page for "fdisk". It says it's obsolete and advises to use "gpart" instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fz8wjkzzeagm0u2hvsqvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fz8wjkzzeagm0u2hvsqvl.png" alt="gpart's manual page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's probably the command I need. I try it and... It seems like it just lists the main disk, probably because the others are blank. And this is when I check the VM's configuration and notice the changes I had made before were missing. I'll have to do them again. Unfortunately, after adding the disks, the command's output was still the same.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw1194esvit5ebeqat8bh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw1194esvit5ebeqat8bh.png" alt="There's only one disk listed here"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still, when checking the contents of /dev, two new devices appear just after the ones representing the main HDD and its partitions. Even if I can't get them to appear on any tools that come to my mind, I still want to try to initialize, so I'll log in as root and try "fdisk /dev/ada1", as it's the only console-based tool I really have experience with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frf2g3gxxz7thu1hz7r3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frf2g3gxxz7thu1hz7r3m.png" alt="FreeBSD's fdisk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seems like I'm out of luck here, too, as this behaves in a different way to Debian's "fdisk". So I check the manual, and I eventually try "gpart create -s mbr /dev/ada1", wanting to initialize the second disk's partition table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwa1qxg7tpgkjw9vrf4tm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwa1qxg7tpgkjw9vrf4tm.png" alt="Trying gpart for the first time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It does seem to have liked that, even though I can't really see any difference when running "gpart list". Still, it does appear if I add the "-a" option, so I think I'm in the right direction. Now, I'll try creating two partitions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0hc9f2hglu3z7yavm8ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0hc9f2hglu3z7yavm8ef.png" alt="Adding partitions to the second disk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It seems to work, but will it have processed the partition sizes as I wanted? I was trying to make the first take 10 gigabytes, and then the rest for the second one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0p2hdj00gefqbmoarngr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0p2hdj00gefqbmoarngr.png" alt="The disk's partitions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, it does! Now I guess I can initialize the third disk without any problems. That one will have a single "linux-data" partition. Now I'm not sure of whether "gpart" just makes the layout or does the full formatting process. Once I'm done, I'll commit the changes for both disks, since this tool seems to work in a similar way to version-control and transaction-based systems... Or maybe I don't need to do so, as I didn't specify any flags at all, and those were just meant to do exactly what I was talking about before. Just to be sure, I reboot the machine, and sure enough, there they are. Whoops!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhxjy7aqukh7sec15ycwc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhxjy7aqukh7sec15ycwc.png" alt="/dev after doing all that stuff with gpart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyway, before moving on, I want to check whether the partitions are formatted or not. &lt;del&gt;I just noticed the naming style is a bit different in the main disk compared to the other, probably because of the swap thing&lt;/del&gt;. So I'll try to mount... "ada1s1", for example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fa6ssqfsh3x2zv5uoxu66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fa6ssqfsh3x2zv5uoxu66.png" alt="mount fails"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It doesn't seem to work, so I'll try to format that partition, then try again. Unluckily, there's no "mkfs" command, so I'll have to look around. &lt;a href="https://www.freebsd.org/doc/handbook/disks-adding.html" rel="noopener noreferrer"&gt;This online manual page&lt;/a&gt; shows the command "newfs", but it doesn't suit what I need here, since that partition is meant to have a FAT32 filesystem. &lt;a href="http://www.codenicer.com/content/formatting-usb-drive-fat32-using-freebsd" rel="noopener noreferrer"&gt;This one&lt;/a&gt;, however, shows the "newfs_msdos" command, which is the precise one I need.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fb04h5ucq9ej9l67d1ebw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fb04h5ucq9ej9l67d1ebw.png" alt="Formatting and trying to mount"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still, I don't get any different results than before. I search online how to mount a FAT32 device, &lt;a href="https://forums.freebsd.org/threads/mount-usb-stick-with-fat32-file-system.56675/" rel="noopener noreferrer"&gt;land here,&lt;/a&gt; and it turns out I wasn't specifying the device type correctly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpdmnlld71umz4lmqfemh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpdmnlld71umz4lmqfemh.png" alt="It finally worked"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it makes a lot more sense to me. And... I have finally determined what "gpart" does: it just makes the partitions, it doesn't format then. Oh well, I'll move on.&lt;/p&gt;

&lt;h1&gt;
  
  
  Installing a web server
&lt;/h1&gt;

&lt;p&gt;After messing with the disk partitioning and formatting tools, I think it's time to try to do one of the main attractions: the LAMP stack. Just as a reminder, the LAMP stack consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Apache, a web server&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PHP, a programming language which is mainly used alongside Apache&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MySQL, a database management system&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Obviously, these three components would normally be running on a Linux machine, but I'm going to try to make them run together under FreeBSD in order to get some experience with pkg and recall how these had to be configured.&lt;/p&gt;

&lt;p&gt;I'll start by Apache. The first thing I should obviously try is searching for it in the repositories. It should be done with "pkg search apache".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnsdx8gyv7hjbrjqyzvyn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnsdx8gyv7hjbrjqyzvyn.png" alt="What results of "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It does seem to return a lot of results, but I quickly find the one that I need: "apache24-2.4.41". I won't be picky about the version number since I just want to see it running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6emxdn8n1niiny9zvk4e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6emxdn8n1niiny9zvk4e.png" alt="Installing Apache"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After waiting for a while, the installation ends and I get this message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fke0lovw78isv7d1eelga.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fke0lovw78isv7d1eelga.png" alt="Post-installation message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once I do the "rc.conf" modification, &lt;del&gt;which is a weird way to add a service to start after booting, especially coming from Ubuntu,&lt;/del&gt; I try the VM's IP address in my browser and... it doesn't work. Probably I need to start it manually. Luckily, the "service" command does drive me somewhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ftf3bww20x3pcykn6dw22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ftf3bww20x3pcykn6dw22.png" alt="The service command"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I suspect FreeBSD is running services by using &lt;em&gt;System V&lt;/em&gt; or something similar... Oh well, at least listing the &lt;em&gt;scripts&lt;/em&gt; shows "apache24", so I start it by typing "service apache24 start", it complains a bit and, once I try it in my web browser...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0g0gjvhfofklxfskvmxb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0g0gjvhfofklxfskvmxb.png" alt="It works!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yeah, that's what I was going to say... Nevermind, I should create a quick PHP script so that the module can be tested as soon as it's installed. But it seems like I have to find where the main folder is, since it's not /var/www/html. I think Apache is the best guy to ask. However, /etc doesn't contain any "apache" folders. After having a little fight with "find", since this command is a lot pickier than in Linux (or than I recalled), I manage to get Apache's folders.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdsdhl5v0ggxl4a6ul632.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdsdhl5v0ggxl4a6ul632.png" alt="Apache's folders"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Checking the server's folder, /usr/local/etc/apache24, I notice its file structure is very different from what I expected, so I'll probably have to look further in the files in order to find where that HTML file lives. I'll start by the main one, httpd.conf, and right there I find it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwv2hoi9amyqh9i7f9z4s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwv2hoi9amyqh9i7f9z4s.png" alt="httpd.conf contents"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's the DocumentRoot, the directive you're looking for when you want to find to figure out where the server's files are located. Since there are no virtual hosts, apparently, there's no room for error, I'll check that one directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvysf02iivrcjyxurguep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvysf02iivrcjyxurguep.png" alt="There's the index"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bingo! Now I'll write a quick script to test PHP. It will be useful once we start messing with it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6y9mraxehcrei88y6but.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6y9mraxehcrei88y6but.png" alt="A quick PHP script"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oddly enough, when I try it without having installed PHP, it shows me the PHP code instead of directly offering it as a download. No one wants either of those scenarios, not even me, so let's tackle that. First, I'll search in the repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnneze6qawy1zu9gsrzfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnneze6qawy1zu9gsrzfe.png" alt="Searching just "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result of running "pkg search php" scrolled on my face for a few seconds, so I should try to reduce the results a little bit. "apache php" and vice-versa don't return any results at all, and it doesn't even understand it if I don't quote it. Oh well, I guess "grep" will come to my rescue once again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbbm8tj8yb3s4s6hszaps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbbm8tj8yb3s4s6hszaps.png" alt="Searching "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don't really like the results I'm seeing, but at least I know PHP 7 is available. Searching "php7" or even "php7." should be helpful, right? Well, just barely, and I still can't see the main package, which is what I probably need, so I'll just run "pkg install php"... which doesn't work. However, specifying a version (7.3) without dots seems to do the trick.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F07eidj8cz07z7kor5qlw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F07eidj8cz07z7kor5qlw.png" alt="Installing PHP 7.3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The installation worked. Now, will it directly link to Apache? Well, nope, it still displays the same result when I try running the script I had made before. It doesn't surprise me at all, even though now I have to figure out how to make it work. No hints from the manual, nor the configuration files, nor the packages info, so I'll have to search online. Not wanting to "cheat", as there are several tutorials on how to make a FAMP (which is the same I'm trying to achieve), I find &lt;a href="https://www.freebsd.org/doc/handbook/network-apache.html" rel="noopener noreferrer"&gt;FreeBSD's manual page on the server&lt;/a&gt;, and after finding "mod_php" while explaining the modules, I have an idea: searching &lt;em&gt;that&lt;/em&gt; in the repositories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fg9j1xae5se9k4w4sz8qv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fg9j1xae5se9k4w4sz8qv.png" alt="pkg search mod_php"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there it is, I think. I haven't figured out a way of looking at a package's info without installing it, so running "pkg install mod_php73-7.3.12" is going to be a shot in the dark. It doesn't need anything else before installing itself, and sure enough, look at this message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnohgq3pv2glfdcoa4uf9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fnohgq3pv2glfdcoa4uf9.png" alt="mod_php's message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After adding those lines to Apache's configuration file, right after the root directory's directives, I restart Apache's service with "service apache24 restart". It seems to complain about the same about not having a domain, and...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsmmnixytoh0r0dubme2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsmmnixytoh0r0dubme2w.png" alt="PHP is working under Apache"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tada! PHP scripts are now working under Apache. MySQL is the only thing that remains to prepare. I'm not sure about installing it, but linking it with PHP should be pretty easy. Anyway, I'll do another search in the repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ftv4y0tpckibmvxr93sfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ftv4y0tpckibmvxr93sfa.png" alt="The result of running "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, we're good for the moment. I'll just pick "mysql57-server-5.7.27" and "php73-pdo_mysql-7.3.12". The last one isn't PHP's traditional MySQL API, but a newer one called PDO, aimed at trying to provide an almost universal interface when dealing with SQL databases. Regardless, this is a good time to test whether I can select several packages to install.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fb145j7f7hnee2p7oh3ok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fb145j7f7hnee2p7oh3ok.png" alt="Selecting several packages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks like I can, so I'll perform the installation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwhmv9vhenvl04au4w089.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwhmv9vhenvl04au4w089.png" alt="MySQL has been installed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Interesting, an automatically generated password. Normally, when working on Ubuntu, MySQL had a root user without a password who could just access it locally. That will be interesting to implement in the MySQL script. Right now, I have to start the service manually, so upon listing the available services, I run "service mysql-server start". Then I have to change something up in the rc.conf, and then I run the command again. And the secret password file appears. I won't be showing it, but I can tell copying and pasting the password works even in the terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F44of4ih8qnplg4g2cqju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F44of4ih8qnplg4g2cqju.png" alt="MySQL wants me to change my password"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yeah, it makes sense. Let's see if I remember how the "ALTER USER" statement worked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgiouz827uf5fvl4vtmmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgiouz827uf5fvl4vtmmm.png" alt="Altering root's password"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I got it right first try. I quickly exit and try to enter again to check it changed correctly, and sure enough, it worked perfectly. In order to make things a little bit easier for me, I create another user for remote access with all privileges. Before trying it out in a graphical program, I test it in a local terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fa58c1b2hdhfatf5fj5g9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fa58c1b2hdhfatf5fj5g9.png" alt="Can't connect, the port isn't even open"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I guess that would have been way too easy. No problem, I think I have to touch some configuration in order for the MySQL to be exposed externally. I'll try checking the main configuration file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3b4tuvo89v4b0e01172y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3b4tuvo89v4b0e01172y.png" alt="MySQL's main config file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It must be that directive. If I change the IP address to the one assigned by my DHCP, I should be able to connect, right? Granted, I'd have to restart the service in the first place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fd20cohv91sgo30y0h391.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fd20cohv91sgo30y0h391.png" alt="Checking the VM's ports and connecting to MySQL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's a lot better, now I can open my GUI client and insert some data. I'll just make a quick table so that the script isn't all that difficult.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9394cyowrvj290tfnlo9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9394cyowrvj290tfnlo9.png" alt="Some test data inserted into a table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here goes the script, &lt;del&gt;which doesn't use HTML5 nor AJAX since this is just a test&lt;/del&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9ljggqy7jmxa8149fbc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9ljggqy7jmxa8149fbc8.png" alt="Another PHP script, this time testing MySQL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's the result. It seems to work perfectly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fiptw277asqgrwieiiili.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fiptw277asqgrwieiiili.png" alt="How it's seen on the browser"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Well, this is getting long enough, so I'll split this further into another part, where I'll finally try to install a desktop and make some conclusions about how easy FreeBSD is to deal with.&lt;/p&gt;

</description>
      <category>unix</category>
      <category>experimenting</category>
      <category>freebsd</category>
      <category>shell</category>
    </item>
    <item>
      <title>Moving away from Ubuntu: peeking at FreeBSD (Part 1)</title>
      <dc:creator>Juan Miguel Medina Prieto</dc:creator>
      <pubDate>Sun, 22 Dec 2019 18:50:05 +0000</pubDate>
      <link>https://dev.to/jmmedina00/moving-away-from-ubuntu-peeking-at-freebsd-part-1-2jne</link>
      <guid>https://dev.to/jmmedina00/moving-away-from-ubuntu-peeking-at-freebsd-part-1-2jne</guid>
      <description>&lt;p&gt;I've been aware of the existence of OSes besides Windows since I was given my small green laptop by my school, as a result of some politicians in my region attempting to get a nice picture taken, and it showed: the computer was a low-end one, even for the day's standards (we're talking about 2010 or so), so there wasn't a lot that you could do on it comfortably. For example, after a few years, I wasn't able to properly browse any web sites, and let's not talk about the fact I couldn't run an IDE on it when I had to bring it to classes in order to code on it: it crashed spectacularly or simply refused to run at all.&lt;/p&gt;

&lt;p&gt;Even though, I'm glad of having had it: &lt;strong&gt;it made me deal with some form of Ubuntu for the first time&lt;/strong&gt;, as my parents wouldn't let me use their &lt;em&gt;better&lt;/em&gt;, but still sorta low-end, Windows machine, &lt;del&gt;they didn't want me to download things on it and eventually make it unable to boot&lt;/del&gt;, so I had to spend time learning how things worked on there. Not that much of a problem for me, but it was a deal-breaker for most of my classmates.&lt;/p&gt;

&lt;p&gt;Regardless, nowadays, I have &lt;em&gt;some&lt;/em&gt; culture about how Linux came to be and their relatives and, as I don't really need Windows, &lt;del&gt;since the games I want to play are available on Linux or are playable in Wine or even a VM&lt;/del&gt;, and I'm pretty much aware of how painful it can be when it's cracked, I just prefer to use Ubuntu in my main computer. Besides, it's very likely I'll end up working with a Linux machine, and that way it won't be that big of a deal for me, especially when it comes to running commands.&lt;/p&gt;

&lt;p&gt;However, to be honest, I have never used any distro other than Debian-based ones (with different desktops) for a reasonable amount of time, and I've always been curious about how different it is to work on other distributions. Sure, a lot of things will be very similar, but I still want to see &lt;strong&gt;how different&lt;/strong&gt; it really is by myself.&lt;/p&gt;

&lt;p&gt;So, I'm going to start by shooting at the dark with the first one: &lt;strong&gt;FreeBSD&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What's FreeBSD?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fp8jyvxodxkows5fgsnp4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fp8jyvxodxkows5fgsnp4.png" alt="FreeBSD logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FreeBSD isn't really a Linux distribution,&lt;/strong&gt; but an OS derived from one of its relatives, BSD. This operating system was created by the University of California, Berkeley from Unix's source code and, soon before its original development ceased, became the basis of several open-source OSes under the BSD license, including FreeBSD, which allowed the code to be used in proprietary projects such as Apple's OSes and PS3 and PS4's OSes.&lt;/p&gt;

&lt;p&gt;Obviously, by itself, it's much less popular and, according to some posts out there, harder to use than Linux, especially in desktop, even though it's somewhat better when it comes to running servers. &lt;strong&gt;There's no way I can reliably measure its performance in a server,&lt;/strong&gt; but I should be able to tell if it's a lot more of a pain to use than Ubuntu Server.&lt;/p&gt;

&lt;h1&gt;
  
  
  My goals
&lt;/h1&gt;

&lt;p&gt;In order to give FreeBSD a first try, I'm going to attempt to do the following tasks after installing it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Figuring out the folder structure (if it's any different from &lt;a href="https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard" rel="noopener noreferrer"&gt;FHS&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Installing an SSH server in order to use it from a terminal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trying to install a web server (Apache is preferred), make it work with PHP and run a script to read some data from a local database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Attempting to install a desktop environment&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The setup
&lt;/h1&gt;

&lt;p&gt;In order to keep it simple, I'm going to create a VM using VirtualBox with 2GB of RAM and a 30GB virtual HDD. I'm choosing the defaults for FreeBSD, so the storage layout comes out like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fybghf8bn29mbbk4tjio9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fybghf8bn29mbbk4tjio9.png" alt="Storage setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the network, I simply change the default network adapter to be attached to a bridged adapter, which will make the VM fully visible for my local network:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw913eoze1i5gmrh244pf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw913eoze1i5gmrh244pf.png" alt="Network setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Installation
&lt;/h1&gt;

&lt;p&gt;I boot up the virtual machine after selecting the FreeBSD installation image for the virtual disc drive. I'm greeted by this screen upon waiting for a countdown to run out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbgjac9smv2zj92q0zrex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbgjac9smv2zj92q0zrex.png" alt="First screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I select the Install option and arrive at the keymap screen. Since I don't have a standard US keyboard, I first try the "Test default keymap"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcrszohsjhhup1eflsuc3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcrszohsjhhup1eflsuc3.png" alt="Keymap testing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nope, it's just a prompt to check you had the right keymap, which is not my case, so I cancel it and select my language's keymap. Apparently, it doesn't want to show some special characters (such as ñ or letters with accents), but the rest of the keys print the correct characters for my keyboard, so I'll move on.&lt;/p&gt;

&lt;p&gt;After the keymap, I'm asked for a hostname (I respond with &lt;em&gt;bsdchamber&lt;/em&gt;), and I'm asked about what to install. Since I'm not sure, I just prefer to leave as is and select OK.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgw7bsz3eon6085om19vd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgw7bsz3eon6085om19vd.png" alt="Software selection?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, I get to the partitioning. Thankfully, I don't really need to do any specific stuff, so I just select the default option and tell it to use the entire disk and create an MBR partition scheme.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpeyf99uk2p8zvi5prjws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpeyf99uk2p8zvi5prjws.png" alt="Partioning"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is how the final partitioning setup looks like (the device names are quite interesting, by the way):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F761dfse4fc8dvkdg6rfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F761dfse4fc8dvkdg6rfg.png" alt="Disk setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once it's finished, it begins copying all the data to the hard-drive:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2s483x6xqnaief0jic7e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2s483x6xqnaief0jic7e.png" alt="Copying data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Suddenly, I'm prompted to enter a root password:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdc67pnem9j6pqim483o8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdc67pnem9j6pqim483o8.png" alt="Root password"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then it asks me to select a network interface and asks me whether I want to configure IPv4, DHCP, and IPv6. I only accept the first two. Then, I'm prompted to the DNS selection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq2l4u0k6xd63ouz3w06y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq2l4u0k6xd63ouz3w06y.png" alt="DNS choosing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is quite surprising to find nowadays, but I still fill them in with the Google Public DNS addresses.&lt;/p&gt;

&lt;p&gt;After that, the timezone selector pops out, and I select my timezone without any problem at all. I also get to this system configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1asibj5w11ei4ssmbvhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1asibj5w11ei4ssmbvhp.png" alt="System"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good, SSH is already included, but I still want the NTP daemon (for syncing up the time), so I select them with the space bar and move on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fulyy50utqhynzh8vuubj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fulyy50utqhynzh8vuubj.png" alt="Hardening"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This screen is quite interesting, and probably a reason why they say FreeBSD has somewhat better security than Linux. Oh well, I'll just select what feels more natural to me, including &lt;em&gt;secure_console&lt;/em&gt;. Once I click OK, it asks me whether I'd like to add new users. I probably should, but since this is just a lab, there's no need to do so, so I pass, and I land on the final screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9comhztlh6odo9ipolvc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9comhztlh6odo9ipolvc.png" alt="Final screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, at least you can change something up if you change your mind, but I'll just exit and finish the installation. It also asks about manual configuration, but I prefer not to do so. After that, I reboot to the new installation, even though I have to remove the virtual disc manually.&lt;/p&gt;

&lt;h1&gt;
  
  
  First boot
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmklmv4k3ys8lecm0n4c6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmklmv4k3ys8lecm0n4c6.png" alt="First login"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I selected &lt;em&gt;secure_console&lt;/em&gt;, I do have to put credentials to enter into a shell, just like I'm used to in Debian-based distributions, and it works pretty much the same. In fact, the command line format seems to be exactly the same, even if it looks quite ugly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8oimwiq1xhsg68l0ck49.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8oimwiq1xhsg68l0ck49.png" alt="The shell"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I try the "ifconfig" command to check the network, and, sure enough, it is identical as in Linux. Next, I try to login via SSH.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fa3j5hvg7fyo85hwn3i5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fa3j5hvg7fyo85hwn3i5f.png" alt="Unable to connect"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Strangely, it refuses to accept my password, but I suspect it's probably due to the fact that I'm trying to login to the root user. I guess I'm creating another user, after all, al least just to get started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9xopo7rcraq2os0zxg3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9xopo7rcraq2os0zxg3a.png" alt="Adding an user"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks like the "adduser" command I used works a bit differently than I expected, but I still managed to create a user for me. I'll try entering with that one instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ft9qcivllrdpkarfw8446.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ft9qcivllrdpkarfw8446.png" alt="Login"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it looks like we have a winner! I shouldn't be surprised, anyway, as root login via SSH isn't allowed when security measures are applied to Linux machines. Luckily, I should be able to enter as root with this brand-new user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fky0krzxaka0i2d91o46y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fky0krzxaka0i2d91o46y.png" alt="Su Sorry"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or maybe not. I'll have to check the manual. "man su" should do it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3zqq6yl92ktg29yu9wh7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3zqq6yl92ktg29yu9wh7.png" alt="su manual page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, this is interesting. It looks like I have to add my own user to the group "wheel" if I want to be able to log in as root. But, first of all, I should figure out how to add a user to a group. I used to do it with "adduser", but it probably won't work here, especially knowing what happened before. The manual itself says that "adduser" is just for creating new users.&lt;/p&gt;

&lt;p&gt;Sadly, after trying to check some manual pages and listing the commands in /usr/bin, I can't find anything at all, so I guess I'll have to edit the /etc/group file manually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flsl7g2y7e7zfh14xrc5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Flsl7g2y7e7zfh14xrc5o.png" alt="No nano available"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh, great, I don't have nano installed. That's... the only text editor I can really use, so I'll have to install it. Luckily, I saw that you could install stuff in the login message by typing "pkg install", so hopefully "pkg install nano" will work?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqaetm7lrvz1hftgx3la0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqaetm7lrvz1hftgx3la0.png" alt="Installing nano"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Phew! It needed to do some more stuff, as it was the first thing I was installing via pkg, but it worked, so now I should be able to edit the file easily.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F08o2dg27c0b76gp5j6nt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F08o2dg27c0b76gp5j6nt.png" alt="Editing /etc/group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, now that thing's done, will it allow me to...?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdroflgejr0a2nirj5qs5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdroflgejr0a2nirj5qs5.png" alt="Using su to log in as root"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It does, luckily. And I notice right away that my user's home path is slightly different than usual. I'll have a look at the root directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fy83zd982u9v9b5bksso4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fy83zd982u9v9b5bksso4.png" alt="The root directory"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a few folders that I don't recognize, but otherwise, it is very similar to what I'm used to seeing when working on Debian. I should also see what shell I'm using, just in case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53mxj7u70vzqvyfmx5s3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53mxj7u70vzqvyfmx5s3.png" alt="FreeBSD's users"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So root is using "csh"... I really thought I was using bash, since it was so similar, but I guess that being able to see &lt;em&gt;hidden&lt;/em&gt; files was a dead giveaway of the fact something was off. In fact, I can't recognize any of the files that are present in root's home. Still, what I feel the most comfortable on is bash, so I'm going to try to install it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvg64j67p977kc0vtwjf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fvg64j67p977kc0vtwjf6.png" alt="There's bash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice! It's included! Once I get it installed, I grab its location by using "which", and change it up in the /etc/passwd file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7kpj4rpaxzluu77r87fg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7kpj4rpaxzluu77r87fg.png" alt="Changing the shell"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I save the file and exit the connection, just to enter again and log in as root. After seeing the shell looks exactly the same, I decide to see all processes running (at least, from my user's perspective).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fj4wuxhafwkfhymgtwgz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fj4wuxhafwkfhymgtwgz6.png" alt="Processes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I imagined, the changes didn't apply, and since I'm not looking at the proper command to do it, I just prefer to reboot the machine so that the changes take effect. A simple "reboot" will do it.&lt;/p&gt;

&lt;p&gt;Unfortunately, that doesn't work, and I'm still stuck with csh. However, I notice there's another file, master.passwd, so I try to modify that one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmlecmjftk2liwgyqiai0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmlecmjftk2liwgyqiai0.png" alt="master.passwd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one seems to have the root password, but it looks exactly the same as the other file otherwise. I save and reconnect to the host, but it doesn't work, either, so I search on the Internet, and I land on &lt;a href="https://www.freebsd.org/doc/en_US.ISO8859-1/articles/linux-users/shells.html" rel="noopener noreferrer"&gt;this page&lt;/a&gt;, which shows the command "chsh" in order to do what I wanted. I try "chsh root", but it takes me to a vi editor. Luckily, I happen to find another manual entry, where it says I can change it by setting the EDITOR variable, which is done with "setenv EDITOR nano", in my case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxcyzqg6hy2mgasv6ktsz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxcyzqg6hy2mgasv6ktsz.png" alt="Changing the shell with nano"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it fails. It simply refuses to accept that shell as valid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw5mch1bui9e87fcsnnzf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw5mch1bui9e87fcsnnzf.png" alt="No bash for root"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Out of curiosity, I tried changing my non-root user's shell. And surprisingly, it did work!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7tbcoy8vehvcex3dspbr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7tbcoy8vehvcex3dspbr.png" alt="Bash is accepted here"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had seen before that the manual pages advised against changing the default shell, but I didn't expect that it wouldn't let me do so. Oh well, I'll have to deal with it.&lt;/p&gt;




&lt;p&gt;This is it for the moment. In the next part, I'll be trying to install the LAMP stack on FreeBSD and then a desktop environment.&lt;/p&gt;

</description>
      <category>unix</category>
      <category>experimenting</category>
      <category>freebsd</category>
      <category>shell</category>
    </item>
  </channel>
</rss>
