IT Hírek

PHP HTTP Headers Information (New)

PHP Classes - 11 óra 50 perc
Package: PHP HTTP Headers Information Summary: Get the headers of HTTP response of a given URL Groups: HTTP, PHP 5 Author: Waqar A Description: This class can get the headers of HTTP response of a given URL...

Kategóriák: IT Hírek

Improving Performance Perception with Pingdom and GTmetrix

Sitepoint PHP - p, 06/22/2018 - 20:00

This article is part of a series on building a sample application --- a multi-image gallery blog --- for performance benchmarking and optimizations. (View the repo here.)

In this article, we'll analyze our gallery application using the tools we explained in the previous guide, and we'll look at possible ways to further improve its performance.

As per the previous post, please set up Ngrok and pipe to the locally hosted app through it, or host the app on a demo server of your own. This static URL will enable us to test our app with external tools like GTmetrix and Pingdom Tools.

We went and scanned our website with GTmetrix to see how we can improve it. We see that results, albeit not catastrophically bad, still have room for improvement.

The first tab --- PageSpeed --- contains a list of recommendations by Google. The first item under the PageSpeed tab --- a warning about a consistent URL --- pertains to our application outputting the images randomly, so that is an item we will skip. The next thing we can do something about is browser caching.

Browser Caching

We see that there is a main.css file that needs its Expires headers set, and the images in the gallery need the same thing. Now, the first idea for these static files would be to set this in our Nginx configuration:

location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { expires 14d; }

We can simply put this inside our server block and leave it to Nginx, right?

Well, not really. This will take care of our static files, like CSS, but the /raw images we are being warned about aren't really that static. So this snippet in our Nginx configuration won't exactly fix this issue so easily. For our images, we have an actual controller that creates these on the fly, so it would be ideal if we could set our response headers right there, in the controller. For some reason, these weren't being set properly by Glide.

Maybe we could set our Nginx directive in a way to include the raw resources, but we felt the controller approach to be more future-proof. This is because we aren't sure what other content may end up with an raw suffix eventually --- maybe some videos, or even audio files.

So, we opened /src/ImageController.php in our image gallery app, and dropped these two lines inside of our serveImageAction(), just before the line return $response:

// cache for 2 weeks $response->setSharedMaxAge(1209600); // (optional) set a custom Cache-Control directive $response->headers->addCacheControlDirective('must-revalidate', true);

This will modify our dynamic image responses by adding the proper Cache Control and Expires headers.

Symfony has more comprehensive options for the caching of responses, as documented here.

Having restarted Nginx, we re-tested our app in GTmetrix, and lo and behold:

The post Improving Performance Perception with Pingdom and GTmetrix appeared first on SitePoint.

Kategóriák: IT Hírek

Atyja szerint rossz irányba halad a C++, baj lehet belőle hamarosan - p, 06/22/2018 - 12:45
A C++ fejlesztése veszélyes irányba halad, aminek hamarosan súlyos következményei lehetnek, ha nem kerül az irány korrigálásra. Ezt nem más, mint Bjarne Stroustrup, a nyelv egykori kitalálója állítja, aki szerint a C++ hamarosan "saját súlya alatt összeroskadhat".
Kategóriák: IT Hírek

MySQL Performance Boosting with Indexes and Explain

Sitepoint PHP - cs, 06/21/2018 - 20:00

Techniques to improve application performance can come from a lot of different places, but normally the first thing we look at --- the most common bottleneck --- is the database. Can it be improved? How can we measure and understand what needs and can be improved?

One very simple yet very useful tool is query profiling. Enabling profiling is a simple way to get a more accurate time estimate of running a query. This is a two-step process. First, we have to enable profiling. Then, we call show profiles to actually get the query running time.

Let's imagine we have the following insert in our database (and let's assume User 1 and Gallery 1 are already created):

