PHP & Web Development Blogs

Showing 11 to 15 of 43 blog articles.
3268 views · 1 years ago

![Midwest PHP and Nomad PHP Join Forces!](https://tessakriesel.com/wp-content/uploads/midwest-php.png)

### [Interested in sponsoring? Check out the prospectus](https://drive.google.com/file/d/1ZYDi9dKCI47caU9vr69dvdhH0Enm2XcM/view)

### A little history

Several years ago I had the distinct privilege of founding Midwest PHP with Jonathan Sundquist. The goal was simple, to bring an affordable PHP conference to Minnesota and the midwest region.

Midwest PHP was created for one simple reason - there weren't a lot of alternatives, especially affordable ones. At the time, your choices were ZendCon in Silicon Valley, php[tek] in Chicago, or Northeast PHP in Boston. While Northeast PHP formed the blueprint of a community conference - it still required a flight and a costly hotel in Boston. I wanted something where local attendees, college students, and those just beginning in their PHP careers could go to learn, network, and become part of the PHP community.

Shortly after Midwest PHP was formed (originally we were using the name PHPFreeze - until Sundquist told me what a horrible idea it was), Adam Culp launched Sunshine PHP which has become one of the top community focused PHP conferences (but still requires that flight and hotel in Miami). Sundquist and I knew that any reasonable developer would still prefer to attend a conference in a blizzard than enjoy the beautiful Floridian weather (ok, that might not be it, but we still understood the need that existed).

After moving to California for my new job, Jonathan Sundquist continued to run Midwest PHP as more community conferences appeared. With his efforts, and the torch being passed to Mike Willbanks, Midwest PHP celebrated it's seventh consecutive year, becoming the longest continuously running PHP conference (if you go by formed date, if you go by actual conference date Sunshine PHP beats us out by a month).

### A renewed focus

![Developers at Midwest PHP](https://pbs.twimg.com/profile_banners/735022608/1416087272/1500x500)

Because of the incredible work Jonathan and Mike have done, Midwest PHP has stood the test of time - and the peaks and valleys that come with any conference. With the shifts in the PHP community and the sad loss of several community conferences - we realized the need for Midwest PHP is more now than ever, and to meet that need we needed to reimagine the way the conference operated.

We also realized that the best way to make Midwest PHP accessible was to combine forces, creating a seamless partnership between Nomad PHP and Midwest PHP. Through this partnership we're not only able to stream the event to make it more accessible ($19.95/mo), but also expand the conference.

This year, taking place on **April 2-4, 2020 - Midwest PHP will bring together over 800 developers** both in-person and virtually! Making this year truly unique, however, and staying with our purpose of helping new developers be part of the PHP community is a **brand new, FREE, beginner track**. I'm excited to say we will be giving away 200 tickets to those wishing to attend our Beginner or Learn PHP track!!!

We will also work to keep prices as low as possible as we offer our standard PHP tracks (Everyday PHP and PHP Performance & Security) starting at $250/ person, and **a brand new enterprise track** geared at developers facing challenges at unprecedented scale starting at $450/ person.

Last but not least, it is our goal with the help of our sponsors to include the workshop day as part of your ticket price - allowing you to get one day of in-depth training, and two more full days of sessions. On top of this, we're also excited to make the Nomad PHP and Nomad JS video libraries available for Standard and Enterprise attendees, providing over 220 additional virtual sessions on demand!

### For sponsors

Sponsoring a conference is hard. We understand the challenge of gauging ROI, planning travel, and coordinating outreach. With the combined forces of Midwest PHP and Nomad PHP, we're able to offer sponsors unique plans that maximize their investment - while ensuring the funds go back into the event to create an amazing experience for our attendees.

Beyond Midwest PHP's goal to be the largest PHP conference this year - the included Nomad PHP advertising will help you reach a much larger and broader audience, allowing for follow up advertisements and consistent engagement with the PHP community.

### [Interested in sponsoring? Check out the prospectus](https://drive.google.com/file/d/1ZYDi9dKCI47caU9vr69dvdhH0Enm2XcM/view)

### Next steps

For more information, please visit the [Midwest PHP](https://midwestphp.org) website. The venue, call for papers, and additional information will all be posted there soon.

5908 views · 1 years ago

![Introduction to Gitlab CI for PHP developers](https://images.ctfassets.net/vzl5fkwyme3u/5EUoVwcn2inEG3LsNJFAYp/14e5c704d91665c0de6ffd506a283ec3/AdobeStock_90389954.png?w=1000)

As a developer, you've probably at least heard something about [CI - Continuous integration](https://en.wikipedia.org/wiki/Continuous_integration). And if you haven't - you better fix it ASAP, because that's something awesome to have on your skill list and can get extremely helpful in your everyday work. This post will focus on CI for PHP devs, and specifically, on CI implementation from [Gitlab](https://docs.gitlab.com/ee/ci/README.html). I will suppose you know the basics of [Git](https://git-scm.com/), [PHP](https://php.net/), [PHPUnit](https://phpunit.de/), [Docker](https://www.docker.com/) and unix shell. Intended audience - intermediate PHP devs.

Adding something to your workflow must serve a purpose. In this case the goal is to automate routine tasks and achieve better quality control. Even a basic PHP project IMO needs the following:

* [linter](https://en.wikipedia.org/wiki/Lint_(software)) checks (cannot merge changes that are invalid on the syntax level)

* Code style checks

* Unit and integration tests

All of those can be just run eventually, of course. But I prefer an automated CI approach even in my personal projects because it leads to a higher level of discipline, you simply can't avoid following a set of rules that you've developed. Also, it reduces a risk of releasing a bug or regression, thus improving quality.

Gitlab is as generous as giving you their CI for free, even for your private repos. At this point it is starting to look as advertising, therefore a quick comparison table for [Gitlab](https://about.gitlab.com/pricing/), Github, [Bitbucket](https://bitbucket.org/product/pricing). AFAIK, Github does not have a built-in solution, instead it is easily integrated with third parties, of which [Travis CI](https://github.com/marketplace/travis-ci/plan/MDIyOk1hcmtldHBsYWNlTGlzdGluZ1BsYW43MA==#pricing-and-setup) seems to be the most popular - I will therefore mention Travis here.

### Public repositories (OSS projects). All 3 providers have a free offer for the open-source community!

| Provider | Limits |

|---|---|

| Gitlab | 2,000 CI pipeline minutes per group per month, shared runners |

| Travis | Apparently unlimited |

| Bitbucket| 50 min/month, max 5 users, File storage <= 1Gb/month |

### Private repositories

| Provider | Price | Limits |

|---|---|---|

| Gitlab | Free | 2,000 CI pipeline minutes per group per month, shared runners |

| Travis | $69/month | Unlimited builds, 1 job at a time |

| Bitbucket| Free | 50 min/month, max 5 users, File storage <= 1Gb/month |

## Getting started

I made a small project based on Laravel framework and called it "ci-showcase". I work in Linux environment, and the commands I use in the examples, are for linux shell. They should be pretty much the same on Mac and nearly the same on Windows though.

```sh

composer create-project laravel/laravel ci-showcase

```

Next, I went to gitlab website and created a new public project: https://gitlab.com/crocodile2u/ci-showcase. Cloned the repo and copied all files and folders from the newly created project - the the new git repo. In the root folder, I placed a `.gitignore` file:

```

.idea

vendor

.env

```

Then the `.env` file:

```

APP_ENV=development

```

Then I generated the application encryption key: `php artisan key:generate`, and then I wanted to verify that the primary setup works as expected: `./vendor/bin/phpunit`, which produced the output `OK (2 tests, 2 assertions)`. Nice, time to commit this: `git commit && git push`

[At this point](https://gitlab.com/crocodile2u/ci-showcase/tree/step-1), we don't yet have any CI, let's do something about it!

### Adding .gitlab-ci.yml

Everyone going to implement CI with Gitlab, is strongly encouraged to bookmark this page: https://docs.gitlab.com/ee/ci/README.html. I will simply provide a short introduction course here plus a bit of boilerplate code to get you started easier.

First QA check that we're going to add is PHP syntax check. PHP has a built-in linter, which you can invoke like this: `php -l my-file.php`. This is what we're going to use. Because the `php -l` command doesn't support multiple files as arguments, I've written a small wrapper shell script and saved it to `ci/linter.sh`:

```sh

#!/bin/sh

files=`sh ci/get-changed-php-files.sh | xargs`

last_status=0

status=0

# Loop through changed PHP files and run php -l on each

for f in "$files" ; do message=`php -l $f` last_status="$?" if [ "$last_status" -ne "0" ]; then # Anything fails -> the whole thing fails echo "PHP Linter is not happy about $f: $message" status="$last_status" fi

done

if [ "$status" -ne "0" ]; then echo "PHP syntax validation failed!"

fi

exit $status

```

Most of the time, you don't actually want to check each and every PHP file that you have. Instead, it's better to check only those files that have been changed. The Gitlab pipeline runs on every push to the repository, and there is a way to know which PHP files have been changed. Here's a simple script, meet `ci/get-changed-php-files.sh`:

```sh

#!/bin/sh

# What's happening here?

#

# 1. We get names and statuses of files that differ in current branch from their state in origin/master.

# These come in form (multiline)

# 2. The output from git diff is filtered by unix grep utility, we only need files with names ending in .php

# 3. One more filter: filter *out* (grep -v) all lines starting with R or D.

# D means "deleted", R means "renamed"

# 4. The filtered status-name list is passed on to awk command, which is instructed to take only the 2nd part

# of every line, thus just the filename

git diff --name-status origin/master | grep '\.php$' | grep -v "^[RD]" | awk '{ print $2 }'

```

These scripts can easily be tested in your local environment ( at least if you have a Linux machine, that is ;-) ).

Now, as we have our first check, we'll finally create our `.gitlab-ci.yml`. This is where your pipeline is declared using [YAML notation](https://yaml.org/):

```yml

# we're using this beautiful tool for our pipeline: https://github.com/jakzal/phpqa

image: jakzal/phpqa:alpine

# For this sample pipeline, we'll only have 1 stage, in real-world you would like to also add at least "deploy"

stages: - QA

linter:

stage: QA

# this is the main part: what is actually executed

script: - sh ci/get-changed-php-files.sh | xargs sh ci/linter.sh

```

The first line is `image: jakzal/phpqa:alpine` and it's telling Gitlab that we want to run our pipeline using a PHP-QA utility by [jakzal](https://github.com/jakzal). It is a docker image containing PHP and a huge variety of QA-tools. We declare one stage - QA, and this stage by now has just a single job named `linter`. Every job can have it's own docker image, but we don't need that for the purpose of this tutorial. Our project reaches [Step 2](https://gitlab.com/crocodile2u/ci-showcase/tree/step-2). Once I had pushed these changes, I immediately went to the [project's CI/CD page](https://gitlab.com/crocodile2u/ci-showcase/pipelines). Aaaand.... the pipeline was already running! I clicked on the `linter` job and saw the following happy green output:

```

Running with gitlab-runner 11.9.0-rc2 (227934c0) on docker-auto-scale ed2dce3a

Using Docker executor with image jakzal/phpqa:alpine ...

Pulling docker image jakzal/phpqa:alpine ...

Using docker image sha256:12bab06185e59387a4bf9f6054e0de9e0d5394ef6400718332c272be8956218f for jakzal/phpqa:alpine ...

Running on runner-ed2dce3a-project-11318734-concurrent-0 via runner-ed2dce3a-srm-1552606379-07370f92...

Initialized empty Git repository in /builds/crocodile2u/ci-showcase/.git/

Fetching changes...

Created fresh repository.

From https://gitlab.com/crocodile2u/ci-showcase * [new branch] master -> origin/master * [new branch] step-1 -> origin/step-1 * [new branch] step-2 -> origin/step-2

Checking out 1651a4e3 as step-2...

Skipping Git submodules setup

$ sh ci/get-changed-php-files.sh | xargs sh ci/linter.sh

Job succeeded

```

It means that our pipeline was successfully created and run!

### PHP Code Sniffer.

[PHP Code Sniffer](https://github.com/squizlabs/PHP_CodeSniffer) is a tool for keeping app of your PHP files in one uniform code style. It has a hell of customizations and settings, but here we will only perform simple check for compatibilty with [PSR-2](https://www.php-fig.org/psr/psr-2/) standard. A good practice is to create a configuration XML file in your project. I will put it in the root folder. Code sniffer can use a few file names, of which I prefer `phpcs.xml`:

```xml

<?xml version="1.0"?>

/resources

```

I also will append another section to `.gitlab-ci.yml`:

```yml

code-style: stage: QA script: # Variable $files will contain the list of PHP files that have changes - files=`sh ci/get-changed-php-files.sh` # If this list is not empty, we execute the phpcs command on all of them - if [ ! -z "$files" ]; then echo $files | xargs phpcs; fi

```

Again, we check only those PHP files that differ from master branch, and pass their names to `phpcs` utility. That's it, [Step 3](https://gitlab.com/crocodile2u/ci-showcase/tree/step-3) is finished! If you go to see the pipeline now, you will notice that `linter` and `code-style` jobs run in parallel.

## Adding PHPUnit

Unit and integration tests are essential for a successful and maintaiable modern software project. In PHP world, [PHPUnit](https://phpunit.de/) is de facto standard for these purposes. The PHPQA docker image already has PHPUnit, but that's not enough. Our project is based on [Laravel](https://laravel.com/), which means it depends on a bunch of third-party libraries, Laravel itself being one of them. Those are installed into `vendor` folder with [composer](https://getcomposer.org/). You might have noticed that our `.gitignore` file has `vendor` folder as one of it entries, which means that it is not managed by the Version Control System. Some prefer their dependencies to be part of their Git repository, I prefer to have only the `composer.json` declarations in Git. Makes the repo much much smaller than the other way round, also makes it easy to avoid bloating your production builds with libraries only needed for development.

Composer is also included into PHPQA docker image, and we can enrich our `.gitlab-ci.yml`:

```yml

test: stage: QA cache: key: dependencies-including-dev paths: - vendor/ script: - composer install - ./vendor/bin/phpunit

```

PHPUnit requires some configuration, but in the very beginning we used `composer create-project` to create our project boilerplate. **laravel/laravel** package has a lot of things included in it, and `phpunit.xml` is also one of them. All I had to do was to add another line to it:

```xml

```

APP_KEY enironment variable is essential for Laravel to run, so I generated a key with `php artisan key:generate`.

`git commit` & `git push`, and we have all three jobs on the **QA** stage!

## Checking that our checks work

In [this branch](https://gitlab.com/crocodile2u/ci-showcase/tree/failing-checks) I intentionally added changes that should fail all three job in our pipeline, take a look at [git diff](https://gitlab.com/crocodile2u/ci-showcase/compare/step-4...failing-checks). And we have this out from the pipeline stages:

**Linter**:

```

$ ci/linter.sh

PHP Linter is not happy about app/User.php:

Parse error: syntax error, unexpected 'syntax' (T_STRING), expecting function (T_FUNCTION) or const (T_CONST) in app/User.php on line 11

Errors parsing app/User.php

PHP syntax validation failed!

ERROR: Job failed: exit code 255

```

**Code-style**:

```

$ if [ ! -z "$files" ]; then echo $files | xargs phpcs; fi

FILE: ...ilds/crocodile2u/ci-showcase/app/Http/Controllers/Controller.php

----------------------------------------------------------------------

FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE

---------------------------------------------------------------------- 13 | WARNING | Line exceeds 120 characters; contains 129 characters

----------------------------------------------------------------------

Time: 39ms; Memory: 6MB

ERROR: Job failed: exit code 123

```

**test**:

```

$ ./vendor/bin/phpunit

PHPUnit 7.5.6 by Sebastian Bergmann and contributors.

F. 2 / 2 (100%)

Time: 102 ms, Memory: 14.00 MB

There was 1 failure:

1) Tests\Unit\ExampleTest::testBasicTest

This test is now failing

Failed asserting that false is true.

/builds/crocodile2u/ci-showcase/tests/Unit/ExampleTest.php:17

FAILURES!

Tests: 2, Assertions: 2, Failures: 1.

ERROR: Job failed: exit code 1

```

Congratulations, our pipeline is running, and we now have much less chance of messing up the result of our work.

## Conclusion

Now you know how to set up a basic QA pipeline for your PHP project. There's still a lot to learn. Pipeline is a powerful tool. For instance, it can make deployments to different environments for you. Or it can build docker images, store artifacts and more! Sounds cool? Then spend 5 minutes of your time and leave a comment, you can also tell me if there is a pipeline topic you would like to be covered in next posts.

3277 views · 1 years ago

![When PHP Frameworks Suck](https://images.ctfassets.net/vzl5fkwyme3u/2H05p7yk8iyAjFi9j3ui8d/6f8037faa2a3c5b5f6abf66549b2dc80/frameworks_suck.png?w=1000)

### INTRO

If you are working as a PHP software developer, there is an extremely high chance that all of your application, you’re currently working on, using frameworks of any kind.

PHP community developers of all levels worship frameworks since there are big historical and practical reasons for that.

### Historical reasons

Since early PHP versions, developers were disreputable because not everybody considered PHP as a programming language, similar to JavaScript a couple of years ago. While strong type language existed decades ago, PHP continues to be soft type since now, only in version 7 basic types were introduced. There is also a matter of the fact that you can script in PHP without using a single object.

But that opened a space for frameworks to step in and introduce themselves as a tool or standard which will shape projects, give them right and order, introduce structure and rules.

And finally, they did. Frameworks are good examples of nice structures, using all available new features PHP offers with every version, enforcing some good practice, etc.

### Practical reasons

The framework offers a lot of common problems already solved. They offer a nice ecosystem for other developers to contribute and plug their components. There is a lot of online resources for learning and stay updated about any particular framework. Also, what every framework community tries very hard, is to make setup and usage easy.

### WHEN PHP FRAMEWORKS SUCKS

I recently had the opportunity to give a talk on a conference and one meetup about why PHP framework sometimes sucks. Sometimes things we see in framework tutorials does not seem to be very much aligned with some object-oriented standards we are striving to enforce, and with basic clean code guidelines. On the other hand, there is nothing wrong with using a framework, if you use it right.

This article is the first "pilot" article in this series. In every new blog in this series, we will go more in-depth about every specific topic I covered during my presentation.

I'm very excited to share this knowledge, as I saw many developers suffer from bonded-to-framework disease.

https://twitter.com/damnjan/status/1058306144458956800

I won’t spend much time here on any particular framework discussion. This series will be just a guide on how to unbind yourself from frameworks and use them as a tool, instead of being independent.

**Here is the [link](https://docs.google.com/presentation/d/1K9nZXleJ_mV5qucQBXjjoF1vv23Hnz3sI96E9eEkgmw/edit?usp=sharing) to the presentation slides.**

7013 views · 1 years ago

![Securing PHP RESTful APIs using Firebase JWT Library](https://images.ctfassets.net/vzl5fkwyme3u/4oaGx3XTrH7kq4KkQbQPQ6/87f8b47b39e42dfdd93e111aa4f91e9f/AdobeStock_191967596.png?w=1000)

Hello Guys,

In our [Last Blog Post](https://nomadphp.com/blog/69/create-simple-restful-apis-using-php-amp-mysql), we have created restful apis,But not worked on its security and authentication. Login api can be public but after login apis should be authenticate using any secure token. one of them is JWT, So i am providing the Steps for Create and use JWT Token in our already created API.

Now its time To Implement JWT Authentication IN our Api, So these are the steps to implement it in our already created Apis

### Step 1:Install and include Firebase JWT(JSON WEB TOKEN) in our project with following composer command        

``` composer require firebase/php-jwt ```

include the composer installed packages

```

require_once('vendor/autoload.php');

```

use namespace using following:

``` use \Firebase\JWT\JWT; ```

### Step 2: Create a JWT server side using Firebase Jwt Library's encode method in Login action , and return it to Client

Define a private variable named Secret_Key in Class like following:

``` private / / you may change this key ```

Now create a method in your class named generateToken as following:

```

public function generateToken($UiD)

{

$payload = array(

'iss' => $_SERVER['HOST_NAME'],

'exp' => time()+600, / / token expiry time in timestamp We have used current we have used 10 minutes as expiry time

'uId' => $UiD

);

try{

$jwt = JWT::encode($payload, $this->Secret_Key,'HS256'); / / last parameter is the Engryption Algorithm name

$res=array("status"=>true,"Token"=>$jwt);

}catch (UnexpectedValueException $e) {

$res=array("status"=>false,"Error"=>$e->getMessage());

}

return $res;

}

```

In our login action , if the user has been logged in successfully then with the status,_data_ and message just replace the login success code with following code:

```

$return['status']=1;

$return['_data_']=$UserData[0];

$return['message']='User Logged in Successfully.';

/ / generate and add jWT token using following method we created in class / /

$jwt=$obj->generateToken($UserData[0]['id']);

if($jwt['status']==true)

{

/ / if token generated successfully then add token in JWT key in response

$return['JWT']=$jwt['Token'];

}

else{

/ / if token generation failed then unset the return user data and add false status

unset($return['_data_']);

$return['status']=0;

$return['message']='Error:'.$jwt['Error'];

}

/ / generate and add jWT token using following method we created in class / /

```

### Step 3: Now with every request after login should have the JWT token in its Post(even we can receive it in get or authentication header also but here we are receiving it in post)

No afetr successfully login you will get the JWt Token in your response,Just add that Token with every post request of after login api calls. So we will do it using postman, Find the screenshot 1 for checking the JWT Token is coming in login api response

![JWT DEMO LOGIN API RESPONSE](https://www.w3school.info/blogimages/JWT_LOGIN_API.png)

### Step 4:After reciving the JWt in every after login api call, we need to check whether the token is fine using JWT decode method in After login Apis like ```UserBlogs``` is a After login Api, So for verify that we are creating Authencate method in class like following:

```

public function Authenticate($JWT,$Curret_User_id)

{

try {

$decoded = JWT::decode($JWT,$this->Secret_Key, array('HS256'));

$payload = json_decode(json_encode($decoded),true);

if($payload['uId'] == $Curret_User_id) / / verify that the user id coming in after login api is equals to the decoded payload user id, if matched then the token is fine and data not tempered

{

$res=array("status"=>true);

}else{

$res=array("status"=>false,"Error"=>"Invalid Token or Token Exipred, So Please login Again!");

}

}catch (UnexpectedValueException $e) {

$res=array("status"=>false,"Error"=>$e->getMessage());

}

return $res;

}

```

### Step 5: Cross check the response returned by Authenticate method in ```UserBlogs``` Action of api , replace the ```UserBlogs``` Action inner content with following code:

```

if(isset($_POST['Uid']))

{

/ / Authenticate the JWT before fetching the data

$resp=$obj->Authenticate($_POST['JWT'],$_POST['Uid']);

if($resp['status']==false)

{

$return['status']=0;

$return['message']='Error:'.$resp['Error'];

}

/ / Authenticate the JWT before fetching the data

else{

$blogs=$obj->get_all_blogs($_POST['Uid']);

if(count($blogs)>0)

{

$return['status']=1;

$return['_data_']=$blogs;

$return['message']='Success.';

}

else

{

$return['status']=0;

$return['message']='Error:Invalid UserId!';

}

}

}

else

{

$return['status']=0;

$return['message']='Error:User Id not provided!';

}

```

Ah great its time to check out the UserBlogs Api, please find the screenshoot for that, Remember we need to put the JWt Token in POST Parameter as we have already recived that Value in Login Api call.

![JWT DEMO Authentication in userBlogs API Call](https://www.w3school.info/blogimages/JWT_userBlogs_API.png)

Now if you want to verify that token is expiring in given time(10 minutes after generation time/login time), i am just clicking the same api with same token after 10 minutes and you can see there will not return any data and it is returning status false with following message :

![JWT DEMO Authentication in userBlogs API Call](https://www.w3school.info/blogimages/JWT_userBlogs_API_TOKEN_EXPIRY.png)

Also if you want to eloborate it more then i suggest you to try with modify Uid value with same token , you will another authentication issue and also if you modify the JWT token also then also you will not get the desired result and get authentication Issue

Thanks for reading out if you want the complete code of this file then please find following:

```

<?php

header("Content-Type: application/json; charset=UTF-8");

require_once('vendor/autoload.php');

use \Firebase\JWT\JWT;

class DBClass {

private $host = "localhost";

private $username = "root";

private $password = ""; / / set your passwrd here

private $database = "news";

public $connection;

private $Secret_Key="*$%43MVKJTKMN$#"; / / you may change this key

/ / get the database connection

public function connect(){

$this->connection = null;

try{

$this->connection = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->database, $this->username, $this->password);

$this->connection->exec("set names utf8");

}catch(PDOException $exception){

echo "Error: " . $exception->getMessage();

}

return $this->connection;

}

public function login($email,$password){

if($this->connection==null)

{

$this->connect();

}

$query = "SELECT id,name,email,createdAt,updatedAt from users where email= ? and password= ?";

$stmt = $this->connection->prepare($query);

$stmt->execute(array($email,md5($password)));

$ret= $stmt->fetchAll(PDO::FETCH_ASSOC);

return $ret;

}

public function get_all_blogs($Uid){

if($this->connection==null)

{

$this->connect();

}

$query = "SELECT b.*,u.id as Uid,u.email as Uemail,u.name as Uname from blogs b join users u on u.id=b.user_id where b.user_id= ?";

$stmt = $this->connection->prepare($query);

$stmt->execute(array($Uid));

$ret= $stmt->fetchAll(PDO::FETCH_ASSOC);

return $ret;

}

public function response($array)

{

echo json_encode($array);

exit;

}

public function generateToken($UiD)

{

$payload = array(

'iss' => $_SERVER['HOST_NAME'],

'exp' => time()+600, / / token expiry time in timestamp We have used current we have used 10 minutes as expiry time

'uId' => $UiD

);

try{

$jwt = JWT::encode($payload, $this->Secret_Key,'HS256'); / / last parameter is the Engryption Algorithm name

$res=array("status"=>true,"Token"=>$jwt);

}catch (UnexpectedValueException $e) {

$res=array("status"=>false,"Error"=>$e->getMessage());

}

return $res;

}

public function Authenticate($JWT,$Current_User_id)

{

try {

$decoded = JWT::decode($JWT,$this->Secret_Key, array('HS256'));

$payload = json_decode(json_encode($decoded),true);

if($payload['uId'] == $Current_User_id) / / verify that the user id coming in after login api is equals to the decoded payload user id, if matched then the token is fine and data not tempered

{

$res=array("status"=>true);

}else{

$res=array("status"=>false,"Error"=>"Invalid Token or Token Exipred, So Please login Again!");

}

}catch (UnexpectedValueException $e) {

$res=array("status"=>false,"Error"=>$e->getMessage());

}

return $res;

}

}

/ / receive the requests here / /

$return=array();

$obj = new DBClass();

if(isset($_GET['action']) && $_GET['action']!='')

{

if($_GET['action']=="login")

{

if(isset($_POST['email']) && isset($_POST['password']))

{

$UserData=$obj->login($_POST['email'],$_POST['password']);

if(count($UserData)>0)

{

$return['status']=1;

$return['_data_']=$UserData[0];

$return['message']='User Logged in Successfully.';

/ / generate and add jWT token using following method we created in class / /

$jwt=$obj->generateToken($UserData[0]['id']);

if($jwt['status']==true)

{

/ / if token generated successfully then add token in JWT key in response

$return['JWT']=$jwt['Token'];

}

else{

/ / if token generation failed then unset the return user data and add false status

unset($return['_data_']);

$return['status']=0;

$return['message']='Error:'.$jwt['Error'];

}

/ / generate and add jWT token using following method we created in class / /

}

else

{

$return['status']=0;

$return['message']='Error:Invalid Email or Password!';

}

}

else

{

$return['status']=0;

$return['message']='Error:Email or Password not provided!';

}

}

elseif($_GET['action']=="UserBlogs")

{

if(isset($_POST['Uid']))

{

/ / Authenticate the JWT before fetching the data

$resp=$obj->Authenticate($_POST['JWT'],$_POST['Uid']);

if($resp['status']==false)

{

$return['status']=0;

$return['message']='Error:'.$resp['Error'];

}

/ / Authenticate the JWT before fetching the data

else{

$blogs=$obj->get_all_blogs($_POST['Uid']);

if(count($blogs)>0)

{

$return['status']=1;

$return['_data_']=$blogs;

$return['message']='Success.';

}

else

{

$return['status']=0;

$return['message']='Error:Invalid UserId!';

}

}

}

else

{

$return['status']=0;

$return['message']='Error:User Id not provided!';

}

}

}

else

{

$return['status']=0;

$return['message']='Error:Action not provided!';

}

$obj->response($return);

$obj->connection=null;

?>

```

3887 views · 1 years ago

![Implement Web Push Notification in PHP using W3C provided Notification API](https://images.ctfassets.net/vzl5fkwyme3u/ERmW7y6S781gcWIiFjTRn/de1c1aeb3c79093210828760fc373ab2/AdobeStock_152541972.png?w=1000)

Hi Guys,

I am sharing you the simple steps by which you can broadcast the web push notifications to your subscriber. In this tutorial we are making a subscriber form and saving information using Ajax and PHP and then through a server side code returning response to current logged in user and showing notification to that user.

**Following are the steps to build this system**

#### 1. Create a database, I am creating db with name 'web_notifications'

Creating subscribers and notifications tables using following sql statements

```

CREATE TABLE IF NOT EXISTS `subscribers` (

`id` int(11) NOT NULL,

`name` varchar(255) NOT NULL,

`email` varchar(255) NOT NULL,

`createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `subscribers`

ADD PRIMARY KEY (`id`);

ALTER TABLE `subscribers`

MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

CREATE TABLE IF NOT EXISTS `notifications` (

`id` int(11) NOT NULL,

`to_user` int(11) NOT NULL,

`title` varchar(255) NOT NULL,

`body` varchar(255) NOT NULL,

`url` varchar(255) NOT NULL,

`is_sent` int(11) NOT NULL DEFAULT '0',

`createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `notifications`

ADD PRIMARY KEY (`id`);

ALTER TABLE `notifications`

MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

```

#### 2. Now create a db_connect.php file with following code

```

<?php

session_start();

$servername = "localhost";

$username = "root";

$password = "";

$dbname = "web_notifications";

/ / Create connection

$conn = new mysqli($servername, $username, $password, $dbname);

/ / Check connection

if ($conn->connect_error) {

die("Connection failed: " . $conn->connect_error);

}

?>

```

#### 3. Create a cookies.js file to read and write browser cookies

```

function WriteCookie(key,content) {

var now = new Date();

now.setMonth( now.getMonth() + 1 );

document.cookie = key+"=" + escape(content) + ";";

document.cookie = "expires=" + now.toUTCString() + ";"

}

function ReadCookie(key) {

var allcookies = document.cookie;

cookiearray = allcookies.split(';');

var CookieData=Array();

for(var i=0; i<cookiearray.length; i++) {

k = cookiearray[i].split('=')[0];

v = cookiearray[i].split('=')[1];

CookieData[k]=v;

}

return CookieData[key];

}

```

#### 4. Create a ajax file to read and mark is_sent if any notification foun to be sent in database for that user. create file with name 'fetch_notifications.php' with following content

```

<?php require 'db_connect.php';

$sql = "SELECT id,title,body,url FROM notifications where to_user='"[email protected]$_GET['user_id']."' and is_sent='0' ";

$result = $conn->query($sql);

$data=array();

if ($result->num_rows > 0) {

while($row = $result->fetch_assoc()) {

$data[]=$row;

/ /update this record as is_sent

$upd = "update notifications set is_sent='1' where id='".$row['id']."' ";

$conn->query($upd);

}

}

if(count($data)>0)

{

$response=array("status"=>1,"notification"=>$data);

}

else

{

$response=array("status"=>0,"error"=>"No new notification!");

}

echo json_encode($response);

$conn->close();

?>

```

#### 5. Now code index.php to show subscriber form and on submit insert record into the subscriber table

```

<?php require 'db_connect.php'; ?>

<!DOCTYPE html>

<html>

<head>

<title>Web Push Notification Demo</title>

<script src="./cookies.js" type="text/javascript"></script>

<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">

<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">

<?php

if(isset($_POST['subscribe_form']))

{

$_SESSION['is_login']=0;

$username=$conn->real_escape_string($_POST['username']);

$useremail=$conn->real_escape_string($_POST['useremail']);

$sql = "INSERT INTO subscribers set name='".$username."',email='".$useremail."' ";

if ($conn->query($sql) === TRUE) {

$_SESSION['is_login']=1;

$_SESSION['Uid']= $conn->insert_id;

$_SESSION['Uname']= $username;

?>

<script type="text/javascript">

WriteCookie("Uid","<?php echo $_SESSION['Uid']; ?>");

</script>

<?php

$msg="<p style='color:green'>You have subscribe for push notification succesfully :)</p>";

} else {

$msg="<p style='color:red'>Error in subscribing for notifications</p>";

}

}

?>

<div class="container">

<?php

if(isset($msg) && $msg!='')

{

?>

<br>

<div class="alert alert-info">

<?php echo $msg; ?>

</div>

<?php

}

if(isset($_SESSION['is_login']) && $_SESSION['is_login']==1)

{

?>

<h2>Welcome <?php echo $_SESSION['Uname']; ?></h2>

<script type="text/javascript">

setInterval(function(){

check_notification();

}, 10000);

function check_notification()

{

var Uid=ReadCookie("Uid");

if(Uid!==undefined)

{

$.ajax({url: "fetch_notifications.php?user_id="+Uid, success: function(result){

var response=JSON.parse(result);

if(response.status==1)

{

response=response.notifications;

for (var i = response.length - 1; i >= 0; i--) {

var url = response[i]['url'];

var noti = new Notification(response[i]['title'], {

icon: 'logo.png',/ /this should be your logo in root folder

body: response[i]['body'],

});

noti.onclick = function () {

window.open(url);

noti.close();

};

};

}

else{

console.log(response.error);

}

}

});

}

}

</script>

<?php

}

else

{

?>

<h2 class="text-center">Subscribe for Notifications</h2>

<div class="row justify-content-center">

<div class="col-12 col-md-8 col-lg-6 pb-5">

<div class="card border-primary rounded-0">

<div class="card-header p-0">

<div class="bg-info text-white text-center py-2">

<h3><i class="fa fa-envelope"></i> Information</h3>

<p class="m-0">provide your information</p>

</div>

</div>

<div class="card-body p-3">

<form method="post">

<!--Body-->

<div class="form-group">

<div class="input-group mb-2">

<div class="input-group-prepend">

<div class="input-group-text"><i class="fa fa-user text-info"></i></div>

</div>

<input type="text" class="form-control" id="username" name="username" placeholder="Input Your Name Here" required>

</div>

</div>

<div class="form-group">

<div class="input-group mb-2">

<div class="input-group-prepend">

<div class="input-group-text"><i class="fa fa-envelope text-info"></i></div>

</div>

<input type="text" class="form-control" id="useremail" name="useremail" pattern="[^@\s][email protected][^@\s]+\.[^@\s]+" title="Invalid email address" placeholder="[email protected]" required>

</div>

</div>

<div class="text-center">

<input type="submit" value="Subscribe" name="subscribe_form" class="btn btn-info btn-block rounded-0 py-2">

</div>

</form>

</div>

</div>

</div>

</div>

<?php }?>

</div>

</head>

<body>

</body>

</html>

<?php

$conn->close();

?>

```

The frontend of your subscription page (index.php) should look like this:

![Subscribing Form to User](https://www.w3school.info/blogimages/front_web_push_page.png)

Now we are ready to receive notification in frontend, but we still need to create an admin page from where we can send notification to subscriber(s).

#### 6. Create a table for admin user

```

CREATE TABLE IF NOT EXISTS `admin` (

`id` int(11) NOT NULL,

`username` varchar(255) NOT NULL,

`password` varchar(255) NOT NULL,

`createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `admin`

ADD PRIMARY KEY (`id`);

ALTER TABLE `admin`

MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

INSERT INTO `web_notifications`.`admin` (`id`, `username`, `password`, `createdAt`) VALUES (NULL, 'admin', MD5('123456'), CURRENT_TIMESTAMP);

```

Following is the code for admin.php to add the notifications to subscriber(s) account also i have inserted following login credentials for admin in admin table:

username:admin

password:123456

#### 7. Now put following code in admin.php

```

<?php require 'db_connect.php'; ?>

<!DOCTYPE html>

<html>

<head>

<title>ADMIN PAGE</title>

<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">

<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">

<?php

if(isset($_POST['login']))

{

$_SESSION['admin_login']=0;

$username=$conn->real_escape_string($_POST['username']);

$password=$conn->real_escape_string($_POST['password']);

$sql = "SELECT * FROM admin where username='".$username."' and password='".md5($password)."' ";

$result = $conn->query($sql);

if ($result->num_rows > 0) {

$_SESSION['admin_login']=1;

$msg="<p style='color:green'>Admin Logged-in Successfully :)</p>";

}

else {

$msg="<p style='color:red'>INVALID CREDENTIALS FOR ADMIN</p>";

}

}

if(isset($_POST['add_notification']))

{

$title=$conn->real_escape_string($_POST['title']);

$body=$conn->real_escape_string($_POST['body']);

$url=$conn->real_escape_string($_POST['url']);

$users=$_POST['users'];

foreach ($users as $user_id) {

$ins = "insert into notifications set to_user='".$user_id."' , title='".$title."', url='".$url."', body='".$body."' ";

$conn->query($ins);

}

$msg="<p style='color:green'>Notification(s) added to subscribers account.</p>";

}

?>

<div class="container">

<?php

if(isset($msg) && $msg!='')

{

?>

<br>

<div class="alert alert-info">

<?php echo $msg; ?>

</div>

<?php

}

if(isset($_SESSION['admin_login']) && $_SESSION['admin_login']==1)

{

?>

<h2>Welcome Admin, Send notification to Subscriber(s)</h2>

<form method="post">

<div class="form-group">

<label for="sel1">Select Subscriber(s):</label>

<select multiple="multiple" required="required" class="form-control" id="users" name="users[]">

<?php

$sql = "SELECT id,name FROM subscribers";

$result = $conn->query($sql);

$data=array();

if ($result->num_rows > 0) {

while($row = $result->fetch_assoc()) {

echo "<option value='".$row['id']."'>".$row['name']."</option>";

}

}

?>

</select>

</div>

<div class="form-group">

<label for="email">Title</label>

<input type="text" required class="form-control" placeholder="notification title here" name="title" id="title">

</div>

<div class="form-group">

<label for="email">Message</label>

<textarea required class="form-control" placeholder="notification message here" name="body" id="body"></textarea>

</div>

<div class="form-group">

<label for="email">Url</label>

<input type="url" required class="form-control" placeholder="notification landing/click url here" name="url" id="url">

</div>

<input type="submit" class="btn btn-primary btn-block" name="add_notification" value="Submit" />

</form>

<?php

}

else

{

?>

<h2 class="text-center">ADMINISTRATOR</h2>

<div class="row justify-content-center">

<div class="col-12 col-md-8 col-lg-6 pb-5">

<div class="card border-primary rounded-0">

<div class="card-header p-0">

<div class="bg-info text-white text-center py-2">

<h3><i class="fa fa-envelope"></i> LOGIN</h3>

<p class="m-0">provide admin login credentials</p>

</div>

</div>

<div class="card-body p-3">

<form method="post">

<!--Body-->

<div class="form-group">

<div class="input-group mb-2">

<div class="input-group-prepend">

<div class="input-group-text"><i class="fa fa-user text-info"></i></div>

</div>

<input type="text" class="form-control" id="username" name="username" placeholder="Input username here" required>

</div>

</div>

<div class="form-group">

<div class="input-group mb-2">

<div class="input-group-prepend">

<div class="input-group-text"><i class="fa fa-key text-info"></i></div>

</div>

<input type="password" class="form-control" id="password" name="password" placeholder="your password here" required>

</div>

</div>

<div class="text-center">

<input type="submit" value="Login" name="login" class="btn btn-info btn-block rounded-0 py-2">

</div>

</form>

</div>

</div>

</div>

</div>

<?php }?>

</div>

</head>

<body>

</body>

</html>

<?php

$conn->close();

?>

```

The admin page will ask login credentials first then it will look like following screenshot:

![Admin Send Notifiv=cation to subscribers](https://www.w3school.info/blogimages/admin_web_push_page.png)

Now in your project if you open index.php you have a frontend where user will register themselves to receive notifications, and admin.php is your backend where you can send notifications to users or subscribers

If you face any problem in setup this small project please just let me know in the comments below, or by messaging me.

SPONSORS

Faster PHP Cloud Hosting