Is PHP alive?

Is PHP alive?

PHP is the language we hear least about in the media.

Angular, React, Node.js and Python are the trendiest ones right now. Even computer science degrees focus their efforts on the Java and C languages.

PHP sits in the corner and watches all other languages ​​get into the spotlight.

In this article, I want to find out if PHP is dead or is it still alive.

79% Internet

When someone mentions PHP, it is often frustrating for that programming language – however, according to W3Techs, it powers 79% of the Internet. Despite the bad reputation it got back in the 90s and early 2000s for its insecurity, it still manages to hold onto the title of the most used backend language.

However, much of this success can be attributed to the fact that it is used in WordPress. The widely popular CMS appeared in 2003, when the Internet and personal blogs began to be widely used. It has managed to surpass Google’s Blogger as a CMS.

WordPress has done its best to keep it incredibly simple.

According to a talk by Matt Mullenweg during his 2014 visit to Auckland, New Zealand, he mentioned that Squarespace’s Superbowl ads gave WordPress a free advertising boost as people started using WordPress as a comparison platform.

With built-in PHP in WordPress, supported by almost every shared hosting provider and still owning 61.5% of the Internet, PHP as a language doesn’t seem to be going anywhere anytime soon.

Even if WordPress decides to completely switch to another language, there will still be a large number of legacy sites that will need to switch to the new system.

Changing trend

PHP’s close relationship with WordPress contributed to the way developers began to view the programming language. Most of all it has to do with the development of themes and plugins for the WordPress ecosystem.

Job prospects outside of WordPress-related activities are often associated with legacy platforms originally built in PHP. It is not often a natural choice for startups or new business projects to deliver this language to potential candidates.

Experience with PHP recruiting has been on the decline, according to Darwin Recruitment, a UK-based recruiting agency that released data on its recruiting services.

However, there is a competitive trend towards more applications that require PHP. This backward effect does not indicate that PHP’s competitiveness for the job has diminished over time.

But this is one of the many agencies that may have a different trend. In contrast, worldwide interest in the search term “PHP” has shown a declining trend over the past 5 years, with China and the Philippines being the most popular search countries.

This coincides interestingly with what the number of searches for “WordPress” looks like, with corresponding dips and peaks.

How is the thematic market?

By being PHP related to WordPress, themes and emerging plugins are becoming mainstream commercial destinations, especially if you want to go freelance.
ThemeForest is currently the largest WordPress-related commerce marketplace, with 114 authors currently earning over $ 1 million – at first glance this might seem like a lot, but it really isn’t because there are 47k actives in the market. WordPress themes that are on sale. There is no easy way to determine how many products are per author, but it is unlikely that their cumulative result is more than 10%.

While this can be a good source of passive income, it takes a lot more work to make a business out of it. However, wherever we work, it will bring us dividends.

Hosting giant BlueHost acquired Mojo Themes and renamed it Mojo Marketplace, and they wanted to cash in on WordPress themes. However, they weren’t as successful as Envato ThemeForest, with most of their top themes being less than a thousand in size.

PHP is not completely dead, but it is not completely alive either – not like JavaScript, which is currently in the development ecosystem. PHP’s relationship with WordPress is close and is based on long-term adoption of the platform by regular users.

Since PHP is an integral part of the content creation ecosystem, it is unlikely to disappear in a year, two, or anytime soon. Hosting companies have a role to play as well as they continue to maintain WordPress as their main CMS, making it much more accessible to casual users than other server-side languages ​​like Java and C ++.

Despite all the chatter about PHP dying, it won’t happen as long as WordPress is up and running. Unfortunately, a lot of legacy PHP codes are associated with older versions of WordPress that site owners have not yet updated.

What is special about PHP is that it also has strong communities that are not related to WordPress, such as Laravel and Symfony. PHP itself is also actively supported, with the next release scheduled for November 2019. In general, PHP is doing fine so far.

PHP engine