INSERT INTO `homestead`.`images` (`id`, `gallery_id`, `original_filename`, `filename`, `description`) VALUES (1, 1, 'me.jpg', 'me.jpg', 'A photo of me walking down the street'), (2, 1, 'dog.jpg', 'dog.jpg', 'A photo of my dog on the street'), (3, 1, 'cat.jpg', 'cat.jpg', 'A photo of my cat walking down the street'), (4, 1, 'purr.jpg', 'purr.jpg', 'A photo of my cat purring');

Obviously, this amount of data will not cause any trouble, but let's use it to do a simple profile. Let's consider the following query:

SELECT * FROM `homestead`.`images` AS i WHERE i.description LIKE '%street%';

This query is a good example of one that can become problematic in the future if we get a lot of photo entries.

To get an accurate running time on this query, we would use the following SQL:

set profiling = 1; SELECT * FROM `homestead`.`images` AS i WHERE i.description LIKE '%street%'; show profiles;

The result would look like the following:

Query_Id Duration Query 1 0.00016950 SHOW WARNINGS 2 0.00039200 SELECT * FROM homestead.images AS i \nWHERE i.description LIKE \'%street%\'\nLIMIT 0, 1000 3 0.00037600 SHOW KEYS FROM homestead.images 4 0.00034625 SHOW DATABASES LIKE \'homestead\ 5 0.00027600 SHOW TABLES FROM homestead LIKE \'images\' 6 0.00024950 SELECT * FROM homestead.images WHERE 0=1 7 0.00104300 SHOW FULL COLUMNS FROM homestead.images LIKE \'id\'

As we can see, the show profiles; command gives us times not only for the original query but also for all the other queries that are made. This way we can accurately profile our queries.

But how can we actually improve them?

We can either rely on our knowledge of SQL and improvise, or we can rely on the MySQL explain command and improve our query performance based on actual information.

Explain is used to obtain a query execution plan, or how MySQL will execute our query. It works with SELECT, DELETE, INSERT, REPLACE, and UPDATE statements, and it displays information from the optimizer about the statement execution plan. The official documentation does a pretty good job of describing how explain can help us:

With the help of EXPLAIN, you can see where you should add indexes to tables so that the statement executes faster by using indexes to find rows. You can also use EXPLAIN to check whether the optimizer joins the tables in an optimal order.

The post MySQL Performance Boosting with Indexes and Explain appeared first on SitePoint.

Kategóriák: IT Hírek

UI-tesztelő fejlesztői eszköz forrását adta ki a Microsoft - cs, 06/21/2018 - 11:15
A Microsoft a hét közepén jelentette be, hogy elérhetővé tette egy, elsősorban fejlesztőknek szánt UI-tesztelésre használható segédeszköz forráskódját. A WinAppDriver UI Recorder rendkívül egyszerűvé teszi a teljesen automatikusan futtatható unit tesztek létrehozását a felhasználói felület működésének ellenőrzésére.
Kategóriák: IT Hírek

PHP License Key Generator API (New)

PHP Classes - cs, 06/21/2018 - 07:32
Package: PHP License Key Generator API Summary: Generate and validate software license keys Groups: PHP 5, Security, Validation Author: Juraj Puchký Description: This package can generate and validate software license keys...

Kategóriák: IT Hírek

PHP-level Performance Optimization with Blackfire

Sitepoint PHP - sze, 06/20/2018 - 20:00

Throughout the past few months, we've introduced Blackfire and ways in which it can be used to detect application performance bottlenecks. In this post, we'll apply it to our freshly started project to try and find the low-points and low-hanging fruit which we can pick to improve our app's performance.

If you're using Homestead Improved (and you should be), Blackfire is already installed. Blackfire should only ever be installed in development, not in production, so it's fine to only have it there.

Note: Blackfire can be installed in production, as it doesn't really trigger for users unless they manually initiate it with the installed Blackfire extension. However, it's worth noting that defining profile triggers on certain actions or users that don't need the extension will incur a performance penalty for the end user. When Blackfire-testing live, make the test sessions short and effective, and avoid doing so under heavy load.

While it's useful to be introduced to Blackfire before diving into this, applying the steps in this post won't require any prior knowledge; we'll start from zero.


