<?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>Forem: Verkko­kauppa.com</title>
    <description>The latest articles on Forem by Verkko­kauppa.com (@verkkokauppacom).</description>
    <link>https://forem.com/verkkokauppacom</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%2Forganization%2Fprofile_image%2F829%2F98ac2a19-f9fc-4875-8502-790d2f7fbd1a.png</url>
      <title>Forem: Verkko­kauppa.com</title>
      <link>https://forem.com/verkkokauppacom</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/verkkokauppacom"/>
    <language>en</language>
    <item>
      <title>How to: Kubernetes for Cheap on Google Cloud</title>
      <dc:creator>Niko Kosonen</dc:creator>
      <pubDate>Tue, 03 Mar 2020 07:08:10 +0000</pubDate>
      <link>https://forem.com/verkkokauppacom/how-to-kubernetes-for-cheap-on-google-cloud-1aei</link>
      <guid>https://forem.com/verkkokauppacom/how-to-kubernetes-for-cheap-on-google-cloud-1aei</guid>
      <description>&lt;p&gt;[TL;DR: Run Kubernetes on two micro instances on GKE without external load balancers. Cluster setup from scratch. &lt;a href="https://github.com/nkoson/gke-tutorial"&gt;github.com/nkoson/gke-tutorial&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;My excitement of running &lt;em&gt;kubernetes&lt;/em&gt; on Google Cloud Platform was quickly curbed by the realization that, despite Google's virtual machines starting at affordable price points, their network ingress is another story: Let's say you want to set up a simple cluster for your own personal projects, or a small business. At the time of writing, a couple of micro nodes running in Iowa will set you back $7.77/mo, but the only (officially marketed, AFAIK) method of getting traffic in is by using a load balancer - which start at whopping $18.26 for the first 5 forwarding rules. That is a deal breaker for me, since there are plenty of other cloud providers with better offerings to smaller players. &lt;/p&gt;

&lt;p&gt;That's when I stumbled upon a &lt;a href="https://charlieegan3.com/posts/2018-08-15-cheap-gke-cluster-zero-loadbalancers/"&gt;great article&lt;/a&gt; about running a GKE cluster without load balancers. With this newly incited motivation, I set out to create my GKE cluster - with the requirement of it being as cheap as possible while enjoying a key benefit of the cloud: being free of manual maintenance. &lt;/p&gt;

&lt;p&gt;I have composed this article as a step-by-step tutorial. Based on my own experience in setting up a cluster on a fresh GCP account, I try to cover every topic from configuring the infrastructure to serving HTTP(S) requests from inside the cluster. Please notice, that I did this mainly to educate myself on the subject, so critique and corrections are wholeheartedly welcome.&lt;/p&gt;

&lt;p&gt;We'll be using Terraform.io to manage our cloud infrastructure, so go ahead and register an account, if you haven't already. You'll obviously need access to a Google Cloud Platform account, as well.&lt;/p&gt;

&lt;p&gt;Let’s get going by creating a new project on the GCP console:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Project selector (top bar) -&amp;gt; New Project -&amp;gt; Enter name -&amp;gt; Create&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This will create a nice empty project for us, which differs from the default starter project in that the newly created blank doesn’t come with any predefined API’s or service accounts.&lt;br&gt;
We’ll start digging our rabbit hole by enabling the Compute Engine API, which we need to communicate with GCP using Terraform. We'll also enable the Service Usage API so that Terraform can enable services for us as we go forward.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;APIs &amp;amp; Services -&amp;gt; API Library -&amp;gt; Compute Engine API -&amp;gt; Enable&lt;/p&gt;

&lt;p&gt;APIs &amp;amp; Services -&amp;gt; API Library -&amp;gt; Service Usage API -&amp;gt; Enable&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the APIs have been initialized, we should find that GCP has generated a new service account for us. The aptly named Compute Engine default service account grants us remote access to the resources of our project.&lt;br&gt;
Next, we’ll need to create a key for Terraform to authenticate with GCP:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;IAM &amp;amp; Admin -&amp;gt; Service accounts -&amp;gt; Compute Engine default service account -&amp;gt; Create key -&amp;gt; Create as JSON&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key that we just downloaded can be used in our terraform.io console as an environment variable, or directly from local disk when running Terraform CLI commands. The former requires newlines edited out of the JSON file and the contents added as &lt;code&gt;GOOGLE_CLOUD_KEYFILE_JSON&lt;/code&gt; in our terraform.io workspace:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Workspaces -&amp;gt; (select a workspace) -&amp;gt; Variables -&amp;gt; Environment Variables&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Make sure you set the value as “sensitive / write only”, if you decide to store the key in your terraform.io workspace.&lt;br&gt;
As stated above, it’s also possible to read the key from your local drive by adding the following in the Terraform provider resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"google"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"3.4.0"&lt;/span&gt;
  &lt;span class="nx"&gt;credentials&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;filename&amp;gt;.json"&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;In this tutorial, we’ll be using the latter of the two methods.&lt;/p&gt;

&lt;p&gt;While we’re here, it’s worth noting that the Compute Engine default service account doesn’t have the permissions to create new roles and assign IAM policies in the project. This is something that we will need later as part of our terraforming process, so let’s get it over with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;IAM &amp;amp; admin -&amp;gt; edit Compute Engine default service account (pen icon) -&amp;gt; Add another role -&amp;gt; select "Role Administrator" -&amp;gt; Save&lt;/p&gt;

&lt;p&gt;Add another role -&amp;gt; select "Project IAM Admin" -&amp;gt; Save&lt;/p&gt;

&lt;p&gt;Add another role -&amp;gt; select "Service Account Admin" -&amp;gt; Save&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We’re now ready to initialize Terraform and apply our configuration to the cloud.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will set up your local Terraform workspace and download the Google provider plugin, which is used to configure GCP resources.&lt;/p&gt;

&lt;p&gt;We can proceed to apply the configuration to our GCP project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will feed the configuration to the terraform.io cloud, check its syntax, check the state of our GCP project and, finally, ask for confirmation to apply our changes. Enter ‘yes’ and sit back. This is going to take a while.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;module.cluster.google_project_iam_custom_role.kluster: Creating...
module.cluster.google_service_account.kluster: Creating..
module.cluster.google_compute_network.gke-network: Creating...
module.cluster.google_compute_address.static-ingress: Creating...
module.cluster.google_service_account.kubeip: Creating...
module.cluster.google_container_node_pool.custom_nodepool[&lt;span class="s2"&gt;"ingress-pool"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;: Creating...

module.cluster.google_container_node_pool.custom_nodepool[&lt;span class="s2"&gt;"ingress-pool"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 1m8s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once the dust has settled, it’s time to check the damage. We set out to configure a minimal cloud infrastructure for running a &lt;em&gt;kubernetes&lt;/em&gt; cluster, so let’s see how we’ve managed so far.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Compute Engine -&amp;gt; VM Instances&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This page reveals that we now have two virtual machines running. These machines are part of node pools ingress-pool and web-pool. A node pool is a piece of configuration, which tells Google Container Engine (GKE) how and when to scale the machines in our cluster up or down. You can find the node pool definitions in &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/cluster.tf#L32"&gt;cluster.tf&lt;/a&gt; and &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/node_pool.tf#L1"&gt;node_pool.tf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you squint, you can see that the machines have internal IP addresses assigned to them. These addresses are part of our &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/network.tf#L9"&gt;subnetwork&lt;/a&gt; range. There is a bunch of other address ranges defined in our cluster, which we’ll glimpse over right now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;subnet_cidr_range&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="c1"&gt;# 10.0.0.0 -&amp;gt; 10.0.255.255&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Defined in &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/network.tf#L9"&gt;google_compute_subnetwork&lt;/a&gt;, this is the address range of the subnetwork, in which our GKE cluster will run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;master_ipv4_cidr_block&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.1.0.0/28"&lt;/span&gt;
&lt;span class="c1"&gt;# 10.1.0.0 -&amp;gt; 10.1.0.15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The master node of our &lt;em&gt;kubernetes&lt;/em&gt; cluster will be running under this block, used by &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/main.tf#L36"&gt;google_container_cluster&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;cluster_range_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.2.0.0/16"&lt;/span&gt;
&lt;span class="c1"&gt;# 10.2.0.0 -&amp;gt; 10.2.255.255&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Rest of our &lt;em&gt;kubernetes&lt;/em&gt; nodes will be running under this range, defined as a &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/network.tf#L21"&gt;secondary range&lt;/a&gt; as part of our subnet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;services_range_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.3.0.0/16"&lt;/span&gt;
&lt;span class="c1"&gt;# 10.3.0.0 -&amp;gt; 10.3.255.255&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also a secondary range in our subnet, the service range contains our &lt;em&gt;kubernetes&lt;/em&gt; services, more of which a bit later.&lt;/p&gt;

&lt;p&gt;Understanding the basic building blocks of our network, there are a couple more details that we need to grasp in order for this to make sense as a whole. The nodes in our cluster can communicate with each other on the subnet we just discussed, but what about incoming traffic? After all, we’ll need to not only accept incoming connections, but also download container images from the web. Enter Cloud NAT:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Networking -&amp;gt; Network Services -&amp;gt; Cloud NAT&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Part of our router configuration, Cloud NAT grants our VM instances Internet connectivity without external IP addresses. This allows for a secure way of provisioning our &lt;em&gt;kubernetes&lt;/em&gt; nodes, as we can download container images through NAT without exposing the machines to public Internet.&lt;br&gt;
In our &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/network.tf#L40"&gt;definition&lt;/a&gt;, we set the router to allow automatically allocated addresses and to operate only on our subnetwork, which we set up earlier.&lt;/p&gt;

&lt;p&gt;OK, our NAT gives us outbound connectivity, but we’ll need a inbound address for our cheap-o load balancer / ingress / certificate manager all-in-one contraption, &lt;strong&gt;traefik&lt;/strong&gt;. We’ll talk about the application in a while, but let’s first make sure that our external static IP addresses are in check:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Networking -&amp;gt; VPC network -&amp;gt; External IP addresses&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There should be two addresses on the list; an automatically generated one in use by our NAT, plus another, currently unused address which is named static-ingress. This is crucial for our cluster to accept connections without an external load balancer, since we can route traffic through to our ingress node using a static IP.&lt;br&gt;
We’ll be running an application, kubeip, in our cluster to take care of assigning the static address to our ingress node, which we’ll discuss in a short while.&lt;/p&gt;

&lt;p&gt;This is a good opportunity to take a look at our firewall settings:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Networking -&amp;gt; VPC network -&amp;gt; Firewall rules&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have added a single custom rule, which lets inbound traffic through to our ingress node. Notice, how we specify a target for the rule to match only with instances that carry the ingress-pool tag. After all, we only need HTTP(S) traffic to land on our internal load balancer (&lt;em&gt;traefik&lt;/em&gt;). The custom firewall rule is defined &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/network.tf#L76"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lest we forget, one more thing: We'll be using the CLI tool &lt;strong&gt;gcloud&lt;/strong&gt; to get our &lt;em&gt;kubernetes&lt;/em&gt; credentials up and running in the next step. Of course, &lt;em&gt;gcloud&lt;/em&gt; needs a configuration of its own, as well, so let's get it over with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Answer truthfully to the questions and you shall be rewarded with a good gcloud config.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes
&lt;/h2&gt;

&lt;p&gt;Our cloud infrastructure setup is now done and we're ready to run some applications in the cluster. In this tutorial, we'll be using &lt;strong&gt;kubectl&lt;/strong&gt; to manage our &lt;em&gt;kubernetes&lt;/em&gt; cluster. To access the cluster on GCP, kubectl needs a valid config, which we can quickly fetch by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters get-credentials &amp;lt;cluster&amp;gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Aggressive optimizations
&lt;/h3&gt;

&lt;p&gt;Disclaimer: I don't recommend doing any of the things I've done in this section. Feel free to crank up the &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/cluster.tf#L32"&gt;node pool machine types&lt;/a&gt; to something beefier (such as g1-small) in favor of keeping logging and metrics alive. At the time of writing this tutorial, I had to make some rather aggressive optimizations on the cluster to run everything on two micro instances. We did mention being cheap, didn't we?&lt;/p&gt;

&lt;p&gt;Realizing that it's probably not a good idea to disable logging, we have &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/cluster.tf#L29"&gt;disabled logging&lt;/a&gt; on GCP. Now that we're up to speed, why don't we go ahead and turn off &lt;em&gt;kubernetes&lt;/em&gt; metrics as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl scale &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 deployment/metrics-server-v0.3.1 &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's over 100MB of memory saved on our nodes at the expense of not knowing the total memory and CPU consumption anymore. Sounds like a fair deal to me!&lt;br&gt;
We'll scale kube-dns service deployments down as well, since running multiple DNS services in our tiny cluster seems like an overkill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl scale &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 deployment/kube-dns-autoscaler &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kube-system
kubectl scale &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 deployment/kube-dns &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;kubernetes&lt;/em&gt; default-backend can go too. We'll be using &lt;strong&gt;nginx&lt;/strong&gt; for this purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl scale &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 deployment/l7-default-backend &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;At this point I realized that the instance spun up from &lt;strong&gt;web-pool&lt;/strong&gt; was stuck at "ContainerCreating" with all the &lt;em&gt;kubernetes&lt;/em&gt; deployments I just disabled still running, so I just deleted the instance to give it a fresh start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute instances list
gcloud compute instances delete &amp;lt;name of the web-pool instance&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After a few minutes, GCP had spun up a new instance from the &lt;em&gt;web-pool&lt;/em&gt; instance pool, this time without the metrics server, default backend and with only one DNS service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployments
&lt;/h3&gt;

&lt;p&gt;The cluster we're about to launch has three deployments: &lt;em&gt;nginx&lt;/em&gt; for serving web content, &lt;strong&gt;kubeIP&lt;/strong&gt; for keeping our ingress node responsive and &lt;em&gt;traefik&lt;/em&gt; which serves a dual purpose; routing incoming connections to &lt;em&gt;nginx&lt;/em&gt;, plus handling SSL. We'll discuss each deployment next.&lt;/p&gt;

&lt;h3&gt;
  
  
  nginx-web
&lt;/h3&gt;

&lt;p&gt;Incoming HTTP(S) traffic in our cluster is redirected to the &lt;em&gt;nginx&lt;/em&gt; server, which we use as our web backend. Put simply in &lt;em&gt;kubernetes&lt;/em&gt; terms, we're going to &lt;strong&gt;deploy&lt;/strong&gt; a container image within a &lt;strong&gt;namespace&lt;/strong&gt; and send traffic to it through a &lt;strong&gt;service&lt;/strong&gt;. We'll do namespace first. Navigate to &lt;code&gt;k8s/nginx-web/&lt;/code&gt; and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; namespace.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pretty straightforward so far. The namespace we just created is defined &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/nginx-web/namespace.yaml#L1"&gt;here&lt;/a&gt;. Next up is the deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see from the &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/nginx-web/deployment.yaml#L5"&gt;definition&lt;/a&gt;, we want our deployment to run under the namespace &lt;code&gt;nginx-web&lt;/code&gt;. We need the container to run on a virtual machine that's spun up from the node pool &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/cluster.tf#L46"&gt;web-pool&lt;/a&gt;, hence the &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/cluster.tf#L46"&gt;nodeSelector&lt;/a&gt; parameter. We're doing this because we want to run everything &lt;em&gt;except&lt;/em&gt; the load balancer on a preemptible VM to cut down costs while ensuring maximum uptime.&lt;/p&gt;

&lt;p&gt;Moving on, the &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/nginx-web/deployment.yaml#L23"&gt;container section&lt;/a&gt; defines a Docker image we want to run from our private Google Container Registry (GCR) repository. Below that, we open the ports 80 and 443 for traffic and set up health check (liveness probe) for our container. The cluster will now periodically GET the container at the endpoint &lt;em&gt;/health&lt;/em&gt; and force a restart if it doesn't receive a 200 OK response within the given time. Readiness probe is basically the same, but will tell the cluster when the container is ready to start accepting connections after initialization.&lt;/p&gt;

&lt;p&gt;We won't dive too deep into Docker in this tutorial, but we have included a basic nginx:alpine container with placeholder web content in this tutorial. We'll need to upload the container image to GCR for &lt;em&gt;kubernetes&lt;/em&gt; to use it as per the deployment we just created. Navigate to &lt;code&gt;docker/nginx-alpine&lt;/code&gt; and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; eu.gcr.io/&amp;lt;project&amp;gt;/nginx-web &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This builds the image and tags it appropriately for use in our cluster. We need docker to authenticate with GCP, so let's register gcloud as docker's credential helper by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud auth configure-docker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To push the image into our registry, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;docker push eu.gcr.io/&amp;lt;project&amp;gt;/nginx-web
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can check that everything went fine with the deployment by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get event &lt;span class="nt"&gt;--namespace&lt;/span&gt; nginx-web
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;LAST SEEN  TYPE      REASON              KIND   MESSAGE
1m         Normal    Pulling             Pod    pulling image &lt;span class="s2"&gt;"eu.gcr.io/gke-tutorial-xxxxxx/nginx-web:latest"&lt;/span&gt;
1m         Normal    Pulled              Pod    Successfully pulled image &lt;span class="s2"&gt;"eu.gcr.io/gke-tutorial-xxxxxx/nginx-web:latest"&lt;/span&gt;
1m         Normal    Created             Pod    Created container
1m         Normal    Started             Pod    Started container
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We now have an &lt;em&gt;nginx&lt;/em&gt; container running in the right place, but we still need to route traffic to it within the cluster. This is done by creating a &lt;em&gt;service&lt;/em&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; service.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/nginx-web/service.yaml#L2"&gt;service definition&lt;/a&gt; is minimal: We simply route incoming traffic to applications that match the &lt;em&gt;selector&lt;/em&gt; &lt;code&gt;nginx-web&lt;/code&gt;. In other words, traffic that gets sent to this service on ports 80 and 443 will get directed to pods running our web backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  kubeIP
&lt;/h3&gt;

&lt;p&gt;Working in a cloud environment, we cannot trust that our virtual machines stay up infinitely. In contrary, we actually embrace this by running our web server on a &lt;em&gt;preemptible&lt;/em&gt; node. Preemptible nodes are cheaper to run, as long as we accept the fact that they go down for a period of time at least once a day.&lt;br&gt;
We could easily ensure higher availability in our cluster by simply scaling up the number of nodes, but for the sake of simplicity, we'll stick to one of each type, defined by our node pools &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/cluster.tf#L33"&gt;ingress-pool&lt;/a&gt; and &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/cluster.tf#L46"&gt;web-pool&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A node pool is a set of instructions on how many and what type of instances we should have running in our cluster at any given time. We'll be running &lt;em&gt;traefik&lt;/em&gt; on a node created from &lt;em&gt;ingress-pool&lt;/em&gt; and the rest of our applications run on nodes created from &lt;em&gt;web-pool&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Even though the nodes from &lt;em&gt;ingress-pool&lt;/em&gt; are not preemptible, they might restart some time. Because our cheap-o cluster doesn't use an external load balancer (which is expen\$ive), we need to find another way to make sure that our ingress node always has the same IP for connectivity.&lt;br&gt;
We solve this issue by creating a &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/network.tf#L63"&gt;static IP address&lt;/a&gt; and using &lt;em&gt;kubeip&lt;/em&gt; to bind that address to our ingress node when necessary.&lt;/p&gt;

&lt;p&gt;Let's create the deployment for &lt;em&gt;kubeip&lt;/em&gt; by navigating to &lt;code&gt;k8s/kubeip&lt;/code&gt; and running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We define &lt;code&gt;kube-system&lt;/code&gt; as the &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/kubeip/deployment.yaml#L9"&gt;target namespace&lt;/a&gt; for &lt;em&gt;kubeip&lt;/em&gt;, since we want it to communicate directly with the &lt;em&gt;kubernetes&lt;/em&gt; master and find out when a newly created node needs a static address. Using a &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/kubeip/deployment.yaml#L21"&gt;nodeSelector&lt;/a&gt;, we force &lt;em&gt;kubeip&lt;/em&gt; to deploy on a &lt;em&gt;web-pool&lt;/em&gt; node, just like we did with &lt;em&gt;nginx&lt;/em&gt; earlier.&lt;/p&gt;

&lt;p&gt;Next in the config we define a bunch of environment variables, which we bind to values in a &lt;em&gt;ConfigMap&lt;/em&gt;. We instruct our deployment to fetch GCP service account credentials from a &lt;em&gt;kubernetes&lt;/em&gt; &lt;em&gt;secret&lt;/em&gt;. Through the &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/kubeip/deployment.yaml#L70"&gt;service account&lt;/a&gt;, &lt;em&gt;kubeip&lt;/em&gt; can have the required access rights to make changes (assign IPs) in GCP.&lt;/p&gt;

&lt;p&gt;We created a GCP &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/iam.tf#L42"&gt;service account for kubeip&lt;/a&gt; as part of our Terraform process. Now we just need to extract its credentials just like we did with our main service account in the beginning of this tutorial. For added variety, let's use the command line this time. From the root of our project, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud iam service-accounts list
gcloud iam service-accounts keys create keys/kubeip-key.json &lt;span class="nt"&gt;--iam-account&lt;/span&gt; &amp;lt;kubeip service-account &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now that we have saved the key, we'll store it in the cluster as a &lt;em&gt;kubernetes&lt;/em&gt; secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic kubeip-key &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keys/kubeip-key.json &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We have created a GCP service account for &lt;em&gt;kubeip&lt;/em&gt; and configured &lt;em&gt;kubeip&lt;/em&gt; to access it via the &lt;em&gt;kubernetes&lt;/em&gt; secret. We will still need a &lt;em&gt;kubernetes service account&lt;/em&gt; to access information about the nodes in the cluster. Let's do that now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; serviceaccount.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We define a (&lt;em&gt;kubernetes&lt;/em&gt;) &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/kubeip/serviceaccount.yaml#L3"&gt;ServiceAccount&lt;/a&gt; and below it the &lt;em&gt;ClusterRole&lt;/em&gt; and &lt;em&gt;ClusterRoleBinding&lt;/em&gt; resources, which define what our service account is allowed to do and where.&lt;/p&gt;

&lt;p&gt;Next, we need to create the &lt;em&gt;ConfigMap&lt;/em&gt; for the deployment of &lt;em&gt;kubeip&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; configmap.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/kubeip/configmap.yaml#L2"&gt;config&lt;/a&gt;, we set &lt;em&gt;kubeip&lt;/em&gt; to run in &lt;code&gt;web-pool&lt;/code&gt; and watch instances spun up from &lt;code&gt;ingress-pool&lt;/code&gt;. When &lt;em&gt;kubeip&lt;/em&gt; detects such an instance, it checks if there is an unassigned IP address with the &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/gke/network.tf#L70"&gt;label&lt;/a&gt; &lt;em&gt;kubeip&lt;/em&gt; and value &lt;em&gt;static-ingress&lt;/em&gt; in the reserve and gives that address to the instance. We have restricted the &lt;code&gt;ingress-pool&lt;/code&gt; to a single node, so we only need a single static IP address in our reserve.&lt;/p&gt;

&lt;h3&gt;
  
  
  traefik
&lt;/h3&gt;

&lt;p&gt;External load balancers are very useful in keeping your web service responsive under high load. They are also prohibitively expensive for routing traffic to that single pod in your personal cluster, so we're going to make do without one.&lt;/p&gt;

&lt;p&gt;In our tutorial cluster, we dedicate a single node to hosting &lt;em&gt;traefik&lt;/em&gt;, which we configure to route traffic to our web backend (&lt;em&gt;nginx&lt;/em&gt; server). &lt;em&gt;Traefik&lt;/em&gt; can also fetch SSL certificates from resolvers such as &lt;code&gt;letsencrypt&lt;/code&gt; to protect our HTTPS traffic. We're not going to cover procuring a domain name and setting up DNS in this tutorial, but, for reference, I have left everything that's required for setting up a DNS challenge commented out in the code.&lt;/p&gt;

&lt;p&gt;Let's create a namespace and a service account for &lt;em&gt;traefik&lt;/em&gt;. Navigate to &lt;code&gt;k8s/traefik&lt;/code&gt; and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; namespace.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; serviceaccount.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, we'll create the deployment and take a look at what we've done so far:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using a &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/traefik/deployment.yaml#L23"&gt;nodeSelector&lt;/a&gt; once again, we specify that we want &lt;em&gt;traefik&lt;/em&gt; to run on a machine that belongs to &lt;code&gt;ingress-pool&lt;/code&gt;, which means that in our cluster, &lt;em&gt;traefik&lt;/em&gt; will sit on a different machine than &lt;em&gt;kubeip&lt;/em&gt; and &lt;em&gt;nginx&lt;/em&gt;. The thought behind this is that both of our machines are unlikely to go down simultaneously. When &lt;code&gt;web-pool&lt;/code&gt; goes down and is restarted, no problem; &lt;em&gt;traefik&lt;/em&gt; will find it in the cluster and resume routing connections normally.&lt;br&gt;
If our &lt;code&gt;ingress-pool&lt;/code&gt; went down, the situation would be more severe, since we need our external IP bound to that machine. How else would our clients land on our web backend? Remember we don't have an external load balancer...&lt;/p&gt;

&lt;p&gt;Luckily, we have &lt;em&gt;kubeip&lt;/em&gt; which will detect the recently rebooted &lt;code&gt;ingress-pool&lt;/code&gt; machine and assign our external IP back to it in no time. Crisis averted!&lt;/p&gt;

&lt;p&gt;There's a couple key things in our &lt;em&gt;traefik&lt;/em&gt; deployment that sets it apart from our other deployments. First is &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/traefik/deployment.yaml#L22"&gt;hostNetwork&lt;/a&gt; which we need for &lt;em&gt;traefik&lt;/em&gt; to listen on network interfaces of its host machine.&lt;br&gt;
Secondly, we define a &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/traefik/deployment.yaml#L25"&gt;toleration&lt;/a&gt;, because we have &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/cluster.tf#L63"&gt;tainted&lt;/a&gt; the host node pool. Since our &lt;em&gt;traefik&lt;/em&gt; deployment is the only one with this toleration, we can rest assured that no other application is deployed on &lt;code&gt;ingress-pool&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we give &lt;em&gt;traefik&lt;/em&gt; some &lt;a href="https://github.com/nkoson/gke-tutorial/blob/master/k8s/traefik/deployment.yaml#L40"&gt;arguments&lt;/a&gt; : entry points for HTTP, HTTPS and health check (ping in &lt;em&gt;traefik&lt;/em&gt; lingo). We also enable the &lt;em&gt;kubernetes&lt;/em&gt; provider, which lets us use &lt;em&gt;custom resources&lt;/em&gt;. Let's create them now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; resource.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can add &lt;em&gt;routes&lt;/em&gt; to &lt;em&gt;traefik&lt;/em&gt; using our new custom resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;--save-config&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; route.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The two routes now connect the "web" and "websecure" entrypoints (which we set up as arguments for &lt;em&gt;traefik&lt;/em&gt;) to our &lt;code&gt;nginx-web&lt;/code&gt; service. We should now be able to see HTML content served to us by &lt;em&gt;nginx&lt;/em&gt; when we connect to our static IP address. &lt;/p&gt;

&lt;p&gt;Please enjoy your cluster-on-a-budget responsively!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>Creating an FFI-compatible C-ABI library in Rust</title>
      <dc:creator>Otto Rask</dc:creator>
      <pubDate>Fri, 31 Jan 2020 09:15:19 +0000</pubDate>
      <link>https://forem.com/verkkokauppacom/creating-an-ffi-compatible-c-abi-library-in-rust-5dji</link>
      <guid>https://forem.com/verkkokauppacom/creating-an-ffi-compatible-c-abi-library-in-rust-5dji</guid>
      <description>&lt;p&gt;This is part 2 in our PHP FFI + Rust blog series. Previously we took a look at how the FFI feature can be enabled and used in PHP 7.4, and now we will jump over to Rust and see how we can create C-ABI libraries ourselves, which can then be loaded using PHP FFI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rust?
&lt;/h2&gt;

&lt;p&gt;C would be the obvious first choice when writing C-ABI libraries, yes. But alas, I am more versed in Rust than in C. I know how Rust works in general and what features are available, but am not an expert by any means, while I only know how to compile a C program and that's about it.&lt;/p&gt;

&lt;p&gt;When using Rust there are some extra steps needed when compiling C-ABI compatible libraries, namely making sure our compiled code and logic is aligned and packed according to the C standard and creating a header file which C-ABI consumers can use to locate our code which they want to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;We will be working with stable Rust and a single external crate in this post. I assume you have Rust and Cargo installed and are ready to compile Rust programs from the command line. I am working on Linux Ubuntu, but the steps should be similar for other environments as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example code
&lt;/h2&gt;

&lt;p&gt;All code introduced in the series is available at GitHub in the &lt;a href="https://github.com/rask/php-ffi-examples" rel="noopener noreferrer"&gt;examples repository&lt;/a&gt; I have created. Check the &lt;code&gt;201-rusty-hello-world&lt;/code&gt; directory, which contains a runnable example for the code we're about to write here in this post. You can edit and tinker with the code on your own machine and see what happens.&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/rask" rel="noopener noreferrer"&gt;
        rask
      &lt;/a&gt; / &lt;a href="https://github.com/rask/php-ffi-examples" rel="noopener noreferrer"&gt;
        php-ffi-examples
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Runnable examples to learn how PHP FFI works
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Rust Hello World
&lt;/h2&gt;

&lt;p&gt;First we want to make a regular non-C-ABIfied Rust library just to have a starting point for this post. We will take this starting library and convert it to a C-ABI dynamic library in later steps.&lt;/p&gt;

&lt;p&gt;Let's initialize a new blank library:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cargo init --lib my-library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With the skeleton in place, we can check it works by running &lt;code&gt;cargo test&lt;/code&gt; which should return a passed test for the dummy test we have been scaffolded in &lt;code&gt;src/lib.rs&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we can go in and write a Hello World library for the greater good! Open the project in an editor of your choice, and let's begin by altering the unit test inside &lt;code&gt;lib.rs&lt;/code&gt;. We want to have a function that returns the string &lt;code&gt;Hello world!&lt;/code&gt; to whomever calls it.&lt;/p&gt;

&lt;p&gt;Our test could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_library_function_returns_correct_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_hello_world&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&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;We call a function and expect it to return &lt;code&gt;"Hello world!"&lt;/code&gt;. Simple stuff so far. Let's run the test:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cargo test
error[E0425]: cannot find function `get_hello_world` in this scope
 --&amp;gt; src/lib.rs:5:22
  |
5 |         let result = get_hello_world();
  |                      ^^^^^^^^^^^^^^^ not found in this scope

error: aborting due to previous error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Oh no! Our function is nowhere to be found! Let's fix that. In the &lt;code&gt;lib.rs&lt;/code&gt; file, create a new function to fix our error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// Return a hello world string to the caller.&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;super&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="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_library_function_returns_correct_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_hello_world&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&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;We created a new function, and also added &lt;code&gt;use super::*;&lt;/code&gt; to the &lt;code&gt;tests&lt;/code&gt; module definition to load it into the test scope. Now if we attempt to run tests, we are greeted with a &lt;em&gt;compilation&lt;/em&gt; error instead of a test failure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error[E0308]: mismatched types
  --&amp;gt; src/lib.rs:11:27
   |
11 |         assert!(result == String::from("Hello world!"));
   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found struct `std::string::String`
   |
   = note: expected type `()`
              found type `std::string::String`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now the compiler knew the function was available, but type checking failed as we are not returning a &lt;code&gt;String&lt;/code&gt; from the &lt;code&gt;get_hello_world()&lt;/code&gt; function. Let's fix that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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;Let's test again:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;running 1 test
test tests::test_library_function_returns_correct_string ... FAILED

failures:

---- tests::test_library_function_returns_correct_string stdout ----
thread 'tests::test_library_function_returns_correct_string' panicked at 'assertion failed: result == String::from("Hello world!")', src/lib.rs:13:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


failures:
    tests::test_library_function_returns_correct_string

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We did not get compilation errors, which is great, and now our actual testcase failed. We can see that a string was returned (because it compiled) but the strings do not match. To see what the comparison contains, we can convert to &lt;code&gt;assert_eq!()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_library_function_returns_correct_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_hello_world&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&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 now when we run &lt;code&gt;cargo test&lt;/code&gt; we get a better understanding what we are exactly comparing:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;running 1 test
test tests::test_library_function_returns_correct_string ... FAILED

failures:

---- tests::test_library_function_returns_correct_string stdout ----
thread 'tests::test_library_function_returns_correct_string' panicked at 'assertion failed: `(left == right)`
  left: `""`,
 right: `"Hello world!"`', src/lib.rs:13:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


failures:
    tests::test_library_function_returns_correct_string

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Pay attention to the lines starting with &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;right&lt;/code&gt;. We see the right side is the expected value from our test, and the left side does not match. We now need to fix our code to make them match:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&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;Now our tests should pass gloriously:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;running 1 test
test tests::test_library_function_returns_correct_string ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests example-01-hello-world-library

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Woot!&lt;/p&gt;

&lt;p&gt;We now have a library we could package up and ship to &lt;a href="https://crates.io" rel="noopener noreferrer"&gt;crates.io&lt;/a&gt; for others to use! But let's not do that, as we have other plans. Time to make it C-ABI compatible!&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing C-compatible Rust
&lt;/h2&gt;

&lt;p&gt;Writing C-compatible Rust might sound scary, complicated, and strange, but it really is not too difficult. Rust and C share quite a bit in the ABI-compatibility department, meaning &lt;em&gt;in general&lt;/em&gt; Rust can be compiled as a C library assuming you're not doing anything strange.&lt;/p&gt;

&lt;p&gt;The biggest pain point is converting between C and Rust data types and memory guarantees.&lt;/p&gt;

&lt;p&gt;Assuming our library function in Rust returns a &lt;code&gt;String&lt;/code&gt;, which is an entirely Rust concept, we cannot use that directly in C. What we can do, is juggle with a raw pointer (gasp!) and convert the data held by our &lt;code&gt;String&lt;/code&gt; into a C-friendlier format.&lt;/p&gt;

&lt;p&gt;Let's introduce two new functions which become our public API over C-ABI. Add the following functions and tests for them in the &lt;code&gt;lib.rs&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;c_char&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;ffi&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[no_mangle]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;c_hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;c_char&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rust_string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_hello_world&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Convert the String into a CString&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;c_string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CString&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rust_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not convert to CString"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Instead of returning the CString, we return a pointer for it.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c_string&lt;/span&gt;&lt;span class="nf"&gt;.into_raw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[no_mangle]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;c_hello_world_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;c_char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="nf"&gt;.is_null&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// No data there, already freed probably.&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Here we reclaim ownership of the data the pointer points to, to free the memory properly.&lt;/span&gt;
        &lt;span class="nn"&gt;CString&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&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;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;c_char&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;super&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="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_library_function_returns_correct_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_hello_world&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_library_cabi_function_works&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nb"&gt;c_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;c_hello_world&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cstring&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;cstring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;CString&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;CString&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;cstring&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;The good old &lt;code&gt;get_hello_world()&lt;/code&gt; function has been left as is, but we've introduced a new function that calls it: &lt;code&gt;c_hello_world()&lt;/code&gt;. We also have a secondary function &lt;code&gt;c_hello_world_free(ptr: *mut c_char)&lt;/code&gt;, which is used to free the memory that the first function has reserved. We will take a look at it later.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;c_hello_world()&lt;/code&gt; looks like a regular Rust function, but with some sprinkling on top:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;#[no_mangle]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;extern "C"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;-&amp;gt; *mut c_char&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;no_mangle&lt;/code&gt; attribute instructs the Rust compiler to not alter the function name when it is inserted to a binary file. This makes it easier for FFI users to call it, as the name is kept as "human-readable".&lt;/p&gt;

&lt;p&gt;Writing &lt;code&gt;extern "C"&lt;/code&gt; defines that this function should be callable outside Rust codebases, and the &lt;code&gt;"C"&lt;/code&gt; portion instructs the Rust compiler to optimize the function for usage in C-ABI consumers.&lt;/p&gt;

&lt;p&gt;The return type on the other hand is a raw pointer. A mutable one at that. Scared yet? I know I am.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;cargo test&lt;/code&gt; should see the test passes for this new function as well. In the test function we call the C-compatible function, and assert that it produces a pointer to a string that is stored in C format (&lt;code&gt;CString&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Raw pointers as explained by a Rust newbie
&lt;/h3&gt;

&lt;p&gt;Rust has two of these raw pointer thingies: &lt;code&gt;*const T&lt;/code&gt; and &lt;code&gt;*mut T&lt;/code&gt;, where T is the type of data the pointer points to in memory. The &lt;code&gt;*const&lt;/code&gt; and &lt;code&gt;*mut&lt;/code&gt; parts seem like they are some dereferencing operator thingies, but they are not. They are literal type declarations for raw pointers. I was confused as well.&lt;/p&gt;

&lt;p&gt;It is relatively rare to need to use these types of pointers when you're working on a pure Rust code base. They offer some powers that regular Rust does not provide, and are also required when interfacing with other languages, such as C.&lt;/p&gt;

&lt;p&gt;What raw pointers essentially allow us to do is move stuff between the Rust world and C world using them. Well not move the stuff itself, but more like give an adress from Rust to C and vice versa: "The thing you want is here at this address, but I am not helping you with how to use it or process it". Yay pointers!&lt;/p&gt;

&lt;p&gt;The data raw pointers point at can be anything. It could be &lt;code&gt;*const String&lt;/code&gt;, or maybe &lt;code&gt;*mut MyStruct&lt;/code&gt;. The type declaration just lets Rust know what it might be working with.&lt;/p&gt;

&lt;p&gt;Working with raw pointers is &lt;code&gt;unsafe&lt;/code&gt; in Rust, meaning you need to know what you're doing with them. For simple things you can get away with one or two &lt;code&gt;unsafe&lt;/code&gt; blocks, but for complex stuff, you must pay attention. Otherwise you're corrupting or leaking information in memory.&lt;/p&gt;

&lt;p&gt;Passing the pointers themselves around is not &lt;code&gt;unsafe&lt;/code&gt;, but the second you want to read, modify, or delete the data they point to, you are in &lt;code&gt;unsafe&lt;/code&gt; territory.&lt;/p&gt;

&lt;p&gt;You also need to make sure the data behind raw pointers is freed properly. In Rust terms, when we create a raw pointer, we lose ownership and lifetime guarantees to a degree.&lt;/p&gt;

&lt;p&gt;In our code we use &lt;code&gt;CString::into_raw()&lt;/code&gt; and &lt;code&gt;CString::from_raw()&lt;/code&gt; to momentarily release Rust ownership to C land, and then take it back. The &lt;code&gt;from_raw()&lt;/code&gt; also triggers lifetime checks, and frees the data/memory using Rust rules. If you do not do this, you will most probably leak memory in you programs.&lt;/p&gt;

&lt;p&gt;You should also check if you're working with a null pointer before doing anything with the data the pointer may or may not point to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/std/ptr/index.html" rel="noopener noreferrer"&gt;Read more about pointers at the Rust documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about the &lt;code&gt;*_free&lt;/code&gt; function?
&lt;/h3&gt;

&lt;p&gt;We added two functions for our C-ABI compatible API. One creates a &lt;code&gt;CString&lt;/code&gt;, and the other is supposed to free memory which was created for the &lt;code&gt;CString&lt;/code&gt;. How does this work and why is it done this way?&lt;/p&gt;

&lt;p&gt;In C, you are in charge of memory. This means you are responsible for allocating memory, and freeing memory. Rust itself has guarantees in place that allow you to skip the low-level stuff most of the time, but in C you need to be more careful.&lt;/p&gt;

&lt;p&gt;With our C-ABI we are allocating memory inside Rust, and then passing that memory pointer to someone outside Rust. This creates a situation where we "disable" the Rust borrow checker and lifetime rules for that allocation. Someone outside Rust can erase the memory, alter it, and so on.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;free&lt;/code&gt; function, we provide a tool for the external code to return control of memory to Rust, after which Rust is again in charge with borrow checks and other guarantees.&lt;/p&gt;

&lt;p&gt;In terms of FFI, we first call &lt;code&gt;c_hello_world()&lt;/code&gt; which allocates memory inside Rust and provides us with a pointer to the allocation (&lt;code&gt;T::into_raw()&lt;/code&gt;). After we're done using the memory, we call &lt;code&gt;c_hello_world_free()&lt;/code&gt; with the same pointer which we received earlier, and then inside Rust we "consume" (&lt;code&gt;T::from_raw()&lt;/code&gt;) that pointer to return control back to Rust.&lt;/p&gt;

&lt;p&gt;A similar pattern is already in use inside PHP. &lt;code&gt;fopen()&lt;/code&gt; and &lt;code&gt;fclose()&lt;/code&gt; follows the same semantics for example. You open a resource, do work with it, and then you close it to prevent mistakes from happening, even if that would just mean a memory leak or similar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiling into a C library
&lt;/h2&gt;

&lt;p&gt;When we compile our example library right now, we only get a Rust library out of it (&lt;code&gt;.rlib&lt;/code&gt;), which is quite useless if we want to use the library in a C-ABI FFI setup.&lt;/p&gt;

&lt;p&gt;To alter what is compiled and where, we need to alter our &lt;code&gt;Cargo.toml&lt;/code&gt; configuration.&lt;/p&gt;

&lt;p&gt;If you have not touched it since we initialized the project, it should look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[package]
name = "my-library"
version = "0.1.0"
authors = ["John Doe &amp;lt;johndoe@example.com&amp;gt;"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Quite bare, and a good starting point.&lt;/p&gt;

&lt;p&gt;To change our compilation from a Rust library to a dynamic C library, we need to introduce a &lt;code&gt;[lib]&lt;/code&gt; definition:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

[dependencies]

[lib]
name = "my_library"
crate-type = ["cdylib"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After that is in there, running &lt;code&gt;cargo build&lt;/code&gt; should result in a &lt;code&gt;libmy_library.so&lt;/code&gt; library appearing inside &lt;code&gt;target/debug&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hey, now we could take that file and use it in PHP! Does the following look familiar?&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cdef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'ok this is missing for now'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'libmy_library.so'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neat stuff. The only thing we now need is a header. Let's create a new file into the &lt;code&gt;target/debug&lt;/code&gt; directory (for now, later we will move those things elsewhere) called &lt;code&gt;my_library.h&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#define FFI_LIB "libmy_library.so"

char *const c_hello_world();
void c_hello_world_free(char *const str);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;libmy_library.so&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;my_library.h&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can use those in PHP as follows:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/path/to/library.h'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cdef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;$header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'/path/to/libmy_library.so'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$cstr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ffi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;c_hello_world&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$phpstr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cstr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$ffi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;c_hello_world_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cstr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$phpstr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running that should output &lt;code&gt;Hello world!&lt;/code&gt; in your terminal. How cool! Notice we first called the string (read: pointer) returning function, and after we read the contents to a PHP string, we call the memory freeing function to make sure we are not creating leaks.&lt;/p&gt;

&lt;p&gt;Next up: how to automate writing these C header files, instead of manually tinkering with them every time our C-ABI API changes in Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating C headers generation with &lt;code&gt;cbindgen&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In the Rust ecosystem, there are a multitude of libraries and tools with &lt;code&gt;bindgen&lt;/code&gt; in their names. &lt;code&gt;cbindgen&lt;/code&gt;, &lt;code&gt;wasm-bindgen&lt;/code&gt;, etc. The naming comes from "bindings generator", and they are used to assist in creating cross language and cross binary bindings. With &lt;code&gt;cbindgen&lt;/code&gt; we can create bindings (headers) from Rust in a C-ABI compatible way.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;cbindgen&lt;/code&gt; for really small projects might be overkill, but I would say it is not a big dependency to depend on maintenance-wise.&lt;/p&gt;

&lt;p&gt;To get &lt;code&gt;cbindgen&lt;/code&gt; we need to modify our &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

[dependencies]

[build-dependencies]
cbindgen = "0.9.*"

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We added a new section, called &lt;code&gt;build-dependencies&lt;/code&gt;. This means Cargo will install those to be used in a crate build process, not the crate contents itself. &lt;code&gt;cbindgen&lt;/code&gt; is used during the build process, so we install it this way.&lt;/p&gt;

&lt;p&gt;Now you can run &lt;code&gt;cargo update&lt;/code&gt; and it will fetch and install the dependency.&lt;/p&gt;

&lt;p&gt;Once installed, we need to create a build step. Cargo supports a thing called build scripts, which are bare Rust files that are invoked when building a crate. Create a &lt;code&gt;build.rs&lt;/code&gt; file next to &lt;code&gt;Cargo.toml&lt;/code&gt;, and insert the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;cbindgen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;cbindgen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Language&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;crate_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CARGO_MANIFEST_DIR"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nn"&gt;cbindgen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.with_crate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crate_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_language&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Language&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.generate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unable to generate bindings"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.write_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"target/debug/my_library.h"&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;It is short, and the only thing we do is instantiate a &lt;code&gt;cbindgen&lt;/code&gt; builder instance, alter the configuration a bit, then generate and write the headers to a wanted location on the filesystem.&lt;/p&gt;

&lt;p&gt;To make Cargo use this build file, we need to add it to the &lt;code&gt;Cargo.toml&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[package]
name = "my-library"
version = "0.1.0"
authors = ["John Doe &amp;lt;johndoe@example.com&amp;gt;"]
edition = "2018"
build = "build.rs"

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Notice the &lt;code&gt;build&lt;/code&gt; key containing the &lt;code&gt;build.rs&lt;/code&gt; file path.&lt;/p&gt;

&lt;p&gt;When we now run &lt;code&gt;cargo build&lt;/code&gt;, hopefully we see some header generation magic happen.&lt;/p&gt;

&lt;p&gt;(Note: I had to run &lt;code&gt;build&lt;/code&gt; twice, as somehow the &lt;code&gt;cbindgen&lt;/code&gt; crate had to be compiled in two runs, try it if nothing appears that looks like a header file.)&lt;/p&gt;

&lt;p&gt;Once compilation is done, check the &lt;code&gt;target/debug&lt;/code&gt; directory. Can you see the &lt;code&gt;my_library.h&lt;/code&gt; file? Check the contents. It should show a header similar to the one we manually wrote earlier, but now it has some new stuff. This means the &lt;code&gt;cbindgen&lt;/code&gt; build worked, and now we have automated the generation of header files for our C-ABI library.&lt;/p&gt;

&lt;p&gt;What &lt;code&gt;cbindgen&lt;/code&gt; does, is it parses our source code for definitions that are marked as &lt;code&gt;extern "C"&lt;/code&gt; or similar, and generates the bindings automatically based on those.&lt;/p&gt;

&lt;p&gt;We would like to have &lt;code&gt;cbindgen&lt;/code&gt; automatically insert the &lt;code&gt;FFI_LIB&lt;/code&gt; definition into the header as well, in order to be able to use the &lt;code&gt;\FFI::load()&lt;/code&gt; method instead of the &lt;code&gt;\FFI::cdef()&lt;/code&gt; method in our PHP code. Also there are some unwanted includes in the header which are not needed with PHP FFI as far as I know.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cbindgen&lt;/code&gt; currently has no configurability for creating constants like &lt;code&gt;FFI_LIB&lt;/code&gt;, but we can hack those in using the &lt;code&gt;cbindgen::Builder::with_header()&lt;/code&gt; method. Add the following changes to &lt;code&gt;build.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nn"&gt;cbindgen&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.with_crate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crate_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.with_language&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Language&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.with_no_includes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// First we strip the default include lines which are not needed&lt;/span&gt;
    &lt;span class="nf"&gt;.with_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"#define FFI_LIB &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;libmy_library.so&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// then we insert our custom definitions, the .so path is relative to the header file we're writing&lt;/span&gt;
    &lt;span class="nf"&gt;.generate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unable to generate bindings"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.write_to_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"target/debug/my_library.h"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bit hacky, yes, but works. Now if you run &lt;code&gt;cargo build&lt;/code&gt;, the header should contain a new line which defines the &lt;code&gt;FFI_LIB&lt;/code&gt; constant, and the default includes should be gone. Now we have a header file which is compatible with &lt;code&gt;\FFI::load()&lt;/code&gt; in PHP land.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Okay, so now we know how to write basic C-ABI and FFI-ready libraries in Rust, and also took a super quick peek at how to use those in PHP via FFI. Additionally we looked at how to use &lt;code&gt;cbindgen&lt;/code&gt; to do some tedious work for us automatically.&lt;/p&gt;

&lt;p&gt;Homework: try and see if you can work out how to add a user-supplied parameter to the &lt;code&gt;get_hello_world&lt;/code&gt; and &lt;code&gt;c_hello_world&lt;/code&gt; functions, so they can return &lt;code&gt;Hello World, &amp;lt;param&amp;gt;!&lt;/code&gt; or similar.&lt;/p&gt;

&lt;p&gt;In the upcoming posts in this series we will be writing some more complicated code, meaning we need to learn a bit about the various types that are available in PHP FFI from the C-ABI, and how to write Rust code that operates on those types as well.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>c</category>
      <category>ffi</category>
      <category>php</category>
    </item>
    <item>
      <title>Introduction to PHP FFI</title>
      <dc:creator>Otto Rask</dc:creator>
      <pubDate>Mon, 18 Nov 2019 14:59:11 +0000</pubDate>
      <link>https://forem.com/verkkokauppacom/introduction-to-php-ffi-po3</link>
      <guid>https://forem.com/verkkokauppacom/introduction-to-php-ffi-po3</guid>
      <description>&lt;p&gt;In this series of posts, we will be getting acquainted with a new PHP feature: FFI!&lt;/p&gt;

&lt;p&gt;In addition to getting to know PHP FFI, we will be learning how to create and use FFI libraries written in Rust.&lt;/p&gt;

&lt;p&gt;FFI is a new addition to PHP starting from version 7.4. In a nutshell, it allows you to run external binary code via a wrapper written in PHP. The binary code can be anything which supports the C-ABI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: I am not a C/Rust/ABI/FFI expert, I am writing this series to learn things as a PHP developer as well. If you see errors or other things that should be changed, please do let me know.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  FFI? ABI?
&lt;/h2&gt;

&lt;p&gt;FFI (&lt;a href="https://en.wikipedia.org/wiki/Foreign_function_interface" rel="noopener noreferrer"&gt;&lt;em&gt;Foreign Function Interface&lt;/em&gt;&lt;/a&gt;) is a way for two separate codebases to integrate via an ABI (&lt;a href="https://en.wikipedia.org/wiki/Application_binary_interface" rel="noopener noreferrer"&gt;&lt;em&gt;Application Binary Interface&lt;/em&gt;&lt;/a&gt;). One of the most common ABIs for FFI usage is the C-ABI, which defines the binary calling conventions and data structures for compiled C programs. The C-ABI convention is supported by various other non-C languages a well, such as Rust.&lt;/p&gt;

&lt;p&gt;In essence you can load and call code from a C library inside PHP, or any other language that supports FFI via C-ABI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would I want to use FFI?
&lt;/h2&gt;

&lt;p&gt;FFI is great if you need to run resource intensive code and your "own" programming language is not up to the task (e.g. extensive parsing, number crunching, complex rendering, etc.). Instead of using two entirely different programs to do a single thing in steps, you can "glue" two codebases together inside a single program to do the single thing more efficiently. Or maybe you have a good C-ABI library readily available, and just want to save time by not rewriting it in PHP for instance.&lt;/p&gt;

&lt;p&gt;FFI can provide performance boosts, but sometimes it might actually make your programs slower. We will be looking at some simple benchmarks on what works well and what does not at some point. Tricky business, but hopefully we can find the good spots where FFI will be of help. If not, we will see good and bad use-cases in the future when more PHP folks start using FFI.&lt;/p&gt;

&lt;p&gt;Python has become one of the most popular languages for machine learning and number crunching partly because of FFI. It is relatively simple to load and use powerful C-ABI libraries in Python (via FFI), which in itself is a relatively easy language to learn.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to expect from this blog series
&lt;/h2&gt;

&lt;p&gt;This series will walk us through the following in some way or another:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Introduction FFI in PHP (this post),&lt;/li&gt;
&lt;li&gt;  Creating FFI-libraries for PHP in the Rust programming language,&lt;/li&gt;
&lt;li&gt;  How to use C types in PHP,&lt;/li&gt;
&lt;li&gt;  Benchmarking a few FFI use-cases, to see when FFI is the more performant choice and vice versa,&lt;/li&gt;
&lt;li&gt;  Creating sane and testable FFI abstractions in PHP,&lt;/li&gt;
&lt;li&gt;  How to properly integrate FFI with the PHP preloading feature,&lt;/li&gt;
&lt;li&gt;  Creating distributable PHP Composer packages that make use of FFI,&lt;/li&gt;
&lt;li&gt;  Ensuring safety when writing and using FFI libraries in PHP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I mainly use Linux Ubuntu when programming, so you may have to alter the steps I describe throughout this series to make things work right in other environments. Occasionally I might show a cross-platform way to do things, but do not rely on those working as I have no other systems to test things out on at the moment.&lt;/p&gt;

&lt;p&gt;I am writing this series mainly for two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It gives me a good excuse to learn more about PHP, FFI, Rust, and C,&lt;/li&gt;
&lt;li&gt; It gives me a good excuse to teach somebody something useful, as I like to do when blogging.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So hopefully I will learn new stuff and you will learn at least a thing or two as well in the process.&lt;/p&gt;

&lt;p&gt;Let's go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Example source code
&lt;/h2&gt;

&lt;p&gt;To support the blog series, I have created a code repository that contains example code. The examples are runnable, meaning you can see what happens when you run them and change things.&lt;/p&gt;

&lt;p&gt;The example code is available at &lt;a href="https://github.com/rask/php-ffi-examples" rel="noopener noreferrer"&gt;github.com/rask/php-ffi-examples&lt;/a&gt;. You can download/clone the repository to your machine to run and modify as you wish. If you see errors there, you can create issues or send pull requests.&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/rask" rel="noopener noreferrer"&gt;
        rask
      &lt;/a&gt; / &lt;a href="https://github.com/rask/php-ffi-examples" rel="noopener noreferrer"&gt;
        php-ffi-examples
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Runnable examples to learn how PHP FFI works
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;To run the PHP examples, you need a FFI-compatible PHP interpreter build, which we'll take a look at next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing and configuring FFI for PHP
&lt;/h2&gt;

&lt;p&gt;PHP 7.4 ships with FFI built in. It is a core &lt;em&gt;extension&lt;/em&gt;, meaning it can be disabled. PHP can also be compiled without the FFI extension.&lt;/p&gt;

&lt;p&gt;At the time of writing, PHP 7.4 is still under unstable version development. This means we do not have any official stable builds available for PHP FFI coding.&lt;/p&gt;

&lt;p&gt;If you do not have a FFI-enabled PHP build available, you may have to compile a compatible binary of PHP yourself. NOTE: Some popular package repositories provide unstable PHP 7.4 builds that have FFI enabled.&lt;/p&gt;

&lt;p&gt;There are tons of guides online for compiling PHP from sources in various ways, but if you're somewhat familiar with the process or compiling C programs in general, you can follow the steps outlined below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;build-essential autoconf automake libtool bison re2c
&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/php/php-src
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;php-src
&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout php-7.4.0beta4
&lt;span class="nv"&gt;$ &lt;/span&gt;./buildconf
&lt;span class="nv"&gt;$ &lt;/span&gt;./configure &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/&amp;lt;user&amp;gt;/php/7.4.0beta4 &lt;span class="nt"&gt;--with-ffi&lt;/span&gt; &lt;span class="nt"&gt;--with-zlib&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;make
&lt;span class="nv"&gt;$ &lt;/span&gt;make &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We pass in &lt;code&gt;--with-zlib&lt;/code&gt; to be able to use some compressed data such as compressed PHARs while working. You may need a dependency for it. With &lt;code&gt;--prefix&lt;/code&gt; we set where the installation process puts the compiled binaries and other required data.&lt;/p&gt;

&lt;p&gt;After these steps (if successful) you will have a PHP CLI binary sitting in &lt;code&gt;/home/&amp;lt;user&amp;gt;/php/7.4.0beta4/bin/php&lt;/code&gt;. You can check that FFI is available by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;/home/&amp;lt;user&amp;gt;/php/7.4.0beta4/bin/php &lt;span class="nt"&gt;-m&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;FFI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the FFI module is not loaded, verify your build configuration, build steps, and the generated &lt;code&gt;php.ini&lt;/code&gt; file (found inside &lt;code&gt;/home/&amp;lt;user&amp;gt;/php/7.4.0beta4/lib&lt;/code&gt; or a similar location).&lt;/p&gt;

&lt;p&gt;I recommend adding the built PHP binary to your &lt;code&gt;$PATH&lt;/code&gt;, as we will be using it on the command line quite a bit over the course of this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring PHP FFI
&lt;/h2&gt;

&lt;p&gt;By default FFI is enabled only in the following cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; You're running PHP using the CLI SAPI (i.e. you're running PHP scripts in your terminal or via cron or similar use-cases)&lt;/li&gt;
&lt;li&gt; You're using the new PHP 7.4 preloading feature when running a web application&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to disable FFI, you need to alter your &lt;code&gt;php.ini&lt;/code&gt;. Look for a line similar to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;ffi.enable&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;preload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And set the value from &lt;code&gt;preload&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;. If the line is prefixed with a &lt;code&gt;;&lt;/code&gt; comment marker, remove the marker as well.&lt;/p&gt;

&lt;p&gt;If you want to have FFI enabled &lt;em&gt;at all times&lt;/em&gt; instead, you can set the value to &lt;code&gt;true&lt;/code&gt;. This means FFI is available outside preload and CLI. You might really want to reconsider using this setting in production environments, as it will most probably make your application slower and less safe. For web application development environments it might be a good choice, as you do not need to rely on the preload mechanism when working on your code, and implement the preloading mechanism later on.&lt;/p&gt;

&lt;p&gt;Now that we have PHP with FFI available, we can start coding.&lt;/p&gt;

&lt;h2&gt;
  
  
  FFI Hello World
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Example code provided in &lt;a href="https://github.com/rask/php-ffi-examples/tree/master/101-hello-world" rel="noopener noreferrer"&gt;&lt;code&gt;101-hello-world&lt;/code&gt;&lt;/a&gt; directory of the examples repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What would a tutorial or series be without a &lt;em&gt;Hello World&lt;/em&gt; example? Behold:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cdef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'void printf(char *const str, ...);'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'libc.so.6'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello %s!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run it, it should print &lt;code&gt;Hello world!&lt;/code&gt; into your terminal window, as expected from a hello world program.&lt;/p&gt;

&lt;p&gt;Now let's go through it piece by piece.&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Here we begin creating a new FFI definition.&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// `cdef` is used to first input C headers, then provide the location of a C-ABI&lt;/span&gt;
&lt;span class="c1"&gt;// library that provides the implementation for the header.&lt;/span&gt;
&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cdef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;

    &lt;span class="c1"&gt;// Here we provide the C header. It can be a string, which is loaded&lt;/span&gt;
    &lt;span class="c1"&gt;// from an actual file as well using `file_get_contents()` or similar.&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// In this example we define the `printf` function that accepts a regular&lt;/span&gt;
    &lt;span class="c1"&gt;// string. Note the `...`, which is for C variadics.&lt;/span&gt;
    &lt;span class="s1"&gt;'void printf(char *const str, ...);'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Next we point to the C-ABI library file that hosts the&lt;/span&gt;
    &lt;span class="c1"&gt;// implementation itself.&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// In this example we load the global system `libc.so` library, meaning the C&lt;/span&gt;
    &lt;span class="c1"&gt;// standard library. This may or may not exist depending on your setup,&lt;/span&gt;
    &lt;span class="c1"&gt;// meaning you need to validate which library file to load.&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// The "6" at the end is part of the filename, and carries no special meaning&lt;/span&gt;
    &lt;span class="c1"&gt;// in terms of loading a library.&lt;/span&gt;
    &lt;span class="s1"&gt;'libc.so.6'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Lastly, we make use of the generated FFI definition. We have the&lt;/span&gt;
&lt;span class="c1"&gt;// functions defined in the C header available to be used through&lt;/span&gt;
&lt;span class="c1"&gt;// the FFI object.&lt;/span&gt;
&lt;span class="nv"&gt;$ffi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello %s!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far so good. We now know how to load a C-ABI library and call its functions from PHP. And it is not really that verbose either! If you want a PHP analogy, it looks a little like we're using a factory method to instantiate a class filled with magic methods that have been defined elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternate method for loading
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Example code provided in &lt;a href="https://github.com/rask/php-ffi-examples/tree/master/102-hello-world-with-load" rel="noopener noreferrer"&gt;&lt;code&gt;102-hello-world-with-load&lt;/code&gt;&lt;/a&gt; directory of the examples repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can use the &lt;code&gt;cdef&lt;/code&gt; method for loading a library, but you may also use the &lt;code&gt;load&lt;/code&gt; method, which loads and parses a single C header file and infers the library to load from it.&lt;/p&gt;

&lt;p&gt;Given you have a C header file called &lt;code&gt;header.h&lt;/code&gt; written as such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#define FFI_LIB "libc.so.6"

void printf(char *const str, ...);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;FFI_LIB&lt;/code&gt; definition is used to set the path to a dynamic library that should be loaded for this header file.&lt;/p&gt;

&lt;p&gt;Otherwise it can be a somewhat regular C header file.&lt;/p&gt;

&lt;p&gt;You can replace the &lt;code&gt;cdef&lt;/code&gt; in the hello world example above with:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/header.h'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello %s!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the loading a little simpler, and you get some separation of concerns when it comes to defining what libraries you intend to use, and what functions and other goodies you want to use from those libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scoped method for loading
&lt;/h2&gt;

&lt;p&gt;There is a third method for loading a C library, using a feature called FFI scopes.&lt;/p&gt;

&lt;p&gt;Scopes are available only when you are using the PHP 7.4 preloading functionality. The following example is really simple and unrunnable, and you will need to setup your preloading configuration and scripts properly if you intend to use scopes.&lt;/p&gt;

&lt;p&gt;Given you have a &lt;code&gt;header.h&lt;/code&gt; file as such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#define FFI_SCOPE "helloworld"
#define FFI_LIB "libc.so.6"

void printf(char *const str, ...);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then use something like the following in your preloading script:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/header.h'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;my_printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="nv"&gt;$subs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nv"&gt;$ffi&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="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// This string argument is the same as the&lt;/span&gt;
        &lt;span class="c1"&gt;// `FFI_SCOPE` definition in the `header.h` file&lt;/span&gt;
        &lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'helloworld'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$ffi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="nv"&gt;$subs&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 then in your "nonpreloading" normal code you could call FFI as such:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;my_printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello %s!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is of course a very bare example, and you also need the preloading configuration and boilerplate in place to make use of scopes properly. Scoped loading is preferred when preloading, as it provides some performance improvements over the &lt;code&gt;cdef&lt;/code&gt; and &lt;code&gt;load&lt;/code&gt; methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  FFI errors
&lt;/h2&gt;

&lt;p&gt;The FFI feature throws errors in a few situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Invalid library header or binary definition for &lt;code&gt;cdef&lt;/code&gt; and friends,&lt;/li&gt;
&lt;li&gt;  Runtime errors when accessing FFI state or resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if your C header is malformed, you will be greeted with an error:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cdef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'hello this is invalid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'libc.so.6'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will throw an &lt;code&gt;\FFI\ParserException&lt;/code&gt; which lets you know that your header is not valid.&lt;/p&gt;

&lt;p&gt;When accessing resources that do not exist (e.g. are not available in the loaded C-ABI library), you might receive something like follows:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cdef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'void idonotexist();'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'libc.so.6'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// PHP Fatal error:  Uncaught FFI\Exception: Failed resolving C function 'idonotexist'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you attempt to call a C-ABI function that does not exist, you receive the following:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cdef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'void printf(char *const str, ...);'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'libc.so.6'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Above is fine and works, but now we attempt to call something that is not available:&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;some_function&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Uncaught FFI\Exception: Attempt to call undefined C function 'some_function'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you try loading a C-ABI library that does not exist, another exception is thrown:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;cdef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'void printf(char *const str, ...);'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'idonotexist.so'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Uncaught FFI\Exception: Failed loading 'idonotexist.so'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling errors
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Example code provided in &lt;a href="https://github.com/rask/php-ffi-examples/tree/master/103-hello-world-with-trycatch" rel="noopener noreferrer"&gt;&lt;code&gt;103-hello-world-with-trycatch&lt;/code&gt;&lt;/a&gt; directory of the examples repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All FFI errors are either instances of &lt;code&gt;\FFI\Exception&lt;/code&gt;, or descendants of it. So the simplest approach to making your FFI loading a little less fragile is to use a generic try-catch as follows:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/header.h'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\FFI\Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// log error, do fail tasks, prevent running, etc.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello %s!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple example. We have not taken care of the following situation for instance:&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="cp"&gt;&amp;lt;?php&lt;/span&gt; &lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$ffi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;\FFI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/header.h'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\FFI\Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// log error, do fail tasks, prevent running, etc.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$ffi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;function_which_is_not_defined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// oops&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Handling raw FFI exceptions is not the best solution, and you will most probably want to abstract your FFI interactions behind a class or similar, which provides saner error handling itself.&lt;/p&gt;

&lt;p&gt;Another option could be to run some up-front tests against the FFI instance during loading or in automated tests to make sure the library works. This might not work well enough in the end though.&lt;/p&gt;

&lt;p&gt;In any case these are outside the scope of this introductory post, and we will look at FFI abstractions in a later installment of this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;In this part we took a look at the requirements for FFI installation and configuration, and also learned a few ways to load and use a C library inside PHP using FFI.&lt;/p&gt;

&lt;p&gt;In the next part, we're going to learn how to write a C-ABI library using Rust, and hopefully learn a bit about how the C-ABI works and how Rust libraries should be constructed to be a little more FFI-friendly.&lt;/p&gt;

</description>
      <category>php</category>
      <category>ffi</category>
      <category>rust</category>
    </item>
    <item>
      <title>Are legacy browsers worth supporting?</title>
      <dc:creator>Lassi Heikkinen</dc:creator>
      <pubDate>Tue, 22 Oct 2019 08:15:06 +0000</pubDate>
      <link>https://forem.com/verkkokauppacom/are-legacy-browsers-worth-supporting-hlk</link>
      <guid>https://forem.com/verkkokauppacom/are-legacy-browsers-worth-supporting-hlk</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Yes, if your potential users use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In June 2019 &lt;a href="https://verkkokauppa.com"&gt;verkkokauppa.com&lt;/a&gt; was visited or tried to be visited by 34 different browser vendors, by 1518 browser versions, by 20 operating systems and by 4271 screen dimensions. That's a lot to support especially when considering the number of possible combinations.&lt;/p&gt;

&lt;p&gt;A fundamental problem in web development since the early days has been drawing the line which browsers and devices to support and which not. Ultimately the question is how to keep the website serving as many users as possible while making use of the helpful features of the modern browsers which the vast majority of users use anyway.&lt;/p&gt;

&lt;p&gt;When tackling the problem two questions are asked: if non-standard code (aka browser hacks) are justified and how small ROI (Return of Investment) is justified in order to support a broader set of browsers. In other words the second question is: how much time can be invested to support more browsers.&lt;/p&gt;

&lt;p&gt;Perhaps we should also take a closer look if the browsers are used to generate any revenue or are some browsers used more like for fun.&lt;/p&gt;

&lt;p&gt;Let's look at one approach how to determinate which browsers are worth supporting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;p&gt;First we want to gather all data available to make as good decisions as possible. Our tools and sources include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Analytics 360 (GA)&lt;/strong&gt; to know how the users behave.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browserslist (BL)&lt;/strong&gt; to see what browsers our potential users use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can I use (CIU)&lt;/strong&gt; to see what technologies different browsers support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CrossBrowserTesting (CBT)&lt;/strong&gt; to test how our website behaves in different browsers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BrowserHacks (BH)&lt;/strong&gt; to find technical solutions to specific browser errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;While the actual data is business critical and classified we can share a few interesting highlights from June 2019. We use revenue weighted data ourselves but that cannot be shared.&lt;/p&gt;

&lt;h4&gt;
  
  
  Operating systems by visits
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OS                   Share
--------------------------
Android            38.79 %
Windows            29.56 %
iOS                24.70 %
Macintosh           5.66 %
Linux               0.98 %
Chrome OS           0.20 %
Windows Phone       0.06 %
Playstation 4       0.01 %
Tizen               0.01 %
Xbox                0.00 %

Source: GA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Other operating systems include: Xbox, Blackberry, OS/2, FreeBSD, Nintendo WiiU, NetBSD, OpenBSD, Nintendo 3DS, Playstation Vita and SunOS.&lt;/p&gt;

&lt;h4&gt;
  
  
  Screen dimensions by visits
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dimensions           Share
--------------------------
 360 x 640         13.01 %
1920 x 1080        11.09 %
 375 x 667          9.36 %
 768 x 1024         4.74 %
1366 x 768          3.84 %
1536 x 864          3.48 %
 375 x 812          3.17 %
 360 x 740          3.06 %
 360 x 720          2.64 %
 320 x 568          2.63 %

Source: GA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Browser vendors by visits
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser                  Share
------------------------------
Chrome                 53.02 %
Safari                 24.62 %
Firefox                 7.47 %
Samsung Internet        3.93 %
Android Webview         3.60 %
Edge                    2.38 %
Internet Explorer       2.21 %
Safari (in-app)         1.62 %
Opera                   1.00 %
YaBrowser               0.09 %

Source: GA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Browser versions by visits
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser                Version       Share
------------------------------------------
Chrome                    74       27.93 %
Chrome                    75       20.86 %
Safari                    12       20.05 %
Firefox                   67        5.39 %
Samsung Internet           9        2.94 %
Chrome                    73        2.18 %
Internet Explorer         11        1.99 %
Android Webview           74        1.82 %
Safari (in-app)        (n/a)        1.46 %
Edge                      17        1.21 %
Safari                    11        1.21 %
Chrome                    72        1.16 %
Android Webview           75        1.04 %

Source: GA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;According to this data 9.15 % of our website users use mobile in-app browsers (Samsung Internet, Android Webview and Safari in-app) which is surprisingly high number.&lt;/p&gt;

&lt;h4&gt;
  
  
  Browser versions used by at least 1 % of Finnish internet users
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser                Version       Share
------------------------------------------
Chrome                    74        36.3 %
Chrome                    73        16.5 %
Safari                    12        16.7 %
Firefox                   66         4.9 %
Chrome                    71         2.4 %
Internet Explorer         11         1.8 %
Chrome                    72         1.6 %
Samsung Internet           8         1.5 %
Chrome                    57         1.4 %
Edge                      17         1.4 %
Safari                    11         1.2 %
Chrome                    70         1.0 %
Samsung Internet           9         1.0 %

Source: BL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On average our website users seem to use more modern browsers than Finnish internet users in general.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;Looks like the in-app browsers are gaining popularity. They should be investigated in a more detailed manner.&lt;/p&gt;

&lt;p&gt;But the question of this study, what browsers to support? Should we focus on browsers that are used most today or browsers that have more potential?&lt;/p&gt;

&lt;p&gt;The easy answer is that let's support all browsers. The better browser supported website in technical terms, the more revenue, right? Unfortunately in business we don't have time to support browsers that do not generate revenue. But first we must know the reason why a browser isn't generating revenue. Either users just don't want to use that particular browser to purchase or our website isn't working on that browser. If the latter is the case, we might want to fix it - or not.&lt;/p&gt;

&lt;p&gt;To make the decision we can't look at our own data because it doesn't reveal the browsers which don't work with our website. Luckily we have market data from BL to tell the browser shares of Finnish internet users, our primary customer base.&lt;/p&gt;

&lt;p&gt;To use BL data we must decide first how many different browsers we have the resources to test often enough to be able to keep the promise of supporting it. In our case a realistic set could include 10-20 browsers but how to select those browsers to have as broad coverage as possible?&lt;/p&gt;

&lt;p&gt;After making a few iterations it looks like that by supporting 11 browser versions we can attain over 93 % coverage if we assume that when a website works in a specific version (like Chrome 49) it works in all newer versions too (eg. Chrome 57, 59 etc).&lt;/p&gt;

&lt;p&gt;Here are the commands and data to find out the numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./node_modules/browserslist/cli.js "&amp;gt; 0.1% in FI"
and_chr 74
and_ff 66
android 4.4.3-4.4.4
android 4.2-4.3
chrome 73
chrome 72
chrome 71
chrome 70
chrome 64
chrome 63
chrome 59
chrome 57
chrome 49
edge 18
edge 17
firefox 66
firefox 65
firefox 60
firefox 52
ie 11
ios_saf 12.2
ios_saf 12.0-12.1
ios_saf 11.3-11.4
ios_saf 11.0-11.2
ios_saf 10.3
ios_saf 10.0-10.2
ios_saf 9.3
ios_saf 8
op_mini all
opera 58
safari 12.1
safari 12
safari 11.1
safari 10.1
samsung 9.2
samsung 8.2
samsung 7.2-7.4
samsung 4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./node_modules/browserslist/cli.js --coverage=FI "&amp;gt; 0.1%"
These browsers account for 93.66% of all users in the FI
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we combine the versions to minimize the number of tests needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser             Version
---------------------------
Android Chrome           74
Android Firefox          66
Android WebView         4.2
Chrome                   49
Edge                     17
Firefox                  52
Internet Explorer        11
iOS Safari                8
Opera                    58
Safari                 10.1
Samsung Internet          4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After combining the versions we have a set of 11 browser versions to cover 93.66 % of Finnish internet users (2019-07-02). The coverage is actually a bit higher because there are also some newer but less popular versions of for example Chrome which were left out of data because of the 0.1 % threshold.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;One takeaway of this study is quite many users use mobile in-app browsers. Those browsers are difficult to test and support but they could probably generate more revenue if they were looked more carefully.&lt;/p&gt;

&lt;p&gt;The introduced approach to select browsers worth supporting is a so-called quantitative model based on hard data. But in real life the result should be adjusted by the knowledge of experienced developers who may have a good grasp what are the estimated efforts of supporting different browsers. For example supporting Samsung Internet 4 might require too much effort compared to its 0.23 % share.&lt;/p&gt;

&lt;p&gt;To sum up: use data but use it wisely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Afterword
&lt;/h2&gt;

&lt;p&gt;If the browsers could be tested automatically, the set could be much broader and the coverage could get closer to 100 %. CBT has an API but it lacks some browsers and it's not trivial to automate purchasing tests.&lt;/p&gt;

&lt;p&gt;Another takeaway here is that we could monitor GA data to see if generated revenue by some browser version suddenly drops to zero in one day. The reason could be a regression bug that has affected the specific browser to make it not work on that website anymore. Depending on the amount of the revenue loss, the bug is fixed.&lt;/p&gt;

&lt;p&gt;The last point is a little bit out of scope of this study but the anti-pattern from the '00 that tells users the website is best viewed in browser X, had a good point that no-one is going to switch their browser because of some website tells so, even if it's Verkkokauppa.com. But on the other hand if no-one tells the user about it, how can he know that his browser is out-of-date? It's like a closed door in a physical store not telling when it will be open. The relevant information should be offered for the customer but let himself to decide what to do.&lt;/p&gt;

</description>
      <category>browser</category>
      <category>legacy</category>
      <category>ecommerce</category>
      <category>roi</category>
    </item>
    <item>
      <title>Development work at Verkkokauppa.com</title>
      <dc:creator>Lassi Heikkinen</dc:creator>
      <pubDate>Tue, 27 Aug 2019 05:56:00 +0000</pubDate>
      <link>https://forem.com/verkkokauppacom/development-work-at-verkkokauppa-com-3hn2</link>
      <guid>https://forem.com/verkkokauppacom/development-work-at-verkkokauppa-com-3hn2</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;We use React, Node, PHP, MariaDB and ~101 other open-source technologies in a big company. We are very pro and hip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;Verkkokauppa.com is the largest online retailer in Finland, turnover being roughly 500 million. We have developed our systems in-house and used open-source software since the company was founded in 1992. The strategy to have internal software development teams has been one of the key decisions to land where we are today, building one of the best ecommerce websites in the market.&lt;/p&gt;

&lt;p&gt;But we are not just professional and proud of our work, we are also super trendy. We even started to blog what we do and how we do in order to give something back to the dev community. Being transparent is also an evil attempt to lure you to &lt;a href="https://www.verkkokauppa.com/fi/tyopaikat"&gt;join us&lt;/a&gt;. (fluency in Finnish is a requirement)&lt;/p&gt;

&lt;p&gt;This article discusses briefly how our development work is done and what our technological choices are today. Focus is on the customer facing website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Organization
&lt;/h2&gt;

&lt;p&gt;Roughly half of our sixty employees in the tech department are working with internal systems such as product management, storage, logistics, point-of-sale machines and other physical world problems. About a fourth of people are in supportive teams and roles such as infra, testing, planning, DevOps, SecOps and DBAs. And then there are two web teams dedicated to the website the customers face and use. One web team is focusing on the platform (APIs, integrations, build tools etc.) and the other team is looking after the user experience (design, usability, accessibility etc.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;p&gt;We manage working by using agile model based on Scrum since 2013. Two weeks sprints are in use and we have dedicated Scrum Masters, Product Owners and a coach. Planning, Grooming, Cross-Team Planning, Retrospective, Review, Demo and Daily, we have it all! We have also opportunity to play and find new things every second sprint in Proto Friday.&lt;/p&gt;

&lt;p&gt;From the developer's point of view it takes about 4-5 hours per week to participate all scrum meetings. Because heavy processes will always become overhead for the actual work, we try to keep the meetings as compact and relevant as possible. The same ideology goes with project management where Trello has been our choice since 2014.&lt;/p&gt;

&lt;p&gt;We use git, GitLab and A Sane git Workflow to control our codebase state. Code reviewing is encouraged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technologies
&lt;/h2&gt;

&lt;p&gt;Finally here's a list of the most essential technological choices of our site that make it possible to deliver a great customer experience for thousands of simultaneous online users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Platform
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Persistent data
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;MariaDB&lt;/li&gt;
&lt;li&gt;MongoDB&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Non-persistent data
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;LocalStorage&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Back-end
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PHP: Symfony, Composer&lt;/li&gt;
&lt;li&gt;Node: Hapi, NPM&lt;/li&gt;
&lt;li&gt;Webpack, Babel, code splitting&lt;/li&gt;
&lt;li&gt;PHPUnit, Robot, jest&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Front-end
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;React: Redux, universal rendering&lt;/li&gt;
&lt;li&gt;HTML: schema.org&lt;/li&gt;
&lt;li&gt;CSS: Sass&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future
&lt;/h2&gt;

&lt;p&gt;We are quite happy with our choices at the moment. Today radars are reporting on Styled Components, Preact, Svelte, GraphQL, WebAssembly but our focus at the moment is on refactoring codebase to use the technologies listed above and making new features for the users. Plus keeping the business people happy–you never know what they figure out next week.&lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>scrum</category>
    </item>
  </channel>
</rss>