PHP engine
Programming languages ​​come in two flavors: interpreted and compiled.
What language is PHP? In order to answer this question, we need to understand the terminology.
A program that translates code written in one programming language into another is called a translator. A compiler is also a translator. It translates high-level language code into machine code. The compilation process creates a binary executable file that can already be run without a compiler.
The interpreter is a completely different category. The interpreter does not translate the code, but executes it. The interpreter analyzes the program code and executes each line of it. Each time you execute such a code, you must use an interpreter.
In terms of performance, interpreters are significantly inferior to compilers, since binary code is much faster. On the other hand, interpreters allow you to fully control the program during its execution.
As far as PHP is concerned, it is neither a compiler nor an interpreter. PHP is a cross between a compiler and an interpreter. Let”s try to figure it out and see how PHP processes the code.

Consider the figure:

Zend API PHP engine technology
We see that PHP is composed of two almost independent blocks – a translator and an interpreter. Why did you need to do this? Of course, for performance reasons.
A script is fed into PHP. It translates (translates) it, checking the syntax, into a special bytecode (internal representation). PHP then executes the bytecode (not the code of the program itself) without creating an executable file.
Bytecode is much more compact than ordinary program code, so it is easier (and faster) to interpret (execute). Judge for yourself: parsing is carried out only once at the translation stage, and the “semi-finished product” is already executed – byte-code, which is much more convenient for these purposes. Therefore, PHP is more of an interpreter than a compiler. This “double work” was necessary for the following purposes.

Consider a loop:

For (i = 0; i <10; i ++) {
This cycle will “spin” 10 times. In each of these ten passes, the interpreter must parse and execute 100 lines of code. As a result, he needs to analyze and execute 10 * 100 = 1000 lines of code! If we translate the entire cycle into bytecode once, then it will have to analyze 10 times less! This means that scripts will run 10 times faster!
It turns out that PHP is a translating interpreter.
The main phase of PHP work is the interpretation of the internal representation of the program and its execution. It is this phase that takes the most time in serious scenarios. However, the slowdown is not that significant.
It is worth remembering that PHP version 3 was a “pure” interpreter, “and since PHP 4, scripts have become much faster to execute, since PHP 4 (and PHP5) is an interpreter translator.
The Perl language, which is almost always called a compiler, works exactly the same way – it translates the text of the program into an internal representation, and then uses the resulting code at execution. So you can say PHP version 4 is as much a compiler as Perl is.
So, we are forced to conclude that PHP is an interpreter with a built-in translation block that optimizes the course of interpretation.

Using an interpreter (and hence PHP) has its undeniable advantages:

  • There is no need to worry about freeing the allocated memory, there is no need to close files when you finish working with them – the interpreter will do all the routine work, since the program is executed under its vigilant control;
  • You don”t need to think about the types of variables, and you also don”t need to declare a variable before using it for the first time;
  • Debugging programs and detecting errors are greatly simplified – the interpreter has complete control over this process;
In the context of web applications, the interpreter also has a very important advantage – there is no danger of the server “freezing” if the program does not work properly.
There are other advantages as well. In general, using an interpreter can give scripts the power that Web users expect from them.
The loss in PHP performance is noticeable in the case of large and complex loops, when processing a large number of lines, etc.
However, note that this is the only PHP drawback that will appear less and less as more powerful processors come out, so that in the end , generally come to naught.

Salesforce Gotchas and Undocumented ‘Features’

Salesforce Gotchas and Undocumented

– Objects (such as individual leads or contacts) have either a 15-character or an 18-character case-sensitive ID (e.g. 00Q6000000OaR87). You should not count on one format or the other being returned. The two formats can normally be used interchangeably (you can pass either version to an API call), but one notable exception to this rule is the Excel Connector, which integrates Salesforce and Excel. CORRECTION: This is wrong. The lesson here is that when your motivation for writing a blog is your horrible experience with your Salesforce integrators, you shouldn’t take what they say as gospel. David Schach pointed out that the 15-character version is simply case-sensitive. There is no metadata whatsoever. I apologize again for failing to fact-check this.

– Object IDs have a three-character prefix, which will be consistent for standard objects and will differ between instances for custom objects. This is not very well documented, and in fact, a Google search for “salesforce id prefix” returns no results (except this entry, after it gets indexed). If you don’t yet have any objects of a particular type, but need the ID, you can get it from the URL after clicking on the tab (or the ‘>’ to the right of the tabs if you don’t see the object you’re looking for). The URL will be something like ‘’, and the prefix is between the slashes (here, ‘00Q,’ for leads). Prefixes for common standard objects include:

  • 00Q (Leads)
  • 003 (Contacts)
  • 001 (Accounts)
  • 006 (Opportunities)
  • 701 (Campaigns)
  • 500 (Cases)

– Salesforce API logins (including logins from other third-party applications, like the Outlook tool) have the concept of a security token, which can be reset by going to Setup > My Personal Information > Reset My Security Token.

– Security tokens are appended to passwords when logging in via the API, so you might login with the username ‘joe’ and the password ‘mypasswordmytoken’.

– Security tokens change automatically when a password changes – watch out for this!

– There are no default administrator profiles whose passwords do not expire. This means that your API user’s password may expire, and your entire API integration will suddenly fail. I highly recommend making a new administrator profile called something like ‘Admins – Passwords Do Not Expire’ and adding your API and other back-end users to it. The original profile will not (currently) let you check the ‘Passwords do not expire’ checkbox.

– Avoid using the logout() call. As discussed before, all logins for a single user share a session id. Calling logout() will log out all concurrent connections for that Salesforce user via the API. Note also that this behavior is undocumented. Not to worry, the PHP Toolkit gracefully cleans up the SOAP connection for you.

– Deleted and merged data (e.g. deleted leads) can wreak havoc if your application relies on valid Salesforce IDs. Just because a lead exists today doesn’t mean Sales isn’t going to mass-delete those 10,000 ‘junk’ leads tomorrow.

– Salesforce objects are always referred to in the API in the singular form, so ‘Lead’ rather than ‘Leads.’

– Custom fields append __c to the field name, e.g. My_Custom_Field__c.

– There are a couple of Salesforce expressions that I’ll use here to be consistent, including ‘picklist’ to mean an HTML select element, ‘dependent picklist’ to mean a picklist whose appearance or values depend on another picklist, ‘auto number’ to mean an auto-increment field, and ‘upsert’ to mean a call that will update if a record with the specified ID exists, or else insert a new record.

Lastly, a quick aside about languages and operating systems. The code examples presented here assume a POSIX-compliant OS, such as Linux, but Windows-specific considerations should pretty much non-existent. PHP is fairly OS-agnostic for what we’re going to be doing, but inevitably there will be an OS-specific issue that will creep in. As far as portable paths go, PHP lets you use ‘/’ as a path separator in scripts on all OSes, but you could certainly instead use the native (and portable) DIRECTORY_SEPARATOR constant or ‘\\’ in the paths if you prefer. Lastly, in this blog, ‘native’ is taken to mean ‘native to PHP, written in C’ – an example of this is the native SOAP client (versus the much slower NuSOAP client written in PHP).

Creating a Lead via the API

Creating a Lead via the Salesforce

Choosing the Right WSDL For You and Your Organization

Salesforce uses SOAP as its Web Services protocol, and SOAP uses an XML file called a WSDL (Web Services Definition Language) file that defines how a client (our code) can interact with a server (Salesforce). For our purposes, there are two different types of WSDL files, Enterprise and Partner. Here are the specifics:

Enterprise WSDL

  • Is strongly typed
  • Contains the metadata about all standard and custom fields and objects
  • Can only be used against your Salesforce instance

Partner WSDL

  • Is loosely typed
  • Takes an array of key-value pairs
  • Does not contain metadata about objects and fields
  • Can be used against many organizations

In our organization, we use the Enterprise WSDL, and the main reason why is that we like having a reference of all custom fields and objects readily available in the XML. Having the type of every field right there next to the field name at hand is nice as well. This being said, if we had a need to interact with many instances, we might choose to use the Partner WSDL instead.

For the purposes of this blog, we’re generally going to be writing against the Enterprise WSDL, but our first example will be presented at the end in an ‘Enterprise’ version and a ‘Partner’ version. Those of you who find you prefer the Partner WSDL should be able to take the Partner example and adapt it in the future very easily.

Generating the WSDL

Each instance of Salesforce (each sandbox and production instance) will have their own corresponding WSDL file. It can be generated by going to Setup > Develop > API > Generate Enterprise WSDL (or Generate Partner WSDL) and downloading the generated XML. I prefer to keep the default name and put each file in a dev/, staging/, and production/ folder, but you could just as easily call the files dev.enterprise.wsdl.xml, or something to that effect.

Taking a Look at the Lead Generation Code

Now that we’re set up with our WSDL, let’s take a look at the code at the bottom of the previous post. I have removed the logout() call, after I read (and subsequently validated) this absolutely terrific article that pointed out that a user’s API connections all get the same session ID! This means that logout() actually invalidates all of the concurrent connections for that user. This is somewhat of an edge case for us, as page load times are generally very short, but all the same, we won’t be using the logout() call going forward. If you’re wondering, logging out through the Salesforce API does not log the same user out of any API connections. At any rate, here is the code, slightly modified, and this time with some comments:

Configuration passages:

define('SALESFORCE_USER', '');
define('SALESFORCE_PASS', '*password here*');
define('SALESFORCE_TOKEN', '*security token here*');
define('SALESFORCE_WSDL', '/path/to/enterprise.wsdl.xml');



 * explicitly turn off WSDL caching
 * there is a bug in PHP with this setting in php.ini
 * so it's safest to set it in the PHP source
 * This param should take an int, not a string like most examples use
 * see
ini_set('soap.wsdl_cache_enabled', 0);

// instantiate a new Salesforce Enterprise object
$crmHandle = new SforceEnterpriseClient();

// instantiate a SOAP connection to Salesforce
try {
} catch (Exception $e) {
  // handle exception - did you set the WSDL path above?
  // we may also be in a Salesforce outage right now

// log in to Salesforce
try {
} catch (Exception $e) {
  // handle exception - did you modify the credentials above to your own?

// create our lead
$lead = array();
$lead['FirstName'] = 'Joe';
$lead['LastName'] = 'Moke';
$lead['Email'] = '';
$lead['Title'] = 'CEO';

// create the lead
// $lead must be wrapped in an array, as create() can create
// many objects with a single API call
$result = $crmHandle->create(array($lead), 'Lead');

But Wait, Where’s Our Lead?

In this case, there was no exception thrown, but a var_dump() of the $result object will reveal what went wrong:

  object(stdClass)#8 (3) {
    string(7) "Company"
    string(38) "Required fields are missing: [Company]"

Salesforce will usually throw an exception when things go haywire, but not always, as in our example. When there is an error, and no exception is thrown, $result->success will equal false, so always be prepared for both possibilities, and you can re-throw or create an exception as it applies to your situation.

The REQUIRED_FIELD_MISSING error is actually fairly unusual to see in the API. In nearly all cases, creating and updating via the API will exempt you from populating required fields, except where it would render the resulting object useless (omitting LastName from a lead, for instance). So, let’s set the company, and take another look at var_dump($result):

$lead['Company'] = 'Bob\'s Country Bunker';

object(stdClass)#7 (2) {
  string(18) "00QS00000034zU4MAI"

How to Use the Entire Salesforce API

How to Use the Entire Salesforce API

These posts are modeled after the EXAMPLES file that ships with the Salesforce Python Toolkit, in which I wrote snippets of code on how to use all the method calls in the Salesforce API spec. The posts will be modeled after the four sections of the Salesforce API docs; so they will be ‘Core Calls’, ‘Describe Calls’, ‘Utility Calls’, and ‘SOAP Headers’. There will be an Enterprise and Partner WSDL version of each.

NOTE: These examples are not intended to teach the Salesforce API; rather, they are here to illustrate how the PHP Toolkit implements the Salesforce API.


All examples below assume the Toolkit has been instantiated and the connection has been made.

$crmHandle = new SforcePartnerClient();

Additionally, all examples except login() assume you are logged in, using a call such as
$crmHandle->login(‘′, ‘*passwordhere*’ . ‘*securitytokenhere*’);


<create lead> – implies that you copy and paste the code from create(), so you have a Lead in the variable $lead and a SaveResult in the variable $result

<create 2 leads> – same as create lead, except copy and paste the code from ‘create 2 leads’ in create()

Variable Names

  • $crmHandle – reference to an instance of the Toolkit object
  • $result – the result of a Salesforce method call
  • $lead – a lead object


Date formats are in the SOAP dateTime format (ISO 8601), e.g. ‘2009-06-01T23:01:01Z’.

If you see an error like ‘INVALID_TYPE: Must send a concrete entity type’, most likely, you have forgotten to wrap your object in an array before passing it to a method.


<create lead>
$leadConvert = new SObject();
$leadConvert->leadId = $result->id;
// The possible values for convertedStatus depend on what's in the
// picklist for your org.  You can take a look at the 'Convert Lead' screen
// in the UI for your API user to see what's there
$leadConvert->convertedStatus = 'Qualified';
$leadConvert->doNotCreateOpportunity = true;
$leadConvert->overwriteLeadSource = false;
$leadConvert->sendNotificationEmail = false;
$result = $crmHandle->convertLead($leadConvert);


$lead = new SObject();
$lead->fields = array('FirstName' => 'Joe',
                      'LastName' => 'Moke',
                      'Company' => 'Jamoke, Inc.',
                      'Email' => '');
$lead->type = 'Lead';
$result = $crmHandle->create(array($lead), 'Lead');


<create lead>
$result = $crmHandle->delete(array($result->id));


$result = $crmHandle->emptyRecycleBin(array('*ID OF A DELETED OBJECT HERE*'));


// $startDate must be the later of 30 days prior and the last recycle bin purge
$result = $crmHandle->getDeleted('Lead', $startDate, $endDate);


// $startDate must be later than 30 days prior
$result = $crmHandle->getUpdated('Lead', $startDate, $endDate);

BEWARE there is a bug in versions 13.0 and earlier of the PHP Toolkit where it calls both invalidateSessions() AND logout(), generating an exception. I would strongly discourage use of either call, as they log out all concurrent sessions of the user!

You were warned…

$result = $crmHandle->invalidateSessions($crmHandle->getSessionId());


$crmHandle->login('', '*password*' . '*securitytoken*');

SEE NOTES FOR invalidateSessions()

$result = $crmHandle->logout();


// assume we have two IDs stored in $id and $id2

// retrieve what will be the master record
$result = $crmHandle->retrieve('FirstName, LastName, Company, Email', 'Lead', $id);
$lead = $result[0];

$mergeRequest = new SObject();
$mergeRequest->masterRecord = $lead;
$mergeRequest->recordToMergeIds = $id2;
$result = $crmHandle->merge($mergeRequest, 'Lead');

The Toolkit diverges slightly from the API here, as it doesn’t implement process(), but instead implements processSubmitRequest() and processWorkitemRequest().

processSubmitRequest() (non-standard)
NOTE: The API docs for this call are currently incorrect; the ProcessSubmitRequest object takes a property ‘comments’, not ‘comment’ as stated here.

$processRequest = new SObject();
$processRequest->objectId = '*ID OF OBJECT PROCESS REQUEST AFFECTS*';
$processRequest->comments = 'This is what I think.';
$result = $crmHandle->processSubmitRequest(array($processRequest));

processWorkitemRequest() (non-standard)
NOTE: The API docs for this call are currently incorrect; the ProcessWorkitemRequest object takes a property ‘comments’, not ‘comment’ as stated here.

$processRequest = new SObject();
$processRequest->action = 'Approve';
$processRequest->workitemId = '*ID OF OBJECT PROCESS REQUEST AFFECTS*';
$processRequest->comments = 'I approved this request.';
$result = $crmHandle->processWorkitemRequest(array($processRequest));


$result = $crmHandle->query('SELECT FirstName, LastName FROM Lead');

This method is broken in the Partner version as of version 13.1; however, there is a simple fix. Add the following method to SforcePartnerClient.php at line 158:

  public function queryAll($query) {
    return new QueryResult(parent::queryAll($query));

Otherwise, for the following query:

$result = $crmHandle->queryAll('SELECT FirstName, LastName FROM Lead LIMIT 2');

the Toolkit returns

    object(stdClass)#6 (3) {
      string(4) "Lead"
      string(73) "<sf:FirstName>Joe</sf:FirstName><sf:LastName>Moke</sf:LastName>"

instead of

    object(SObject)#5 (2) {
      string(4) "Lead"
      object(stdClass)#9 (2) {
        string(4) "Joe"
        string(5) "Moke"

like the Enterprise version does. The working example is the query above.


// return a maximum of 200 results
$queryOptions = new QueryOptions(200);
$result = $crmHandle->query('SELECT FirstName, LastName FROM Lead');
while ($result->done === false) {
  $result = $crmHandle->queryMore($result->queryLocator);
  // do something with results

NOTE: The Partner version will return a 1-length array with the object in it for a single match, the Enterprise version will return the object itself.

$result = $crmHandle->retrieve('FirstName, LastName, Company, Email', 'Lead', $id);
$lead = $result[0];

Search is currently broken in the Partner Toolkit as of version 13.1, and in order to use it, you must make a couple of modifications to SforcePartnerClient.php. Note that this is not the official implementation, but it does comply with what’s in the partner WSDL.

In the class SforcePartnerClient, after retrieve(), add the following method:

  public function search($searchString) {
    return new SearchResult(parent::search($searchString));

Below the class SforcePartnerClient, add the following class:

class SearchResult {
  public $searchRecords = array();

   * SearchResult ctor
   * SforceBaseClient::search() will return three different structures depending on result count,
   * distinguishable in the following way:
   * 0 results: empty stdClass object
   * 1 result: $response->searchRecords is an object, not an array
   * 2+ results: $response->searchRecords is an array
   * @param stdClass $response  Response from base class
  public function __construct($response) {
    if (!isset($response->searchRecords)) {
    } elseif (!is_array($response->searchRecords)) {
      $this->searchRecords[]->record = new SObject($response->searchRecords->record);
    } else {
      foreach ($response->searchRecords as $searchRecord) {
        $this->searchRecords[]->record = new SObject($searchRecord->record);

You can now use the search() method in the same way as in the Enterprise client:

$result = $crmHandle->search('FIND {Joe Moke} IN Name Fields RETURNING Lead(Name, Phone)');


$result = $crmHandle->undelete(array('*ID HERE*'));

NOTE: Unlike the Enterprise version, you can set more than one field to null in an update() call. However, you must unset() each field as well, not set the field equal to null or ”.

<create lead>

$lead->Id = $result->id;
$lead->fieldsToNull = array('Email', 'Company');
$crmHandle->update(array($lead), 'Lead');


<create lead>

$lead->Id = $result->id;
$lead->FirstName = 'Bob';
$crmHandle->upsert('Id', array($lead), 'Lead');