The following are useful terms when evaluating graphs produced by Blackfire.

  • Reference Profile: We usually need to run our first profile as a reference profile. This profile will be the performance baseline of our application. We can compare any profile with the reference, to measure the performance achievements.

  • Exclusive Time: The amount of time spent on a function/method to be executed, without considering the time spent for its external calls.

  • Inclusive Time: The total time spent to execute a function including all the external calls.

  • Hot Paths: Hot Paths are the parts of our application that were most active during the profile. These could be the parts that consumed more memory or took more CPU time.

The first step is registering for an account at Blackfire. The account page will have the tokens and IDs which need to be placed into Homestead.yaml after cloning the project. There's a placeholder for all those values at the bottom:

# blackfire: # - id: foo # token: bar # client-id: foo # client-token: bar

After uncommenting the rows and replacing the values, we need to install the Chrome companion.

The Chrome companion is useful only when needing to trigger profiling manually --- which will be the majority of your use cases. There are other integrations available as well, a full list of which can be found here.

Optimization with Blackfire

We'll test the home page: the landing page is arguably the most important part of any website, and if that takes too long to load, we're guaranteed to lose our visitors. They'll be gone before Google Analytics can kick in to register the bounce! We could test pages on which users add images, but read-only performance is far more important than write performance, so we'll focus on the former.

This version of the app loads all the galleries and sorts them by age.

Testing is simple. We open the page we want to benchmark, click the extension's button in the browser, and select "Profile!".

Here's the resulting graph:

In fact, we can see here that the execution time inclusive to exclusive is 100% on the PDO execution. Specifically, this means that the whole dark pink part is spent inside this function and that this function in particular is not waiting for any other function. This is the function being waited on. Other method calls might have light pink bars far bigger than PDO's, but those light pink parts are a sum of all the smaller light pink parts of depending functions, which means that looked at individually, those functions aren't the problem. The dark ones need to be handled first; they are the priority.

Also, switching to RAM mode reveals that while the whole call used almost a whopping 40MB of RAM, the vast majority is in the Twig rendering, which makes sense: it is showing a lot of data, after all.

In the diagram, hot paths have thick borders and generally indicate bottlenecks. Intensive nodes can be part of the hot path, but also be completely outside it. Intensive nodes are nodes a lot of time is spent in for some reason, and can be indicative of problems just as much.

By looking at the most problematic methods and clicking around on relevant nodes, we can identify that PDOExecute is the most problematic bottleneck, while unserialize uses the most RAM relative to other methods. If we apply some detective work and follow the flow of methods calling each other, we'll notice that both of these problems are caused by the fact that we're loading the whole set of galleries on the home page. PDOExecute takes forever in memory and wall time to find them and sort them, and Doctrine takes ages and endless CPU cycles to turn them into renderable entities with unserialize to loop through them in a twig template. The solution seems simple --- add pagination to the home page!

By adding a PER_PAGE constant into the HomeController and setting it to something like 12, and then using that pagination constant in the fetching procedure, we block the first call to the newest 12 galleries:

$galleries = $this->em->getRepository(Gallery::class)->findBy([], ['createdAt' => 'DESC'], self::PER_PAGE);

We'll trigger a lazy load when the user reaches the end of the page when scrolling, so we need to add some JS to the home view:

{% block javascripts %} {{ parent() }} <script> $(function () { var nextPage = 2; var $galleriesContainer = $('.home__galleries-container'); var $lazyLoadCta = $('.home__lazy-load-cta'); function onScroll() { var y = $(window).scrollTop() + $(window).outerHeight(); if (y >= $('body').innerHeight() - 100) { $(window).off('scroll.lazy-load'); $; } } $lazyLoadCta.on('click', function () { var url = "{{ url('home.lazy-load') }}"; $.ajax({ url: url, data: {page: nextPage}, success: function (data) { if (data.success === true) { $galleriesContainer.append(; nextPage++; $(window).on('scroll.lazy-load', onScroll); } } }); }); $(window).on('scroll.lazy-load', onScroll); }); </script> {% endblock %}

Since annotations are being used for routes, it's easy to just add a new method into the HomeController to lazily load our galleries when triggered:

/** * @Route("/galleries-lazy-load", name="home.lazy-load") */ public function homeGalleriesLazyLoadAction(Request $request) { $page = $request->get('page', null); if (empty($page)) { return new JsonResponse([ 'success' => false, 'msg' => 'Page param is required', ]); } $offset = ($page - 1) * self::PER_PAGE; $galleries = $this->em->getRepository(Gallery::class)->findBy([], ['createdAt' => 'DESC'], 12, $offset); $view = $this->twig->render('partials/home-galleries-lazy-load.html.twig', [ 'galleries' => $galleries, ]); return new JsonResponse([ 'success' => true, 'data' => $view, ]); }

The post PHP-level Performance Optimization with Blackfire appeared first on SitePoint.

Kategóriák: IT Hírek

Mi jön a 4. ipari forradalom után? (X)

ITHub - sze, 06/20/2018 - 14:54

A saját bőrünkön tapasztaljuk, ahogy egyre inkább behálózza az emberi életet és az azt célzó szolgáltatásokat a digitalizáció. Felmerülhet a kérdés ugyanakkor, hogy vajon milyen hatással bír mindez a színfalak mögött az iparra? És mely képességeit kell fejlesztenie az embernek ahhoz, hogy sikeres lehessen a jövőben?

Kategóriák: IT Hírek

STPL (New)

PHP Classes - sze, 06/20/2018 - 08:30
Package: STPL Summary: Render templates using PHP code scripts Groups: PHP 5, Templates Author: Aleksandar Zivanovic Description: This class can render templates using PHP code scripts...

Kategóriák: IT Hírek

Speed Image Load

PHP Classes - sze, 06/20/2018 - 08:18
Package: Speed Image Load Summary: Resize images in GIF, JPEG, PNG and WebP format Groups: Graphics, PHP 7 Author: riccardo castagna Description: This class can resize images in GIF, JPEG, PNG and WebP format...

Kategóriák: IT Hírek

Senkinek sem szabadna C-ben kódolnia a Stack Overflow alapítója szerint - sze, 06/20/2018 - 07:00
Meglehetősen érdekes véleményt fogalmazott meg az egyik, még mindig rendkívül népszerű nyelvvel kapcsolatban a világ egyik legismertebb fejlesztője a napokban. Joel Spolsky - akinek nevéhez a Joel on Software blog, valamint a Stack Overflow megalapítása is fűződik - kerek perec közölte: szerinte kerülnie kellene a C használatát minden programozónak.
Kategóriák: IT Hírek

Building an Image Gallery Blog with Symfony Flex: Data Testing

Sitepoint PHP - k, 06/19/2018 - 20:00

In the previous article, we demonstrated how to set up a Symfony project from scratch with Flex, and how to create a simple set of fixtures and get the project up and running.

The next step on our journey is to populate the database with a somewhat realistic amount of data to test application performance.

Note: if you did the “Getting started with the app” step in the previous post, you've already followed the steps outlined in this post. If that's the case, use this post as an explainer on how it was done.

As a bonus, we'll demonstrate how to set up a simple PHPUnit test suite with basic smoke tests.

More Fake Data

Once your entities are polished, and you've had your "That's it! I'm done!" moment, it's a perfect time to create a more significant dataset that can be used for further testing and preparing the app for production.

Simple fixtures like the ones we created in the previous article are great for the development phase, where loading ~30 entities is done quickly, and it can often be repeated while changing the DB schema.

Testing app performance, simulating real-world traffic and detecting bottlenecks requires bigger datasets (i.e. a larger amount of database entries and image files for this project). Generating thousands of entries takes some time (and computer resources), so we want to do it only once.

We could try increasing the COUNT constant in our fixture classes and seeing what will happen:

// src/DataFixtures/ORM/LoadUsersData.php class LoadUsersData extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface { const COUNT = 500; ... } // src/DataFixtures/ORM/LoadGalleriesData.php class LoadGalleriesData extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface { const COUNT = 1000; ... }

Now, if we run bin/, after some time we'll probably get a not-so-nice message like PHP Fatal error: Allowed memory size of N bytes exhausted.

Apart from slow execution, every error would result in an empty database because EntityManager is flushed only at the very end of the fixture class. Additionally, Faker is downloading a random image for every gallery entry. For 1,000 galleries with 5 to 10 images per gallery that would be 5,000 - 10,000 downloads, which is really slow.

There are excellent resources on optimizing Doctrine and Symfony for batch processing, and we're going to use some of these tips to optimize fixtures loading.

First, we'll define a batch size of 100 galleries. After every batch, we'll flush and clear the EntityManager (i.e., detach persisted entities) and tell the garbage collector to do its job.

To track progress, let's print out some meta information (batch identifier and memory usage).

Note: After calling $manager->clear(), all persisted entities are now unmanaged. The entity manager doesn't know about them anymore, and you'll probably get an "entity-not-persisted" error.

The key is to merge the entity back to the manager $entity = $manager->merge($entity);

Without the optimization, memory usage is increasing while running a LoadGalleriesData fixture class:

> loading [200] App\DataFixtures\ORM\LoadGalleriesData 100 Memory usage (currently) 24MB / (max) 24MB 200 Memory usage (currently) 26MB / (max) 26MB 300 Memory usage (currently) 28MB / (max) 28MB 400 Memory usage (currently) 30MB / (max) 30MB 500 Memory usage (currently) 32MB / (max) 32MB 600 Memory usage (currently) 34MB / (max) 34MB 700 Memory usage (currently) 36MB / (max) 36MB 800 Memory usage (currently) 38MB / (max) 38MB 900 Memory usage (currently) 40MB / (max) 40MB 1000 Memory usage (currently) 42MB / (max) 42MB

Memory usage starts at 24 MB and increases for 2 MB for every batch (100 galleries). If we tried to load 100,000 galleries, we'd need 24 MB + 999 (999 batches of 100 galleries, 99,900 galleries) * 2 MB = ~2 GB of memory.

After adding $manager->flush() and gc_collect_cycles() for every batch, removing SQL logging with $manager->getConnection()->getConfiguration()->setSQLLogger(null) and removing entity references by commenting out $this->addReference('gallery' . $i, $gallery);, memory usage becomes somewhat constant for every batch.

// Define batch size outside of the for loop $batchSize = 100; ... for ($i = 1; $i <= self::COUNT; $i++) { ... // Save the batch at the end of the for loop if (($i % $batchSize) == 0 || $i == self::COUNT) { $currentMemoryUsage = round(memory_get_usage(true) / 1024); $maxMemoryUsage = round(memory_get_peak_usage(true) / 1024); echo sprintf("%s Memory usage (currently) %dKB/ (max) %dKB \n", $i, $currentMemoryUsage, $maxMemoryUsage); $manager->flush(); $manager->clear(); // here you should merge entities you're re-using with the $manager // because they aren't managed anymore after calling $manager->clear(); // e.g. if you've already loaded category or tag entities // $category = $manager->merge($category); gc_collect_cycles(); } }

As expected, memory usage is now stable:

> loading [200] App\DataFixtures\ORM\LoadGalleriesData 100 Memory usage (currently) 24MB / (max) 24MB 200 Memory usage (currently) 26MB / (max) 28MB 300 Memory usage (currently) 26MB / (max) 28MB 400 Memory usage (currently) 26MB / (max) 28MB 500 Memory usage (currently) 26MB / (max) 28MB 600 Memory usage (currently) 26MB / (max) 28MB 700 Memory usage (currently) 26MB / (max) 28MB 800 Memory usage (currently) 26MB / (max) 28MB 900 Memory usage (currently) 26MB / (max) 28MB 1000 Memory usage (currently) 26MB / (max) 28MB

Instead of downloading random images every time, we can prepare 15 random images and update the fixture script to randomly choose one of them instead of using Faker's $faker->image() method.

Let's take 15 images from Unsplash and save them in var/demo-data/sample-images.

Then, update the LoadGalleriesData::generateRandomImage method:

private function generateRandomImage($imageName) { $images = [ 'image1.jpeg', 'image10.jpeg', 'image11.jpeg', 'image12.jpg', 'image13.jpeg', 'image14.jpeg', 'image15.jpeg', 'image2.jpeg', 'image3.jpeg', 'image4.jpeg', 'image5.jpeg', 'image6.jpeg', 'image7.jpeg', 'image8.jpeg', 'image9.jpeg', ]; $sourceDirectory = $this->container->getParameter('kernel.project_dir') . '/var/demo-data/sample-images/'; $targetDirectory = $this->container->getParameter('kernel.project_dir') . '/var/uploads/'; $randomImage = $images[rand(0, count($images) - 1)]; $randomImageSourceFilePath = $sourceDirectory . $randomImage; $randomImageExtension = explode('.', $randomImage)[1]; $targetImageFilename = sha1(microtime() . rand()) . '.' . $randomImageExtension; copy($randomImageSourceFilePath, $targetDirectory . $targetImageFilename); $image = new Image( Uuid::getFactory()->uuid4(), $randomImage, $targetImageFilename ); return $image; }

It's a good idea to remove old files in var/uploads when reloading fixtures, so I'm adding rm var/uploads/* command to bin/ script, immediately after dropping the DB schema.

Loading 500 users and 1000 galleries now takes ~7 minutes and ~28 MB of memory (peak usage).

Dropping database schema... Database schema dropped successfully! ATTENTION: This operation should not be executed in a production environment. Creating database schema... Database schema created successfully! > purging database > loading [100] App\DataFixtures\ORM\LoadUsersData 300 Memory usage (currently) 10MB / (max) 10MB 500 Memory usage (currently) 12MB / (max) 12MB > loading [200] App\DataFixtures\ORM\LoadGalleriesData 100 Memory usage (currently) 24MB / (max) 26MB 200 Memory usage (currently) 26MB / (max) 28MB 300 Memory usage (currently) 26MB / (max) 28MB 400 Memory usage (currently) 26MB / (max) 28MB 500 Memory usage (currently) 26MB / (max) 28MB 600 Memory usage (currently) 26MB / (max) 28MB 700 Memory usage (currently) 26MB / (max) 28MB 800 Memory usage (currently) 26MB / (max) 28MB 900 Memory usage (currently) 26MB / (max) 28MB 1000 Memory usage (currently) 26MB / (max) 28MB

Take a look at the fixture classes source: LoadUsersData.php and LoadGalleriesData.php.

The post Building an Image Gallery Blog with Symfony Flex: Data Testing appeared first on SitePoint.

Kategóriák: IT Hírek

Windows 10 RS5 Build 17692 - Fluent dizájn, szövegméret, játéksáv fejlesztések

OPREND - k, 06/19/2018 - 18:53
A múltheti Windows 10 Redstone 5 előzetes további újdonságokat hozott a rendszerbe, egy új beállítás segítségével a szövegméretek külön megnövelhetővé váltak, okosodott az érintőbillentyűzet és újra megváltozott a játéksáv is. De a kontextus menük megjelenése is megváltozott a build 17692-ben.
Kategóriák: IT Hírek

Játékfejlesztőket támogató bővítést adott ki a Unity-hez a GitHub - k, 06/19/2018 - 12:00
A GitHub a hét elején jelentette be, hogy elérhetővé tette egy, kifejezetten játékfejlesztőket segíteni hivatott kiterjesztésének első stabil változatát. A GitHub for Unity a jól ismert játékfejlesztő környezetbe épülve támogatja a tervezők és kóderek munkáját, többek között a nagy fájlok (LFS) és a zárolási pontok kezelését segítve a verziókezelőben.
Kategóriák: IT Hírek

YoutubeInfo (New)

PHP Classes - k, 06/19/2018 - 08:59
Package: YoutubeInfo Summary: Get information about a video in YouTube Groups: PHP 5, Video, Web services Author: adriano123456 Description: This package can get information about a video in YouTube...

Kategóriák: IT Hírek

Vége az AI-nak, mindenki menjen haza?! – RAGEPOST

Adatépítész - h, 06/18/2018 - 20:45
Adatépítész -az első magyar datapodcast

Minden ami hír, érdekesség, esemény vagy tudásmorzsa az  adat, datascience, adatbányászat és hasonló kockaságok világából.

 Iratkozz fel az új epizódokért!

Válj a közösség tagjává és támogasd az adást

Kategóriák: IT Hírek

Building an Image Gallery Blog with Symfony Flex: the Setup

Sitepoint PHP - h, 06/18/2018 - 20:00

This post begins our journey into Performance Month's zero-to-hero project. In this part, we'll set our project up so we can fine tune it throughout the next few posts, and bring it to a speedy perfection.

Now and then you have to create a new project repository, run that git init command locally and kick off a new awesome project. I have to admit I like the feeling of starting something new; it's like going on an adventure!

Lao Tzu said:

The journey of a thousand miles begins with one step

We can think about the project setup as the very first step of our thousand miles (users!) journey. We aren't sure where exactly we are going to end up, but it will be fun!

We also should keep in mind the advice from prof. Donald Knuth:

Premature optimization is the root of all evil (or at least most of it) in programming.

Our journey towards a stable, robust, high-performance web app will start with the simple but functional application --- the so-called minimum viable product (MVP). We'll populate the database with random content, do some benchmarks and improve performance incrementally. Every article in this series will be a checkpoint on our journey!

This article will cover the basics of setting up the project and organizing files for our Symfony Flex project. I'll also show you some tips, tricks and helper scripts I'm using for speeding up the development.

What Are We Building?

Before starting any project, you should have a clear vision of the final destination. Where are you headed? Who will be using your app and how? What are the main features you're building? Once you have that knowledge, you can prepare your environment, third-party libraries, and dive into developing the next big thing.

In this series of articles, we'll be building a simple image gallery blog where users can register or log in, upload images, and create simple public image galleries with descriptions written in Markdown format.

We'll be using the new Symfony Flex and Homestead (make sure you've read tutorials on them, as we're not going to cover them here). We picked Flex because Symfony 4 is just about to come out (if it hasn't already, by the time you're reading this), because it's infinitely lighter than the older version and lends itself perfectly to step-by-step optimization, and it's also the natural step in the evolution of the most popular enterprise PHP framework out there.

All the code referenced in this article is available at the GitHub repo.

We're going to use the Twig templating engine, Symfony forms, and Doctrine ORM with UUIDs as primary keys.

Entities and routes will use annotations; we'll have simple email/password based authentication, and we'll prepare data fixtures to populate the database.

Getting Started with the app

To try out the example we've prepared, do the following:

  • Set up an empty database called "blog".
  • Clone the project repository from GitHub.
  • Run composer install.
  • If you now open the app in your browser, you should see an exception regarding missing database tables. That's fine, since we haven't created any tables so far.
  • Update the .env file in your project root directory with valid database connection string (i.e., update credentials).
  • Run the database init script ./bin/ and wait until it generates some nice image galleries.
  • Open the app in your browser and enjoy!

After executing bin/ you should be able to see the home page of our site:

You can log in to the app with credentials and password 123456. See LoadUserData fixture class for more details regarding generated users.

Starting from scratch

In this section, we'll describe how to set up a new project from scratch. Feel free to take a look at the sample app codebase and see the details.

After creating a new project based on symfony/skeleton by executing the command

composer create-project "symfony/skeleton:^3.3" multi-user-gallery-blog

… we can first set minimum stability to "dev" because of some cutting edge packages:

composer config minimum-stability dev

… and then require additional packages (some of them are referenced by their aliases, the new feature brought by Flex):

composer req annotations security orm template asset validator ramsey/uuid-doctrine

Dependencies used only in the dev environment are required with the --dev flag:

composer req --dev fzaninotto/faker doctrine/Doctrine-Fixtures-Bundle

Flex is doing some serious work for us behind the scenes, and most of the libraries (or bundles) are already registered and configured with good-enough defaults! Check the config directory. You can check all the dependencies used in this project in the composer.json file.

Routes are defined by annotations, so the following will be automatically added into config/routes.yaml:

controllers: resource: ../src/Controller/ type: annotation Database, Scripts and Fixtures

Configure the DATABASE_URL environment variable (for example, by editing the .env file) to set up a working DB connection. If you're using our own Homestead Improved (recommended), you've got a database set up called homestead with the user / pass homestead / secret. A DB schema can be generated from existing entities by executing:

./bin/console doctrine:schema:create

If this doesn't run, try executing the console by invoking the PHP binary, like so:

php bin/console doctrine:schema:create

If this step executed fine in the "Getting Started with the app" section above, you should be able to see newly created tables in the database (for Gallery, Image and User entities).

If you want to drop the database schema, you can run:

./bin/console doctrine:schema:drop --full-database --force Fake it 'til you make it!

I can't imagine developing an app today without having data fixtures (i.e., scripts for seeding the DB). With a few simple scripts, you can populate your database with realistic content, which is useful when it comes to rapid app development and testing, but it's also a requirement for a healthy CI pipeline.

I find the Doctrine Fixtures Bundle to be an excellent tool for handling data fixtures as it supports ordered fixtures (i.e., you can control the order of execution), sharing objects (via references) between scripts, and accessing the service container.

Default Symfony services configuration doesn't allow public access to services, as best practice is to inject all dependencies. We'll need some services in our fixtures, so I'm going to make all services in App\Services publicly available by adding the following to config/services.yaml:

App\Service\: resource: '../src/Service/*' public: true

I'm also using Faker to get random but realistic data (names, sentences, texts, images, addresses, …).

Take a look at the script for seeding galleries with random images to get a feeling of how cool this combination is.

Usually, I combine commands for dropping the existing DB schema, creating the new DB schema, loading data fixtures, and other repetitive tasks into a single shell script --- for example, bin/ --- so I can easily regenerate the schema and load dummy data:

# Drop schema ./bin/console doctrine:schema:drop --full-database --force # Create schema ./bin/console doctrine:schema:create # Load fixtures ./bin/console doctrine:fixtures:load -n --fixtures src/DataFixtures/ORM # Install assets ./bin/console assets:install --symlink # Clear cache ./bin/console cache:clear

Make sure you restrict execution of this script on production, or you're going to have some serious fun at one point.

One can argue that randomly generated data can't reproduce different edge cases, so your CI can sometimes fail or pass depending on the data generation. It's true, and you should make sure all edge cases are covered with your fixtures.

Every time you find an edge case causing a bug, make sure you add it to data fixtures. This will help you build a more robust system and prevent similar errors in the future.

The post Building an Image Gallery Blog with Symfony Flex: the Setup appeared first on SitePoint.

Kategóriák: IT Hírek

Speed Image Load

PHP Classes - h, 06/18/2018 - 09:46
Package: Speed Image Load Summary: Resize images in GIF, JPEG, PNG and WebP format Groups: Graphics, PHP 7 Author: riccardo castagna Description: This class can resize images in GIF, JPEG, PNG and WebP format...

Kategóriák: IT Hírek

Speed Image Load

PHP Classes - h, 06/18/2018 - 08:45
Package: Speed Image Load Summary: Resize images in GIF, JPEG, PNG and WebP format Groups: Graphics, PHP 7 Author: riccardo castagna Description: This class can resize images in GIF, JPEG, PNG and WebP format...

Kategóriák: IT Hírek

Microsoft: JavaScript-ben írjuk újra az Office 365-öt - h, 06/18/2018 - 08:15
A Microsoft egyik vezető mérnöke a napokban egy - legalábbis egyesek számára - meglepő tweetet tett közzé csatornáján. Sean Thomas Larkin ugyanis közölte, hogy - több más szoftver mellett - a cég közismert irodai csomagját, az Office 365-öt is átírja JavaScript-re.
Kategóriák: IT Hírek